thank-you-stars 0.1.0 → 0.2.0
raw patch · 5 files changed
+87/−68 lines, 5 files
Files
- README.md +4/−2
- app/Main.hs +6/−11
- src/Utils/ThankYouStars/Package.hs +46/−11
- test/Utils/ThankYouStars/PackageSpec.hs +27/−41
- thank-you-stars.cabal +4/−3
README.md view
@@ -2,9 +2,10 @@ =============== [](https://travis-ci.org/y-taka-23/thank-you-stars)+[](https://hackage.haskell.org/package/thank-you-stars) A tool for starring GitHub repositories. It detects dependent libraries-which are hosted on GitHub via `package.cabal` file,+which are hosted on GitHub via `.cabal` file, and stars the repositories all at once. Setup@@ -34,7 +35,8 @@ ----- Run `thank-you-stars` in the root directory of your project.-Then it scans the `package.cabal` and the local Hackage DB,+Then it scans all `.cabal` files under the current directory+and metadata of the packages from the local Stackage DB, stars your dependent libraries if they are hosted on GitHub. ```console
app/Main.hs view
@@ -3,29 +3,24 @@ import Utils.ThankYouStars.GitHub import Utils.ThankYouStars.Package -import Control.Exception ( SomeException, catch )-import Data.Maybe ( catMaybes ) import qualified Data.Set as S import System.Exit ( die ) import System.Directory ( getHomeDirectory )-import System.FilePath ( joinPath, (<.>) )+import System.FilePath ( joinPath ) main :: IO () main = do homeDir <- getHomeDirectory- thisPkg <- getThisPackageName let tokenFile = joinPath [homeDir, ".thank-you-stars.json"]- cabalFile = unPackageName thisPkg <.> "cabal" eToken <- readToken tokenFile case eToken of Left _ -> do die ("Cannot parse " ++ show tokenFile ++ " into a token")- Right token -> do- desc <- readCabalFile cabalFile- db <- readHackage- let pkgs = S.delete thisPkg $ allBuildDepends desc- mRepos = S.map (flip lookupRepo $ db) pkgs- repos = catMaybes $ S.toList mRepos+ Right token -> do+ db <- readStackIndex+ cabals <- getCabalFiles+ descs <- mapM readCabalFile $ S.toList cabals+ let repos = S.unions $ map (dependentRepos db) descs mapM_ (starAction token) repos starAction :: Token -> GitHubRepo -> IO ()
src/Utils/ThankYouStars/Package.hs view
@@ -1,26 +1,32 @@ module Utils.ThankYouStars.Package (- getThisPackageName- , allBuildDepends- , readHackage- , lookupRepo+ dependentRepos+ , getCabalFiles , readCabalFile- , unPackageName+ , readStackIndex ) where import Utils.ThankYouStars.GitHub -import Data.List ( isInfixOf )+import Data.List ( isInfixOf, isPrefixOf ) import Data.List.Split ( splitOneOf ) import qualified Data.Map as M import qualified Data.Set as S import Data.Maybe-import Distribution.Hackage.DB ( Hackage, readHackage )+import Distribution.Hackage.DB ( Hackage, readHackage' ) import Distribution.Package import Distribution.PackageDescription import Distribution.PackageDescription.Parse ( readPackageDescription ) import Distribution.Verbosity ( normal )-import System.Directory ( getCurrentDirectory )-import System.FilePath ( takeBaseName )+import System.Directory ( getCurrentDirectory+ , getAppUserDataDirectory+ , getPermissions+ , searchable+ , listDirectory+ )+import System.FilePath ( joinPath+ , combine+ , takeExtension+ ) allBuildDepends :: GenericPackageDescription -> S.Set PackageName allBuildDepends desc =@@ -33,12 +39,33 @@ depends :: BuildInfo -> [PackageName] depends = map toPackageName . targetBuildDepends -getThisPackageName :: IO PackageName-getThisPackageName = (PackageName . takeBaseName) <$> getCurrentDirectory+getCabalFiles :: IO (S.Set FilePath)+getCabalFiles = getCurrentDirectory >>= searchCabalFiles +searchCabalFiles :: FilePath -> IO (S.Set FilePath)+searchCabalFiles fp = do+ p <- getPermissions fp+ if searchable p+ then do+ children <- map (combine fp) . filter visible <$> listDirectory fp+ S.unions <$> mapM searchCabalFiles children+ else do+ if takeExtension fp == ".cabal"+ then return $ S.singleton fp+ else return $ S.empty++visible :: FilePath -> Bool+visible fp = not $ "." `isPrefixOf` fp+ readCabalFile :: FilePath -> IO GenericPackageDescription readCabalFile = readPackageDescription normal +dependentRepos :: Hackage -> GenericPackageDescription -> S.Set GitHubRepo+dependentRepos db desc = S.map fromJust $ S.filter isJust mRepos+ where+ pkgs = S.delete (packageName desc) (allBuildDepends desc)+ mRepos = S.map (flip lookupRepo $ db) pkgs+ toPackageName :: Dependency -> PackageName toPackageName (Dependency name _) = name @@ -62,3 +89,11 @@ where isGitHub = isInfixOf "github.com" loc ps = splitOneOf "/." loc++readStackIndex :: IO Hackage+readStackIndex = getStackIndexPath >>= readHackage'++getStackIndexPath :: IO FilePath+getStackIndexPath = do+ stackDir <- getAppUserDataDirectory "stack"+ return $ joinPath [ stackDir, "indices", "Hackage", "00-index.tar" ]
test/Utils/ThankYouStars/PackageSpec.hs view
@@ -1,52 +1,38 @@-{-# LANGUAGE OverloadedStrings #-} module Utils.ThankYouStars.PackageSpec (spec) where import Utils.ThankYouStars.GitHub import Utils.ThankYouStars.Package -import qualified Data.Set as S-import qualified Data.Map as M-import Distribution.Hackage.DB ( readHackage )-import Distribution.Package-import Distribution.Version+import qualified Data.Set as S+import System.Directory ( getCurrentDirectory )+import System.FilePath ( joinPath ) import Test.Hspec spec :: Spec spec = do- describe "getThisPackageName" $ do- it "returns the name of project root directory" $ do- getThisPackageName `shouldReturn`- PackageName { unPackageName = "thank-you-stars" }-- describe "allBuildDepends" $ do- it "returns all packages which are listed in build-depends" $ do- desc <- readCabalFile "thank-you-stars.cabal"- allBuildDepends desc `shouldBe` S.fromList (map PackageName [- "aeson"- , "base"- , "bytestring"- , "Cabal"- , "containers"- , "directory"- , "filepath"- , "hackage-db"- , "hspec"- , "req"- , "split"- , "text"- , "thank-you-stars"- ])+ describe "getCabalFiles" $ do+ it "returns the set of all .cabal files in the subdirectories" $ do+ curr <- getCurrentDirectory+ getCabalFiles `shouldReturn` S.fromList [+ joinPath [curr, "thank-you-stars.cabal"]+ , joinPath [curr, "test", "Fixture", "dummy.cabal"]+ ] - describe "lookupRepo" $ do- it "extracts source-repository from the package description" $ do+ -- TODO: too fragile. mock the stack DB+ describe "dependentRepos" $ do+ it "returns the set of all GitHub repos listed in build-depends" $ do+ db <- readStackIndex desc <- readCabalFile "thank-you-stars.cabal"- let ver = Version [] []- db = M.singleton "thank-you-stars" (M.singleton ver desc)- lookupRepo (PackageName { unPackageName = "thank-you-stars" }) db- `shouldBe` Just GitHubRepo {- owner = "y-taka-23"- , repo = "thank-you-stars"- }- it "returns Nothing if the package is not found" $ do- lookupRepo (PackageName { unPackageName = "hspec" }) M.empty- `shouldBe` Nothing+ dependentRepos db desc `shouldBe` S.fromList [+ GitHubRepo "bos" "aeson"+ , GitHubRepo "haskell" "bytestring"+ , GitHubRepo "haskell" "cabal"+ , GitHubRepo "haskell" "containers"+ , GitHubRepo "haskell" "directory"+ , GitHubRepo "haskell" "filepath"+ , GitHubRepo "peti" "hackage-db"+ , GitHubRepo "hspec" "hspec"+ , GitHubRepo "mrkkrp" "req"+ , GitHubRepo "byorgey" "split"+ , GitHubRepo "bos" "text"+ ]
thank-you-stars.cabal view
@@ -1,9 +1,9 @@ name: thank-you-stars-version: 0.1.0+version: 0.2.0 synopsis: Give your dependencies stars on GitHub! description: A tool for starring GitHub repositories. It detects dependent libraries- which are hosted on GitHub via package.cabal file,+ which are hosted on GitHub via .cabal files, and stars the repositories all at once. homepage: https://github.com/y-taka-23/thank-you-stars#readme license: BSD3@@ -54,7 +54,8 @@ build-depends: base , Cabal , containers- , hackage-db+ , directory+ , filepath , hspec , thank-you-stars ghc-options: -threaded -rtsopts -with-rtsopts=-N