mirror of
https://github.com/nmattia/niv.git
synced 2024-11-07 22:36:53 +03:00
Add attr discovery
This commit is contained in:
parent
d2758102b6
commit
7628070e98
173
Main.hs
173
Main.hs
@ -9,11 +9,15 @@
|
||||
-- TODO: format code
|
||||
|
||||
import Control.Monad
|
||||
import Control.Monad.State
|
||||
import Data.Aeson
|
||||
import Data.Bifunctor
|
||||
import Data.Char (toUpper)
|
||||
import Data.Hashable (Hashable)
|
||||
import Data.Maybe (mapMaybe)
|
||||
import Data.Semigroup ((<>))
|
||||
import Data.String
|
||||
import GHC.Exts (toList)
|
||||
import Options.Applicative
|
||||
import System.Directory
|
||||
import System.FilePath
|
||||
@ -22,6 +26,8 @@ import qualified Data.ByteString.Lazy as L
|
||||
import qualified Data.HashMap.Strict as HMap
|
||||
import qualified Data.List.NonEmpty as NE
|
||||
import qualified Data.Text as T
|
||||
import qualified GitHub as GH
|
||||
import qualified GitHub.Data.Name as GH
|
||||
|
||||
fileFetchNix :: FilePath
|
||||
fileFetchNix = "nix" </> "fetch.nix"
|
||||
@ -62,7 +68,6 @@ getVersionsSpec = do
|
||||
Just _ -> error "foo"
|
||||
Nothing -> error "Cannot decode versions"
|
||||
|
||||
|
||||
newtype PackageName = PackageName { unPackageName :: String }
|
||||
deriving newtype (Eq, Hashable, FromJSONKey, ToJSONKey, Show)
|
||||
|
||||
@ -106,10 +111,62 @@ parsePackageSpec =
|
||||
parsePackage :: Parser (PackageName, PackageSpec)
|
||||
parsePackage = (,) <$> parsePackageName <*> parsePackageSpec
|
||||
|
||||
-- FOOs
|
||||
-------------------------------------------------------------------------------
|
||||
-- PackageSpec State helpers
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
preparePackageURL :: PackageSpec -> IO String
|
||||
preparePackageURL = const $ pure "foo"
|
||||
whenNotSet
|
||||
:: 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
|
||||
@ -150,31 +207,79 @@ cmdInit = do
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
parseCmdAdd :: ParserInfo (IO ())
|
||||
parseCmdAdd = (info ((cmdAdd <$> parsePackages) <**> helper)) fullDesc
|
||||
where
|
||||
parsePackages :: Parser [(PackageName, PackageSpec)]
|
||||
parsePackages = some parsePackage
|
||||
parseCmdAdd = (info ((cmdAdd <$> parsePackage) <**> helper)) fullDesc
|
||||
|
||||
cmdAdd :: [(PackageName, PackageSpec)] -> IO ()
|
||||
cmdAdd (package@(packageName, _) : _) = do
|
||||
putStrLn $ "Adding " <> unPackageName packageName
|
||||
cmdAdd :: (PackageName, PackageSpec) -> IO ()
|
||||
cmdAdd package = do
|
||||
|
||||
print package
|
||||
VersionsSpec versionsSpec <- getVersionsSpec
|
||||
|
||||
-- TODO: new package Spec
|
||||
let fileVersionsValue' = versionsSpec <> HMap.empty
|
||||
(packageName, packageSpec) <- addCompletePackageSpec package
|
||||
|
||||
versionsSpec <- HMap.insert packageName packageSpec . unVersionsSpec <$>
|
||||
getVersionsSpec
|
||||
putStrLn $ "Writing new versions file"
|
||||
encodeFile fileVersionsJson fileVersionsValue'
|
||||
print versionsSpec
|
||||
-- encodeFile fileVersionsJson fileVersionsValue'
|
||||
|
||||
addCompletePackageSpec
|
||||
:: (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
|
||||
@ -202,10 +307,10 @@ cmdShow = do
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
parseCmdUpdate :: ParserInfo (IO ())
|
||||
parseCmdUpdate = info (pure cmdUpdate <**> helper) fullDesc
|
||||
parseCmdUpdate = info ((cmdUpdate <$> parsePackage) <**> helper) fullDesc
|
||||
|
||||
cmdUpdate :: IO ()
|
||||
cmdUpdate = do
|
||||
cmdUpdate :: (PackageName, PackageSpec) -> IO ()
|
||||
cmdUpdate pkgs = do
|
||||
putStrLn $ "Updating versions file"
|
||||
|
||||
VersionsSpec fileVersionsValue <- getVersionsSpec
|
||||
@ -213,13 +318,14 @@ cmdUpdate = do
|
||||
fileVersionsValue' <- forWithKeyM fileVersionsValue $ \key spec -> do
|
||||
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"
|
||||
encodeFile fileVersionsJson fileVersionsValue'
|
||||
@ -297,3 +403,18 @@ mapWithKeyM_
|
||||
mapWithKeyM_ f m = do
|
||||
forM_ (HMap.toList m) $ \(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 []
|
||||
|
14
README.md
14
README.md
@ -80,6 +80,8 @@ in pkgs.hello
|
||||
* `--gitlab`: use gitlab instead of GitHub
|
||||
* `--attribute <attribute> <value>`: sets `<attribute>` to `<value>`
|
||||
|
||||
If the package already exists, merges with the package (prior to heuristics)
|
||||
|
||||
#### update
|
||||
|
||||
* `[p [--commit] [--branch]]`
|
||||
@ -92,11 +94,13 @@ in pkgs.hello
|
||||
|
||||
#### show
|
||||
|
||||
`[--branch] [--rev] [--owner] [--repo] [--attribute <attribute>] <p1> <p2>`...
|
||||
if no attribute (br, rev, ...) is given, all attributes are shown for
|
||||
`<packages>`. Otherwise the specified attributes are shown. If no package is
|
||||
specified: `<packages> = <all packages>`, otherwise `<packages>` is set to
|
||||
the specified packages.
|
||||
* Shows all packages
|
||||
|
||||
#### drop
|
||||
|
||||
`<p1> <p2>`
|
||||
|
||||
* Drops the specified packages
|
||||
|
||||
**NOTE**: should the URLs be used instead? or more simply, how do we differentiate between Gitlab/GitHub?
|
||||
|
||||
|
@ -7,5 +7,7 @@ executable:
|
||||
- aeson
|
||||
- directory
|
||||
- filepath
|
||||
- github
|
||||
- mtl
|
||||
- optparse-applicative
|
||||
- unordered-containers
|
||||
|
Loading…
Reference in New Issue
Block a user