packages feed

licensor 0.1.0 → 0.2.0

raw patch · 5 files changed

+498/−128 lines, 5 filesdep +bytestringdep +cmdargsdep +http-conduitdep −HTTPdep ~base

Dependencies added: bytestring, cmdargs, http-conduit, licensor

Dependencies removed: HTTP

Dependency ranges changed: base

Files

+ CHANGELOG.md view
@@ -0,0 +1,11 @@+# 0.2.0 (2016-09-18)++## Enhancements++- Allow use in multi-package project (f545a4c)+- Handle exceptions (ec640bb)+- Replace HTTP with http-conduit (f6735cb)+- Add command line arguments (2558a05)+- Add a library (835ac70)++# 0.1.0 (2016-08-13)
Main.hs view
@@ -1,63 +1,69 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE RecordWildCards #-} +----------------------------------------------------------------------+-- |+-- Module: Main+-- Description:+--+--+--+----------------------------------------------------------------------+ module Main   ( main   )   where +-- licensor+import Licensor+ -- base import Control.Monad import Data.List import Data.Monoid ((<>))+import qualified Data.Version as Version+import System.Environment import qualified System.Exit as Exit-import System.IO  -- Cabal-import Distribution.License-import Distribution.Package import Distribution.PackageDescription-import Distribution.PackageDescription.Parse-import Distribution.Simple.Utils import Distribution.Text-import Distribution.Verbosity +-- cmdargs+import System.Console.CmdArgs+ -- containers-import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map-import Data.Set (Set) import qualified Data.Set as Set  -- directory-import System.Directory---- HTTP-import Network.HTTP-  ( getRequest-  , getResponseBody-  , simpleHTTP-  )---- process-import System.Process+import System.Directory (doesFileExist)   -- | -- -- -newtype License' = License' { _getLicense :: License }-  deriving (Eq, Read, Show, Text)+data LiArgs =+  LiArgs+    {+    }+  deriving (Data)   -- | -- -- -instance Ord License' where-  compare =-    comparing display+liArgs :: String -> Mode (CmdArgs LiArgs)+liArgs s =+  cmdArgsMode $+    LiArgs+      {+      }+  &= program s+  &= summary (unwords ["licensor", Version.showVersion version])   -- |@@ -66,12 +72,20 @@  main :: IO () main = do+  LiArgs <- cmdArgsRun . liArgs =<< getProgName+   maybePackage <- getPackage    pid <-     case maybePackage of-      Nothing ->-        Exit.die "Error: No Cabal file found."+      Nothing -> do+        stack <- doesFileExist "stack.yaml"+        if stack+          then do+            putStrLn "Found stack.yaml..."+            return Nothing+          else+            Exit.die "Error: No Cabal file found."        Just PackageDescription{..} -> do         putStrLn $@@ -81,7 +95,7 @@             <> "License: "             <> display license             <> ")"-        return package+        return (Just package)    maybeDependencies <- getDependencies @@ -90,9 +104,11 @@       Exit.die "Error: ..."      Just dependencies -> do-      dependenciesByLicense <--        fmap (Set.map display) <$> orderPackagesByLicense pid dependencies+      (dependenciesByLicense', failed) <-+        orderPackagesByLicense pid dependencies +      let dependenciesByLicense = fmap (Set.map display) dependenciesByLicense'+       forM_ (Map.keys dependenciesByLicense) $         \license ->           let@@ -107,82 +123,6 @@                 <> ": "                 <> intercalate ", " (Set.toList n) ---- |--------getPackage :: IO (Maybe PackageDescription)-getPackage = do-  currentDirectory <- getCurrentDirectory-  fmap getPackageDescription <$> findPackageDesc currentDirectory-    >>= either (const (return Nothing)) (fmap Just)----- |--------getPackageDescription :: FilePath -> IO PackageDescription-getPackageDescription =-  fmap packageDescription . readPackageDescription silent----- |--------getDependencies :: IO (Maybe (Set PackageIdentifier))-getDependencies =-  fmap Set.fromList . sequence . fmap simpleParse . lines-    <$> readProcess "stack" ["list-dependencies", "--separator", "-"] ""----- |--------getPackageLicense :: PackageIdentifier -> IO License'-getPackageLicense p@PackageIdentifier{..} = do-  let-    url =-      "http://hackage.haskell.org/package/"-        <> display p-        <> "/"-        <> unPackageName pkgName-        <> ".cabal"-  pd <- simpleHTTP (getRequest url) >>= getResponseBody-  (file, handle) <- openTempFile "/tmp" "licensor"-  hClose handle-  writeFile file pd-  PackageDescription{license} <- getPackageDescription file-  hClose handle-  removeFile file-  return (License' license)----- |--------orderPackagesByLicense-  :: PackageIdentifier-  -> Set PackageIdentifier-  -> IO (Map License' (Set PackageIdentifier))-orderPackagesByLicense p =-  let-    insertPackage package orderedPackages' = do-      license <- getPackageLicense package-      orderedPackages <- orderedPackages'-      return $-        if p == package-          then-            orderedPackages-          else-            Map.insertWith-              Set.union-              license-              (Set.singleton package)-              orderedPackages-  in-    foldr insertPackage (pure mempty)+      unless (null failed) $ do+        putStr "Failed: "+        print failed
README.md view
@@ -1,1 +1,176 @@-# licensor+# The not so great automatic Haskell licensor++[![][1]][0]++[0]: https://circleci.com/gh/jpvillaisaza/licensor+[1]: https://circleci.com/gh/jpvillaisaza/licensor.svg?style=svg++![][2]+![][3]++[2]: https://www.stackage.org/package/licensor/badge/lts+[3]: https://www.stackage.org/package/licensor/badge/nightly++Licensor is a program that generates a report of the dependencies and+transitive dependencies of a Haskell project and their licenses.++## Description++Choosing a license for a software project or determining whether a+particular dependency can be added to a project can be projects+themselves. Unless starting from scratch, programmers should consider+the licenses of the dependencies and transitive dependencies of their+projects to make informed decisions and avoid license compatibility+issues.++Of course, this is just a starting point. "Beyond (...) general+observations, it is difficult, if not impossible, to provide precise+guidance about what licenses may or may not be compatible with each+other. (...) Programmers who are considering combining code governed+by two or more different licenses should proceed cautiously" (Andrew+M. St. Laurent).++## Disclaimer++Licensor is not a lawyer and does not provide legal advice.++For more information about licenses and license compatibility issues,+read the text of the licenses or consult with a lawyer before making+any decision.++## Related programs++Licensor is not the only license compatibility helper for Haskell:++- Licensor uses a Cabal library and Stack program approach for+  detecting licenses and listing dependencies, respectively. For a+  Cabal library and program approach, consider using+  the [cabal-dependency-licenses][rp-01] program.++[rp-01]: https://hackage.haskell.org/package/cabal-dependency-licenses++## Installation and usage++To install Licensor, use Cabal:++```+$ cabal update && cabal install licensor+```++Then, run the `licensor` executable inside a Haskell project:++```+$ licensor+```++To see the license report for Licensor, clone the repository:++```+$ git clone https://github.com/jpvillaisaza/licensor+```++And run `licensor` inside the project:++```+$ cd licensor/ && licensor+```++Or build and run `licensor` inside the project:++```+$ cd licensor/ && stack build --exec licensor+```++For more information, run `licensor --help`:++```+licensor 0.2.0++licensor [OPTIONS]++Common flags:+  -? --help             Display help message+  -V --version          Print version information+     --numeric-version  Print just the version number+```++## Notes++### Dependencies++Licensor uses the Stack program to list dependencies for a Haskell+project. A future enhancement could be to use the Stack library.++### Licenses and license detection++Licensor uses the Cabal library to detect the license of a Haskell+project and its dependencies (including transitive dependencies). To+do so, it uses the license field in the package description. A future+enhancement could be to use both the license and licence file fields+in the package description.++Cabal provides an enumeration of common open source and free software+licenses. These are the licenses that appear in the reports generated+by Licensor:++License                   | Description+------------------------- | -------------------------+GPL                       | GNU General Public License+AGPL                      | GNU Affero General Public License+LGPL                      | GNU Lesser General Public License+BSD2                      | BSD 2-Clause License+BSD3                      | BSD 3-Clause License+BSD4                      | BSD 4-Clause License+MIT                       | MIT License+ISC                       | ISC License+MPL                       | Mozilla Public License+Apache                    | Apache License+PublicDomain              | Public domain+AllRightsReserved         | All rights reserved+UnspecifiedLicense        | Unspecified license (All rights reserved)+OtherLicense              | Other license+UnknownLicense            | Unknown license++## Contribution guidelines++Feel free to create issues for reporting bugs and suggesting+enhancements, or to fork the repository and open a pull request.++## License++Licensor is licensed under the MIT License.+See [LICENSE.md](LICENSE.md).++### License report++Licensor (0.2.0) depends on the following libraries:++Library                   | License+------------------------- | -------------------------+base                      | BSD3+bytestring                | BSD3+Cabal                     | BSD3+cmdargs                   | BSD3+containers                | BSD3+directory                 | BSD3+http-conduit              | BSD3+process                   | BSD3++And the following licenses (including transitive dependencies):++License                   | Number of libraries+------------------------- | -------------------------+BSD3                      | 68+MIT                       | 7++(Tested with Licensor 0.2.0, Stack 1.2.0, and LTS 7.0.)++## Additional resources++- [Choose a License](http://choosealicense.com/)+- [License compatibility][ar-01]+- [Understanding open source and free software licensing][ar-02]+  (Andrew M. St. Laurent)++[ar-01]: https://en.wikipedia.org/wiki/License_compatibility+[ar-02]: http://www.oreilly.com/openbook/osfreesoft/book/
licensor.cabal view
@@ -1,33 +1,67 @@-name:                licensor-version:             0.1.0-synopsis:            A license compatibility helper-description:         A license compatibility helper.-homepage:            https://github.com/jpvillaisaza/licensor-bug-reports:         https://github.com/jpvillaisaza/licensor/issues-license:             MIT-license-file:        LICENSE.md-author:              Juan Pedro Villa Isaza <jpvillaisaza@gmail.com>-maintainer:          Juan Pedro Villa Isaza <jpvillaisaza@gmail.com>-copyright:           2016 Juan Pedro Villa Isaza-category:            Distribution-extra-source-files:  README.md-build-type:          Simple-cabal-version:       >= 1.10+name: licensor+version: 0.2.0 +build-type: Simple+cabal-version: >= 1.21++license: MIT+license-file: LICENSE.md++copyright: 2016 Juan Pedro Villa Isaza+author: Juan Pedro Villa Isaza <jpvillaisaza@gmail.com>+maintainer: Juan Pedro Villa Isaza <jpvillaisaza@gmail.com>++stability: Experimental++homepage: https://github.com/jpvillaisaza/licensor+bug-reports: https://github.com/jpvillaisaza/licensor/issues++synopsis: A license compatibility helper+description: A license compatibility helper.++category: Distribution++tested-with: GHC == 7.10.3, GHC == 8.0.1++extra-source-files: CHANGELOG.md, README.md+++library+  hs-source-dirs:+      src+  exposed-modules:+      Licensor+  other-modules:+      Paths_licensor+  build-depends:+      base >= 4.8 && < 4.10+    , bytestring+    , Cabal >= 1.22 && < 1.25+    , containers+    , directory+    , http-conduit >= 2.1 && < 2.3+    , process+  default-language:+      Haskell2010+  ghc-options:+      -Wall++ executable licensor   main-is:       Main.hs   build-depends:       base >= 4.8 && < 5.0     , Cabal >= 1.22 && < 1.25+    , cmdargs >= 0.10 && < 0.11     , containers     , directory-    , HTTP >= 4000.3 && < 4000.4-    , process+    , licensor   default-language:       Haskell2010   ghc-options:       -Wall -threaded -rtsopts -with-rtsopts=-N+  source-repository head   type: git
+ src/Licensor.hs view
@@ -0,0 +1,210 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++----------------------------------------------------------------------+-- |+-- Module: Licensor+-- Description:+--+--+--+----------------------------------------------------------------------++module Licensor+  ( LiLicense(..)+  , LiPackage(..)+  , getDependencies+  , getPackage+  , orderPackagesByLicense+  , version+  )+  where++-- base+import qualified Control.Exception as Exception+import Data.Monoid ((<>))+import Data.Version (Version)+import System.IO++-- bytestring+import qualified Data.ByteString.Lazy as ByteString++-- Cabal+import Distribution.License+import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Parse+import Distribution.Simple.Utils+import Distribution.Text+import Distribution.Verbosity++-- containers+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as Map+import Data.Set (Set)+import qualified Data.Set as Set++-- directory+import System.Directory++-- http-conduit+--import Network.HTTP.Client.Conduit+import Network.HTTP.Simple++-- licensor+import qualified Paths_licensor++-- process+import System.Process+++-- |+--+--++newtype LiLicense = LiLicense { getLicense :: License }+  deriving (Eq, Read, Show, Text)+++-- |+--+--++instance Ord LiLicense where+  compare =+    comparing display+++-- |+--+--++data LiPackage =+  LiPackage+    { liPackageId :: PackageIdentifier+    , liPackageDependencies :: Set LiPackage+    , liPackageLicense :: License+    }+++-- |+--+--++getPackage :: IO (Maybe PackageDescription)+getPackage = do+  currentDirectory <- getCurrentDirectory+  fmap getPackageDescription <$> findPackageDesc currentDirectory+    >>= either (const (return Nothing)) (fmap Just)+++-- |+--+--++getPackageDescription :: FilePath -> IO PackageDescription+getPackageDescription =+  fmap packageDescription . readPackageDescription silent+++-- |+--+--++getDependencies :: IO (Maybe (Set PackageIdentifier))+getDependencies = do+  eitherDeps <-+    Exception.try $ readProcess "stack" ["list-dependencies", "--separator", "-"] ""++  case eitherDeps of+    Left (_ :: IOError) ->+      return Nothing++    Right deps ->+      return $ fmap Set.fromList $ sequence $ fmap simpleParse (lines deps)+++-- |+--+--++getPackageLicense :: PackageIdentifier -> IO (Maybe LiLicense)+getPackageLicense p@PackageIdentifier{..} = do+  putStr $ display p ++ "..."+  let+    url =+      "GET https://hackage.haskell.org/package/"+        <> display p+        <> "/"+        <> unPackageName pkgName+        <> ".cabal"++  req <- parseRequest url+  eitherPd <- Exception.try $ fmap getResponseBody (httpLBS req)++  case eitherPd of+    Left (_ :: HttpException) ->+      return Nothing++    Right pd -> do++      (file, handle) <- openTempFile "/tmp" "licensor"+      hClose handle+      ByteString.writeFile file pd+      PackageDescription{license} <- getPackageDescription file+      hClose handle+      removeFile file++      putStrLn $ display license++      return $ Just (LiLicense license)+++-- |+--+--++orderPackagesByLicense+  :: Maybe PackageIdentifier+  -> Set PackageIdentifier+  -> IO (Map LiLicense (Set PackageIdentifier), Set PackageIdentifier)+orderPackagesByLicense maybeP =+  let+    cond =+      maybe (const False) (==) maybeP++    insertPackage package orderedPackages' = do+      maybeLicense <- getPackageLicense package++      (orderedPackages, failed) <- orderedPackages'+      return $+        if cond package+          then+            (orderedPackages, failed)+          else+            case maybeLicense of+              Nothing ->+                ( orderedPackages, Set.insert package failed+                )++              Just license ->+                ( Map.insertWith+                    Set.union+                    license+                    (Set.singleton package)+                    orderedPackages+                , failed+                )+  in+    foldr insertPackage (pure (mempty, mempty))+++-- |+--+--++version :: Version+version =+  Paths_licensor.version