1
1
mirror of https://github.com/nmattia/niv.git synced 2024-11-09 12:38:31 +03:00

Add attr discovery

This commit is contained in:
Nicolas Mattia 2019-01-26 23:39:38 +01:00
parent d2758102b6
commit 7628070e98
3 changed files with 158 additions and 31 deletions

173
Main.hs
View File

@ -9,11 +9,15 @@
-- TODO: format code -- TODO: format code
import Control.Monad import Control.Monad
import Control.Monad.State
import Data.Aeson import Data.Aeson
import Data.Bifunctor import Data.Bifunctor
import Data.Char (toUpper) import Data.Char (toUpper)
import Data.Hashable (Hashable) import Data.Hashable (Hashable)
import Data.Maybe (mapMaybe)
import Data.Semigroup ((<>)) import Data.Semigroup ((<>))
import Data.String
import GHC.Exts (toList)
import Options.Applicative import Options.Applicative
import System.Directory import System.Directory
import System.FilePath import System.FilePath
@ -22,6 +26,8 @@ import qualified Data.ByteString.Lazy as L
import qualified Data.HashMap.Strict as HMap import qualified Data.HashMap.Strict as HMap
import qualified Data.List.NonEmpty as NE import qualified Data.List.NonEmpty as NE
import qualified Data.Text as T import qualified Data.Text as T
import qualified GitHub as GH
import qualified GitHub.Data.Name as GH
fileFetchNix :: FilePath fileFetchNix :: FilePath
fileFetchNix = "nix" </> "fetch.nix" fileFetchNix = "nix" </> "fetch.nix"
@ -62,7 +68,6 @@ getVersionsSpec = do
Just _ -> error "foo" Just _ -> error "foo"
Nothing -> error "Cannot decode versions" Nothing -> error "Cannot decode versions"
newtype PackageName = PackageName { unPackageName :: String } newtype PackageName = PackageName { unPackageName :: String }
deriving newtype (Eq, Hashable, FromJSONKey, ToJSONKey, Show) deriving newtype (Eq, Hashable, FromJSONKey, ToJSONKey, Show)
@ -106,10 +111,62 @@ parsePackageSpec =
parsePackage :: Parser (PackageName, PackageSpec) parsePackage :: Parser (PackageName, PackageSpec)
parsePackage = (,) <$> parsePackageName <*> parsePackageSpec parsePackage = (,) <$> parsePackageName <*> parsePackageSpec
-- FOOs -------------------------------------------------------------------------------
-- PackageSpec State helpers
-------------------------------------------------------------------------------
preparePackageURL :: PackageSpec -> IO String whenNotSet
preparePackageURL = const $ pure "foo" :: T.Text
-> StateT (PackageName, PackageSpec) IO ()
-> StateT (PackageName, PackageSpec) IO ()
whenNotSet attrName act = getPackageSpecAttr attrName >>= \case
Just _ -> pure ()
Nothing -> act
withPackageSpecAttr
:: T.Text
-> (Value -> StateT (PackageName, PackageSpec) IO ())
-> StateT (PackageName, PackageSpec) IO ()
withPackageSpecAttr attrName act = getPackageSpecAttr attrName >>= \case
Just v -> act v
Nothing -> pure ()
getPackageSpecAttr
:: T.Text
-> StateT (PackageName, PackageSpec) IO (Maybe Value)
getPackageSpecAttr attrName = do
(_, PackageSpec obj) <- get
pure $ HMap.lookup attrName obj
setPackageSpecAttr
:: T.Text -> Value
-> StateT (PackageName, PackageSpec) IO ()
setPackageSpecAttr attrName attrValue = do
(packageName, PackageSpec obj) <- get
let obj' = HMap.insert attrName attrValue obj
put (packageName, PackageSpec obj')
setPackageName
:: String -> StateT (PackageName, PackageSpec) IO ()
setPackageName packageName = do
(_, spec) <- get
put (PackageName packageName, spec)
hasPackageSpecAttrs
:: [String]
-> StateT (PackageName, PackageSpec) IO Bool
hasPackageSpecAttrs attrNames = do
(_, PackageSpec obj) <- get
pure $ all (\k -> HMap.member (T.pack k) obj) attrNames
packageSpecStringValues :: PackageSpec -> [(String, String)]
packageSpecStringValues (PackageSpec m) = mapMaybe toVal (HMap.toList m)
where
toVal :: (T.Text, Value) -> Maybe (String, String)
toVal = \case
(key, String val) -> Just (T.unpack key, T.unpack val)
_ -> Nothing
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- INIT -- INIT
@ -150,31 +207,79 @@ cmdInit = do
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
parseCmdAdd :: ParserInfo (IO ()) parseCmdAdd :: ParserInfo (IO ())
parseCmdAdd = (info ((cmdAdd <$> parsePackages) <**> helper)) fullDesc parseCmdAdd = (info ((cmdAdd <$> parsePackage) <**> helper)) fullDesc
where
parsePackages :: Parser [(PackageName, PackageSpec)]
parsePackages = some parsePackage
cmdAdd :: [(PackageName, PackageSpec)] -> IO () cmdAdd :: (PackageName, PackageSpec) -> IO ()
cmdAdd (package@(packageName, _) : _) = do cmdAdd package = do
putStrLn $ "Adding " <> unPackageName packageName
print package (packageName, packageSpec) <- addCompletePackageSpec package
VersionsSpec versionsSpec <- getVersionsSpec
-- TODO: new package Spec
let fileVersionsValue' = versionsSpec <> HMap.empty
versionsSpec <- HMap.insert packageName packageSpec . unVersionsSpec <$>
getVersionsSpec
putStrLn $ "Writing new versions file" putStrLn $ "Writing new versions file"
encodeFile fileVersionsJson fileVersionsValue' print versionsSpec
-- encodeFile fileVersionsJson fileVersionsValue'
addCompletePackageSpec addCompletePackageSpec
:: (PackageName, PackageSpec) :: (PackageName, PackageSpec)
-> IO (PackageName, PackageSpec) -> IO (PackageName, PackageSpec)
addCompletePackageSpec x = do addCompletePackageSpec x@(PackageName str, _) = flip execStateT x $ do
-- Figures out the owner and repo
case span (/= '/') str of
(owner@(_:_), '/':repo@(_:_)) -> do
whenNotSet "owner" $
setPackageSpecAttr "owner" (String $ T.pack owner)
whenNotSet "repo" $ do
setPackageSpecAttr "repo" (String $ T.pack repo)
setPackageName repo
_ -> pure ()
pure x -- In case we have @owner@ and @repo@, pull some data from GitHub
(,) <$> getPackageSpecAttr "owner" <*> getPackageSpecAttr "repo" >>= \case
(Just (String owner), Just (String repo)) -> do
liftIO (GH.executeRequest' $ GH.repositoryR (GH.N owner) (GH.N repo))
>>= \case
Right ghRepo -> do
-- Description
whenNotSet "description" $ case GH.repoDescription ghRepo of
Just descr -> setPackageSpecAttr "description" (String descr)
Nothing -> pure ()
-- Branch and rev
whenNotSet "branch" $ case GH.repoDefaultBranch ghRepo of
Just branch -> do
setPackageSpecAttr "branch" (String branch)
liftIO (GH.executeRequest' $
GH.commitsWithOptionsForR
(GH.N owner) (GH.N repo) (GH.FetchAtLeast 1)
[GH.CommitQuerySha branch]) >>= \case
Right (toList -> (commit:_)) -> do
let GH.N rev = GH.commitSha commit
setPackageSpecAttr "rev" (String rev)
_ -> pure ()
Nothing -> pure ()
-- Figures out the URL template
whenNotSet "url_template" $
setPackageSpecAttr "url_template" (String $ T.pack githubURLTemplate)
-- Figures out the URL from the template
withPackageSpecAttr "url_template" (\case
String (T.unpack -> template) -> do
(_, packageSpec) <- get
let stringValues = packageSpecStringValues packageSpec
case renderTemplate stringValues template of
Just renderedURL ->
setPackageSpecAttr "url" (String $ T.pack renderedURL)
Nothing -> pure ()
_ -> pure ()
)
where
githubURLTemplate :: String
githubURLTemplate =
"https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
-- SHOW -- SHOW
@ -202,10 +307,10 @@ cmdShow = do
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
parseCmdUpdate :: ParserInfo (IO ()) parseCmdUpdate :: ParserInfo (IO ())
parseCmdUpdate = info (pure cmdUpdate <**> helper) fullDesc parseCmdUpdate = info ((cmdUpdate <$> parsePackage) <**> helper) fullDesc
cmdUpdate :: IO () cmdUpdate :: (PackageName, PackageSpec) -> IO ()
cmdUpdate = do cmdUpdate pkgs = do
putStrLn $ "Updating versions file" putStrLn $ "Updating versions file"
VersionsSpec fileVersionsValue <- getVersionsSpec VersionsSpec fileVersionsValue <- getVersionsSpec
@ -213,13 +318,14 @@ cmdUpdate = do
fileVersionsValue' <- forWithKeyM fileVersionsValue $ \key spec -> do fileVersionsValue' <- forWithKeyM fileVersionsValue $ \key spec -> do
putStrLn $ "Package: " <> unPackageName key putStrLn $ "Package: " <> unPackageName key
packageUrl <- preparePackageURL spec -- TODO: use StateT
-- let packageUrl <- renderTemplate
putStrLn $ " URL: " <> packageUrl -- putStrLn $ " URL: " <> packageUrl
sha256 <- nixPrefetchURL packageUrl -- sha256 <- nixPrefetchURL packageUrl
putStrLn $ " SHA256: " <> sha256 -- putStrLn $ " SHA256: " <> sha256
putStrLn $ "Writing new versions file" putStrLn $ "Writing new versions file"
encodeFile fileVersionsJson fileVersionsValue' encodeFile fileVersionsJson fileVersionsValue'
@ -297,3 +403,18 @@ mapWithKeyM_
mapWithKeyM_ f m = do mapWithKeyM_ f m = do
forM_ (HMap.toList m) $ \(k, v) -> forM_ (HMap.toList m) $ \(k, v) ->
HMap.singleton k <$> f k v HMap.singleton k <$> f k v
-- | Renders the template. Returns 'Nothing' if some of the attributes are
-- missing.
--
-- renderTemplate [("foo", "bar")] "<foo>" == Just "bar"
-- renderTemplate [("foo", "bar")] "<baz>" == Nothing
renderTemplate :: [(String, String)] -> String -> Maybe String
renderTemplate vals = \case
'<':str -> do
case span (/= '>') str of
(key, '>':rest) ->
liftA2 (<>) (lookup key vals) (renderTemplate vals rest)
c:str -> (c:) <$> renderTemplate vals str
[] -> Just []

View File

@ -80,6 +80,8 @@ in pkgs.hello
* `--gitlab`: use gitlab instead of GitHub * `--gitlab`: use gitlab instead of GitHub
* `--attribute <attribute> <value>`: sets `<attribute>` to `<value>` * `--attribute <attribute> <value>`: sets `<attribute>` to `<value>`
If the package already exists, merges with the package (prior to heuristics)
#### update #### update
* `[p [--commit] [--branch]]` * `[p [--commit] [--branch]]`
@ -92,11 +94,13 @@ in pkgs.hello
#### show #### show
`[--branch] [--rev] [--owner] [--repo] [--attribute <attribute>] <p1> <p2>`... * Shows all packages
if no attribute (br, rev, ...) is given, all attributes are shown for
`<packages>`. Otherwise the specified attributes are shown. If no package is #### drop
specified: `<packages> = <all packages>`, otherwise `<packages>` is set to
the specified packages. `<p1> <p2>`
* Drops the specified packages
**NOTE**: should the URLs be used instead? or more simply, how do we differentiate between Gitlab/GitHub? **NOTE**: should the URLs be used instead? or more simply, how do we differentiate between Gitlab/GitHub?

View File

@ -7,5 +7,7 @@ executable:
- aeson - aeson
- directory - directory
- filepath - filepath
- github
- mtl
- optparse-applicative - optparse-applicative
- unordered-containers - unordered-containers