diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3ba9e36..96fbbab 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,7 +3,7 @@ on: [push, pull_request] jobs: build: name: ghc ${{ matrix.ghc }} - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: matrix: ghc: ["8.6.5", "8.8.4", "8.10.5", "9.0.1"] @@ -11,18 +11,18 @@ jobs: env: CONFIG: "--enable-tests --enable-benchmarks" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 - name: Install system dependencies run: sudo apt-get install -y libsodium-dev - name: Setup Haskell id: setup-haskell-cabal - uses: haskell/actions/setup@v1 + uses: haskell-actions/setup@v2 with: ghc-version: ${{ matrix.ghc }} cabal-version: ${{ matrix.cabal }} - run: cabal v2-update - run: cabal v2-freeze $CONFIG - - uses: actions/cache@v2 + - uses: actions/cache@v5 with: path: | ~/.cabal/store diff --git a/.gitignore b/.gitignore index 8f0f594..0fe7cf8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .stack-work/ dist/ dist-newstyle/ +result \ No newline at end of file diff --git a/README.md b/README.md index b6ca554..5d2c3a0 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,20 @@ # jenkinsPlugins2nix ``` -Usage: jenkinsPlugins2nix [-r|--dependency-resolution [as-given|latest]] +Usage: jenkinsPlugins2nix [-r|--dependency-resolution [as-given|latest|jenkins[:VERSION]]] + [--skip-optional] (-p|--plugin PLUGIN_NAME{:PLUGIN_VERSION}) Generate nix expressions for requested Jenkins plugins. Available options: - -r,--dependency-resolution [as-given|latest] - Dependency resolution (default: latest) + -r,--dependency-resolution [as-given|latest|jenkins[:VERSION]] + Dependency resolution (default: latest). Use + `jenkins:` to resolve against a specific + Jenkins version (for example: `-r jenkins:2.401`). -p,--plugin PLUGIN_NAME{:PLUGIN_VERSION} Plugins we should generate nix for. Latest version is used if not specified. + --skip-optional Skip optional dependencies when downloading plugins -h,--help Show this help text ``` @@ -45,3 +49,17 @@ latest one. In case we only ask for `A`, the version of `B` will depend on `--resolution-strategy`. + +### Jenkins-specific strategy + +You can select a specific Jenkins version for dependency resolution with +`-r jenkins:`. When this strategy is used, dependencies without an +explicit version will be resolved with regard to the supplied Jenkins +version (for example `-r jenkins:2.401`). + +### Optional dependencies + +By default the tool downloads all dependencies (including optional ones) to +avoid backward-compatibility problems. Use `--skip-optional` to exclude +optional dependencies from downloads when you prefer a minimal set of +dependencies. diff --git a/app/Main.hs b/app/Main.hs index 0a68b90..37a6e0f 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -10,9 +10,8 @@ module Main (main) where import qualified Data.Bimap as Bimap import Data.List (intersperse) -import Data.Monoid (mconcat, (<>)) import qualified Data.Text as Text -import Data.Text.Prettyprint.Doc.Render.Terminal (putDoc) +import qualified Prettyprinter.Render.Terminal as Pretty import Nix.JenkinsPlugins2Nix import Nix.JenkinsPlugins2Nix.Types import qualified Options.Applicative as Opt @@ -28,7 +27,7 @@ main = do hPutStrLn stderr err exitFailure Right p -> do - putDoc p + Pretty.putDoc p exitSuccess where opts = Opt.info (parseConfig Opt.<**> Opt.helper) @@ -43,7 +42,7 @@ parseConfig = Config <> Opt.short 'r' <> Opt.help "Dependency resolution" <> Opt.showDefaultWith (resolutions Bimap.!) - <> Opt.metavar (printf "[%s]" . mconcat . intersperse "|" $ Bimap.keysR resolutions) + <> Opt.metavar (printf "[%s]" . concat . intersperse "|" $ Bimap.keysR resolutions) <> Opt.value Latest ) <*> Opt.some (Opt.option requestedPluginReader ( Opt.metavar "PLUGIN_NAME{:PLUGIN_VERSION}" @@ -51,15 +50,22 @@ parseConfig = Config <> Opt.short 'p' <> Opt.help "Plugins we should generate nix for. Latest version is used if not specified." ) ) + <*> Opt.flag Optional Mandatory + ( Opt.long "skip-optional" + <> Opt.help "skip optional dependencies" ) where resolutions :: Bimap.Bimap ResolutionStrategy String - resolutions = Bimap.fromList [(AsGiven, "as-given"), (Latest, "latest")] + resolutions = Bimap.fromList [(AsGiven, "as-given"), (Latest, "latest"), (JenkinsVersion(""), "jenkins")] resolutionReader :: Opt.ReadM ResolutionStrategy - resolutionReader = Opt.eitherReader $ \s -> case Bimap.lookupR s resolutions of - Nothing -> Left $ "Invalid dependency resolution, needs to be one of " - <> show (Bimap.keysR resolutions) - Just v -> Right v + resolutionReader = let + strat = \s -> (Text.unpack (head (Text.splitOn ":" (Text.pack s)))) + version = \s -> (Text.unpack(last (Text.splitOn ":" (Text.pack s)))) + in Opt.eitherReader $ \s -> case Bimap.lookupR (strat s) resolutions of + Nothing -> Left $ "Invalid dependency resolution, needs to be one of " + <> show (Bimap.keysR resolutions) + Just (JenkinsVersion("")) -> Right (JenkinsVersion(version s)) + Just v -> Right v requestedPluginReader :: Opt.ReadM RequestedPlugin requestedPluginReader = Opt.maybeReader $ \p -> Just $! case break (== ':') p of diff --git a/flake.lock b/flake.lock index 75fa2f9..21de1be 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1606424373, - "narHash": "sha256-oq8d4//CJOrVj+EcOaSXvMebvuTkmBJuT5tzlfewUnQ=", + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", "owner": "edolstra", "repo": "flake-compat", - "rev": "99f1c2157fba4bfe6211a321fd0ee43199025dbf", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", "type": "github" }, "original": { @@ -18,11 +18,11 @@ }, "flake-utils": { "locked": { - "lastModified": 1623875721, - "narHash": "sha256-A8BU7bjS5GirpAUv4QA+QnJ4CceLHkcXdRp4xITDB0s=", + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "owner": "numtide", "repo": "flake-utils", - "rev": "f7e004a55b120c02ecb6219596820fcd32ca8772", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "type": "github" }, "original": { @@ -33,16 +33,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1622516815, - "narHash": "sha256-ZjBd81a6J3TwtlBr3rHsZspYUwT9OdhDk+a/SgSEf7I=", + "lastModified": 1685566663, + "narHash": "sha256-btHN1czJ6rzteeCuE/PNrdssqYD2nIA4w48miQAFloM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "7e9b0dff974c89e070da1ad85713ff3c20b0ca97", + "rev": "4ecab3273592f27479a583fb6d975d4aba3486fe", "type": "github" }, "original": { "owner": "NixOS", - "ref": "21.05", + "ref": "23.05", "repo": "nixpkgs", "type": "github" } diff --git a/flake.nix b/flake.nix index 147bc37..de6213a 100644 --- a/flake.nix +++ b/flake.nix @@ -2,7 +2,7 @@ description = "jenkinsPlugins2nix"; inputs = { flake-utils.url = "github:numtide/flake-utils"; - nixpkgs.url = "github:NixOS/nixpkgs/21.05"; + nixpkgs.url = "github:NixOS/nixpkgs/23.05"; flake-compat = { url = "github:edolstra/flake-compat"; flake = false; @@ -28,11 +28,4 @@ }); } ); - - # packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello; - - # defaultPackage.x86_64-linux = self.packages.x86_64-linux.hello; - - - } diff --git a/jenkinsPlugins2nix.cabal b/jenkinsPlugins2nix.cabal index 8e96e18..eb7c451 100644 --- a/jenkinsPlugins2nix.cabal +++ b/jenkinsPlugins2nix.cabal @@ -1,5 +1,5 @@ name: jenkinsPlugins2nix -version: 0.5.0.0 +version: 0.5.0.2 synopsis: Generate nix for Jenkins plugins. description: Generate nix for Jenkins plugins. homepage: https://github.com/Fuuzetsu/jenkinsPlugins2nix#readme @@ -20,15 +20,15 @@ library Nix.JenkinsPlugins2Nix.Parser Nix.JenkinsPlugins2Nix.Types build-depends: base >=4.7 && < 5 - , attoparsec >=0.13 && <0.14 - , bytestring >=0.10 && <0.11 + , attoparsec >=0.13 && <0.15 + , bytestring >=0.10 && <0.12 , containers >=0.6 && <0.7 - , cryptonite >=0.24 && <0.30 - , hnix >=0.6 && <0.15 + , cryptonite >=0.24 && <0.31 + , hnix >=0.6 && <0.18 , http-conduit >=2.3 && <2.4 - , mtl >=2.2 && <2.3 + , mtl >=2.2 && <2.4 , prettyprinter >=1.2 && <1.8 - , text >=1.2 && <1.3 + , text , zip-archive >=0.4 && <0.5 default-language: Haskell2010 ghc-options: -Wall @@ -38,9 +38,9 @@ executable jenkinsPlugins2nix main-is: Main.hs ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall build-depends: base - , bimap >=0.3 && <0.5 + , bimap >=0.3 && <0.6 , jenkinsPlugins2nix - , optparse-applicative >=0.14 && <0.17 + , optparse-applicative >=0.14 && <0.18 , prettyprinter-ansi-terminal >=1.1 && <1.2 , text default-language: Haskell2010 @@ -53,7 +53,8 @@ test-suite jenkinsPlugins2nix-test build-depends: base , containers , jenkinsPlugins2nix - , tasty-hspec >=1.1 && <1.2 + , hspec + , tasty-hspec >=1.1 && <1.3 , text ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall default-language: Haskell2010 diff --git a/jenkinsPlugins2nix.nix b/jenkinsPlugins2nix.nix index 31cbc3f..deac109 100644 --- a/jenkinsPlugins2nix.nix +++ b/jenkinsPlugins2nix.nix @@ -5,7 +5,7 @@ }: mkDerivation { pname = "jenkinsPlugins2nix"; - version = "0.3.0.1"; + version = "0.5.0.1"; src = ./.; isLibrary = true; isExecutable = true; diff --git a/src/Nix/JenkinsPlugins2Nix.hs b/src/Nix/JenkinsPlugins2Nix.hs index 9dd91df..645ac4a 100644 --- a/src/Nix/JenkinsPlugins2Nix.hs +++ b/src/Nix/JenkinsPlugins2Nix.hs @@ -15,12 +15,12 @@ import qualified Crypto.Hash as Hash import qualified Data.ByteString.Lazy as BSL import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map -import Data.Monoid ((<>)) +import qualified Data.Set as Set import Data.Text (Text) import qualified Data.Text as Text import qualified Data.Text.Encoding as Text import qualified Data.Text.IO as Text -import Data.Text.Prettyprint.Doc (Doc) +import Prettyprinter (Doc) import qualified Network.HTTP.Simple as HTTP import qualified Nix.Expr as Nix import Nix.Expr.Shorthands ((@@)) @@ -70,11 +70,12 @@ downloadPlugin p = do -- | Download the given plugin as well as recursively download its dependencies. downloadPluginsRecursive :: ResolutionStrategy -- ^ Decide what version of dependencies to pick. + -> PluginResolution -- ^ Wheter to include or skip optional dependencies -> Map Text RequestedPlugin -- ^ Plugins user requested. -> Map Text Plugin -- ^ Already downloaded plugins. -> RequestedPlugin -- ^ Plugin we're going to download. -> MTL.ExceptT String IO (Map Text Plugin) -downloadPluginsRecursive strategy uPs m p = if Map.member (requested_name p) m +downloadPluginsRecursive strategy presolution uPs m p = if Map.member (requested_name p) m then return m else do -- Adjust the requested plugin based on whether it was @@ -90,26 +91,29 @@ downloadPluginsRecursive strategy uPs m p = if Map.member (requested_name p) m -- It's not a user-specified plugin and we want the latest -- version per strategy so download the latest one. Latest -> p { requested_version = Nothing } + -- Use a specific Jenkins version supplied by the strategy. + JenkinsVersion v -> p { requested_version = Just (Text.pack v) } -- The user has asked for this plugin explicitly so use -- their possibly-versioned request rather than picking -- based on versions listed in manifest dependencies. Just userPlugin -> userPlugin plugin <- MTL.ExceptT $ downloadPlugin adjustedPlugin - foldM (\m' p' -> downloadPluginsRecursive strategy uPs m' $ + foldM (\m' p' -> downloadPluginsRecursive strategy presolution uPs m' $ RequestedPlugin { requested_name = plugin_dependency_name p' , requested_version = Just $! plugin_dependency_version p' }) (Map.insert (requested_name p) plugin m) - (plugin_dependencies $ manifest plugin) + (Set.filter (\dep -> plugin_dependency_resolution dep <= presolution) + (plugin_dependencies $ manifest plugin)) -- | Pretty-print nix expression for all the given plugins and their -- dependencies that the user asked for. mkExprsFor :: Config -> IO (Either String (Doc ann)) -mkExprsFor (Config { resolution_strategy = st, requested_plugins = ps }) = do +mkExprsFor (Config { resolution_strategy = st, requested_plugins = ps, plugin_resolution = pr }) = do eplugins <- MTL.runExceptT $ do let userPlugins = Map.fromList $ map (requested_name &&& id) ps - plugins <- foldM (downloadPluginsRecursive st userPlugins) Map.empty ps + plugins <- foldM (downloadPluginsRecursive st pr userPlugins) Map.empty ps return $ Map.elems plugins return $! case eplugins of Left err -> Left err @@ -123,8 +127,8 @@ mkExprsFor (Config { resolution_strategy = st, requested_plugins = ps }) = do ] False) $ Nix.mkSym "stdenv.mkDerivation" @@ Nix.mkNonRecSet - [ Nix.inherit [ Nix.StaticKey "name" - , Nix.StaticKey "src" ] Nix.nullPos + [ Nix.inherit [ "name" + , "src" ] , "phases" Nix.$= Nix.mkStr "installPhase" , "installPhase" Nix.$= Nix.mkStr "cp $src $out" ] diff --git a/src/Nix/JenkinsPlugins2Nix/Parser.hs b/src/Nix/JenkinsPlugins2Nix/Parser.hs index 16c7cb5..791151b 100644 --- a/src/Nix/JenkinsPlugins2Nix/Parser.hs +++ b/src/Nix/JenkinsPlugins2Nix/Parser.hs @@ -14,10 +14,8 @@ module Nix.JenkinsPlugins2Nix.Parser import Control.Applicative import Control.Monad (void) import qualified Data.Attoparsec.Text as A -import Data.Either (either) import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map -import Data.Monoid ((<>)) import Data.Set (Set) import qualified Data.Set as Set import Data.Text (Text) diff --git a/src/Nix/JenkinsPlugins2Nix/Types.hs b/src/Nix/JenkinsPlugins2Nix/Types.hs index ea46160..7d52c63 100644 --- a/src/Nix/JenkinsPlugins2Nix/Types.hs +++ b/src/Nix/JenkinsPlugins2Nix/Types.hs @@ -27,6 +27,7 @@ data ResolutionStrategy = AsGiven -- | Always pick latest version of the dependency we're told about. | Latest + | JenkinsVersion(String) deriving (Show, Eq, Ord) -- | Program configuration @@ -35,6 +36,8 @@ data Config = Config resolution_strategy :: !ResolutionStrategy -- | User-required plugins. , requested_plugins :: ![RequestedPlugin] + -- | Plugin resolution + , plugin_resolution :: !PluginResolution } deriving (Show, Eq, Ord) -- | Plugin that user requested on the command line. diff --git a/test/Nix/JenkinsPlugins2Nix/Tests/Parser.hs b/test/Nix/JenkinsPlugins2Nix/Tests/Parser.hs index 8ddeff9..0141c1a 100644 --- a/test/Nix/JenkinsPlugins2Nix/Tests/Parser.hs +++ b/test/Nix/JenkinsPlugins2Nix/Tests/Parser.hs @@ -11,7 +11,7 @@ import qualified Data.Set as Set import qualified Data.Text as Text import Nix.JenkinsPlugins2Nix.Parser (runParseManifest) import Nix.JenkinsPlugins2Nix.Types -import qualified Test.Tasty.Hspec as Hspec +import qualified Test.Hspec as Hspec spec :: Hspec.Spec spec = Hspec.describe "parser spec" $ do diff --git a/test/Spec.hs b/test/Spec.hs index 097c3ca..83fa438 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -1,7 +1,7 @@ module Main where import qualified Nix.JenkinsPlugins2Nix.Tests.Parser as Parser -import qualified Test.Tasty.Hspec as Hspec +import qualified Test.Hspec as Hspec main :: IO () main = Hspec.hspec $ do