diff --git a/Makefile b/Makefile index 37fbe2e..7ddb1c5 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,11 @@ -install: +install: build stack build --copy-bins + + gitlab-helper --render-man-page > gitlab-helper.1 + gzip -9 -c gitlab-helper.1 > gitlab-helper.1.gz + sudo mv gitlab-helper.1.gz /usr/local/share/man/man1/gitlab-helper.1.gz + sudo mandb + rm gitlab-helper.1 check: format lint diff --git a/README.md b/README.md index 2013f03..02883b3 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ The app will look at several places for your configuration, in the following ord 1. command-line arguments 1. environment variables +1. A config file at the file path provided in the `CONFIG_FILE` environment variable or the `--config-file` option 1. `${PWD}/.gitlab-helper.yml` (the directory in which the executable is started) 1. `~/.gitlab-helper.yml` diff --git a/app/Main.hs b/app/Main.hs index 93e1cf4..d4f62ef 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -1,7 +1,6 @@ module Main where import Program -import Relude main :: IO () main = run diff --git a/gitlab-helper.cabal b/gitlab-helper.cabal index 390d642..a268463 100644 --- a/gitlab-helper.cabal +++ b/gitlab-helper.cabal @@ -1,6 +1,6 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.37.0. +-- This file has been generated from package.yaml by hpack version 0.38.0. -- -- see: https://github.com/sol/hpack @@ -27,11 +27,7 @@ library exposed-modules: App Branches - Config.Config - Config.Env - Config.File - Config.Optparse - Config.Types + Config Effects MergeRequests Program @@ -48,28 +44,23 @@ library build-depends: aeson , autodocodec - , barbies , base >=4.7 && <5 , burrito >=2.0.1.0 , colonnade , colourista , containers - , directory - , either - , envparse - , githash , gitlab-api-http-client-mtl , gitlab-api-types , http-conduit , http-types , network-uri - , optparse-applicative + , opt-env-conf + , path + , path-io , relude , scientific - , split , text , time - , yaml default-language: Haskell2010 executable gitlab-helper @@ -78,13 +69,10 @@ executable gitlab-helper Paths_gitlab_helper hs-source-dirs: app - default-extensions: - NoImplicitPrelude ghc-options: -fwrite-ide-info -hiedir=.hie -Wall -Wcompat -Widentities -Wincomplete-uni-patterns -Wincomplete-record-updates -Wredundant-constraints -fhide-source-paths -Wpartial-fields -Wmissing-deriving-strategies -Wunused-packages -threaded -rtsopts -with-rtsopts=-N build-depends: base >=4.7 && <5 , gitlab-helper - , relude default-language: Haskell2010 test-suite gitlab-helper-test @@ -95,15 +83,12 @@ test-suite gitlab-helper-test Paths_gitlab_helper hs-source-dirs: test - default-extensions: - NoImplicitPrelude ghc-options: -fwrite-ide-info -hiedir=.hie -Wall -Wcompat -Widentities -Wincomplete-uni-patterns -Wincomplete-record-updates -Wredundant-constraints -fhide-source-paths -Wpartial-fields -Wmissing-deriving-strategies -Wunused-packages -threaded -rtsopts -with-rtsopts=-N build-tool-depends: sydtest-discover:sydtest-discover build-depends: base >=4.7 && <5 , gitlab-helper - , optparse-applicative - , relude + , opt-env-conf-test , sydtest default-language: Haskell2010 diff --git a/package.yaml b/package.yaml index 68fd85a..3665b3a 100644 --- a/package.yaml +++ b/package.yaml @@ -14,10 +14,8 @@ description: Please see the README on GitHub at = 4.7 && < 5 - - relude -default-extensions: - - NoImplicitPrelude +default-extensions: [] ghc-options: - -fwrite-ide-info @@ -41,29 +39,27 @@ library: - -flate-specialise - -fspecialise-aggressively - -Wmissing-export-lists + default-extensions: + - NoImplicitPrelude dependencies: - aeson - autodocodec - - barbies - burrito >= 2.0.1.0 - colonnade - colourista - containers - - directory - - either - - envparse - - githash - http-conduit - http-types - gitlab-api-http-client-mtl - gitlab-api-types - network-uri - - optparse-applicative + - opt-env-conf + - path + - path-io + - relude - scientific - - split - text - time - - yaml executables: gitlab-helper: @@ -87,5 +83,5 @@ tests: - -with-rtsopts=-N dependencies: - gitlab-helper - - optparse-applicative + - opt-env-conf-test - sydtest diff --git a/src/App.hs b/src/App.hs index 96e2075..34ba670 100644 --- a/src/App.hs +++ b/src/App.hs @@ -5,7 +5,7 @@ module App (App (..)) where -import Config.Types +import Config import Gitlab.Client.MTL (HasApiToken (..), HasBaseUrl (..), HasUserAgent (..)) import Relude diff --git a/src/Branches.hs b/src/Branches.hs index 4531316..ae999f8 100644 --- a/src/Branches.hs +++ b/src/Branches.hs @@ -17,7 +17,7 @@ where import App import Colourista.Pure -import Config.Types (Config (..), WithArchivedProjects (SkipArchivedProjects)) +import Config (Config (..), WithArchivedProjects (SkipArchivedProjects)) import qualified Data.Text as T (intercalate) import Data.Time hiding (getCurrentTime) import Effects diff --git a/src/Config.hs b/src/Config.hs new file mode 100644 index 0000000..cc6ff30 --- /dev/null +++ b/src/Config.hs @@ -0,0 +1,281 @@ +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE OverloadedStrings #-} +{-# OPTIONS_GHC -Wno-orphans #-} + +module Config + ( Config (..), + Command (..), + MergeRequestUpdateAction (..), + MergeCiOption (..), + AuthorIs (..), + SearchTerm (..), + SearchTermTitle (..), + Year (..), + WithArchivedProjects (..), + MergeStatusRecheck (..), + Execution (..), + MetaScope (..), + ) +where + +import Autodocodec +import Gitlab.Client.MTL (ApiToken (..), BaseUrl (..), UserAgent (..)) +import Gitlab.Group (Group) +import Gitlab.Lib (Id (..)) +import Gitlab.Project (Project) +import Network.URI (parseURI) +import OptEnvConf hiding (Command) +import Path +import Path.IO +import Relude hiding (Reader, reader) + +data Config = Config + { groupId :: Id Group, + baseUrl :: BaseUrl, + apiToken :: ApiToken, + userAgent :: UserAgent, + projectsExcludeList :: [Id Project], + cmd :: Command + } + deriving stock (Show) + +configFiles :: Parser [Path Abs File] +configFiles = + sequenceA + [ runIO (resolveFile' ".gitlab-helper.yml"), + runIO (getHomeDir >>= \home -> resolveFile home ".gitlab-helper.yml") + ] + +instance HasParser Config where + settingsParser = + withCombinedYamlConfigs configFiles + $ subConfig "config" + $ subEnv_ "GLH" + $ Config + <$> setting + [ help "ID of the GitLab group to work with", + reader (Id <$> positiveIntReader), + long "group-id", + option, + env "GROUP_ID", + conf "groupId", + metavar "GROUP_ID" + ] + <*> setting + [ help "Base URL of your GitLab instance (e.g. `https://my.gitlab.com`)", + reader (BaseUrl <$> maybeReader parseURI), + long "base-url", + option, + env "BASE_URL", + conf "baseUrl", + metavar "URL" + ] + <*> setting + [ help "API Token to use for authorizing requests against the Gitlab API. `api` scope is required.", + reader (ApiToken <$> auto), + long "api-token", + option, + env "API_TOKEN", + conf "apiToken", + metavar "TOKEN" + ] + <*> setting + [ help "User agent to use for requests against the Gitlab API", + reader (UserAgent <$> auto), + long "user-agent", + option, + env "USER_AGENT", + conf "userAgent", + metavar "USER_AGENT", + value (UserAgent "gitlab-helper") + ] + <*> setting + [ help "set the list of projects to exclude as a comma-separated list of IDs", + reader (commaSeparatedList (Id <$> positiveIntReader)), + long "exclude-projects", + option, + conf "excludeProjects", + env "EXCLUDE_PROJECTS", + metavar "ID1,ID2,ID3", + value [] + ] + <*> settingsParser + +instance HasCodec BaseUrl where + codec = BaseUrl <$> bimapCodec (maybeToRight "can't parse URI" . parseURI) show stringCodec + +instance HasCodec ApiToken where + codec = dimapCodec ApiToken (\(ApiToken txt) -> txt) textCodec + +instance HasCodec UserAgent where + codec = dimapCodec UserAgent (\(UserAgent txt) -> txt) textCodec + +positiveIntReader :: Reader Int +positiveIntReader = maybeReader (find (> 0) <$> readMaybe) + +data Command + = ShowBranches + | EnableSourceBranchDeletionAfterMerge Execution + | ShowProjects + | ListProjectsMeta MetaScope + | ShowSchedules + | ShowMergeRequests MergeStatusRecheck + | EnableAllDiscussionsMustBeResolvedForMergeRequirement Execution + | EnableSuccessfulPipelineForMergeRequirement Execution + | SetMergeMethodToFastForward Execution + | CountSuccessfulDeployments Year WithArchivedProjects + | UpdateMergeRequests MergeRequestUpdateAction (Maybe AuthorIs) (Maybe (Either SearchTerm SearchTermTitle)) MergeStatusRecheck Execution + deriving stock (Show) + +instance HasParser Command where + settingsParser = + commands + [ command "show-branches" "Show branches" (pure ShowBranches), + command "show-projects" "Show projects" (pure ShowProjects), + command "list-projects-meta" "List projects in (almost) meta compatible JSON format" (ListProjectsMeta <$> settingsParser), + command "enable-source-branch-deletion" "Enable source branch deletion after merge for all projects in the group" (EnableSourceBranchDeletionAfterMerge <$> settingsParser), + command + "enable-all-discussions-must-be-resolved-for-merge-requirement" + "Enable the requirement that all discussions must be resolved for an MR to be merged for all projects" + (EnableAllDiscussionsMustBeResolvedForMergeRequirement <$> settingsParser), + command + "enable-successful-pipeline-for-merge-requirement" + "Enable the requirement that there must be a successful pipeline for an MR to be merged for all projects. CAUTION: Use with care, might not do what you want in projects without pipelines" + (EnableSuccessfulPipelineForMergeRequirement <$> settingsParser), + command "show-schedules" "Show Pipeline Schedules" (pure ShowSchedules), + command "show-merge-requests" "Show projects with and without enabled merge requests, list open merge requests" (ShowMergeRequests <$> settingsParser), + command + "count-deployments" + "Count the number of successful deployments per project (a successful push pipeline on the default branch is counted as a deployment)" + (CountSuccessfulDeployments <$> settingsParser <*> settingsParser), + command "set-merge-method-to-fast-forward" "Set the merge method for all projects to \"Fast Forward\"" (SetMergeMethodToFastForward <$> settingsParser), + command "update-merge-requests" "Update all MRs from a given user that match a given condition with a given command" mergeRequestUpdateCommandParser + ] + +mergeRequestUpdateCommandParser :: Parser Command +mergeRequestUpdateCommandParser = + UpdateMergeRequests + <$> settingsParser + <*> optional settingsParser + <*> optional rightOrLeft + <*> settingsParser + <*> settingsParser + +rightOrLeft :: (HasParser a, HasParser b) => Parser (Either a b) +rightOrLeft = Right <$> settingsParser <|> Left <$> settingsParser + +data Execution = DryRun | Execute deriving stock (Eq, Show) + +instance HasParser Execution where + settingsParser = + setting + [ help "whether to actually change the world via the API. By default, only a dry run will be performed", + switch Execute, + long "execute", + short 'x', + value DryRun + ] + +data MetaScope = MetaScopeGroup | MetaScopeAll deriving stock (Eq, Show) + +instance HasParser MetaScope where + settingsParser = + commands + [ command "group" "list projects in the group that is configured in the config file" (pure MetaScopeGroup), + command "all" "list all projects in all groups that are visible with the provided API token" (pure MetaScopeAll) + ] + +data MergeRequestUpdateAction = List | Rebase | Merge MergeCiOption | SetToDraft | MarkAsReady deriving stock (Show) + +instance HasParser MergeRequestUpdateAction where + settingsParser = + commands + [ command "rebase" "rebase the merge requests" (pure Rebase), + command "merge" "merge the merge requests" (Merge <$> settingsParser), + command "draft" "set the merge requests to `draft`" (pure SetToDraft), + command "ready" "mark the merge requests as ready" (pure MarkAsReady), + command "list" "list the merge requests" (pure List) + ] + +data MergeCiOption = PipelineMustSucceed | SkipCi deriving stock (Show) + +instance HasParser MergeCiOption where + settingsParser = + setting + [ help "don't enforce that a merge request requires a successful pipeline to be merged (also helpful for projects that don't have pipelines on non-default branches)", + switch SkipCi, + value PipelineMustSucceed, + long "skip-ci" + ] + +newtype AuthorIs = AuthorIs Int deriving stock (Show) + +instance HasParser AuthorIs where + settingsParser = + setting + [ help "only MRs opened by the user with this ID are taken into account", + reader (AuthorIs <$> positiveIntReader), + option, + short 'u', + long "user-id", + metavar "USER_ID" + ] + +newtype SearchTerm = SearchTerm String deriving stock (Show) + +instance HasParser SearchTerm where + settingsParser = + setting + [ help "A string that must appear in the MR description or title.", + option, + reader (SearchTerm <$> str), + short 's', + long "search", + metavar "TXT" + ] + +newtype SearchTermTitle = SearchTermTitle String deriving stock (Show) + +instance HasParser SearchTermTitle where + settingsParser = + setting + [ help "A string that must appear in the MR title.", + option, + reader (SearchTermTitle <$> str), + long "search-title", + metavar "TXT" + ] + +newtype Year = Year Int deriving stock (Show) + +instance HasParser Year where + settingsParser = + setting + [ help "Set the year", + option, + reader (Year <$> positiveIntReader), + long "year", + metavar "YEAR" + ] + +data WithArchivedProjects = IncludeArchivedProjects | SkipArchivedProjects deriving stock (Show) + +instance HasParser WithArchivedProjects where + settingsParser = + (\b -> if b then IncludeArchivedProjects else SkipArchivedProjects) + <$> yesNoSwitch + [ value False, + help "Include or skip archived projects", + long "include-archived-projects" + ] + +data MergeStatusRecheck = RecheckMergeStatus | NoRecheckMergeStatus deriving stock (Show) + +instance HasParser MergeStatusRecheck where + settingsParser = + (\b -> if b then RecheckMergeStatus else NoRecheckMergeStatus) + <$> yesNoSwitch + [ value False, + help "Trigger a recheck of the merge status of the merge requests. This is done on the Gitlab server and might have a performance impact so it's not done by default", + long "recheck-merge-status" + ] diff --git a/src/Config/Config.hs b/src/Config/Config.hs deleted file mode 100644 index 7c509d3..0000000 --- a/src/Config/Config.hs +++ /dev/null @@ -1,36 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} - -module Config.Config (parseConfigOrDie) where - -import Barbies (bsequence', bzipWith) -import Config.Env -import Config.File -import Config.Optparse -import Config.Types -import Data.Either.Validation -import qualified Data.Semigroup as S -import Gitlab.Client.MTL (UserAgent (..)) -import Relude - -parseConfigOrDie :: IO Config -parseConfigOrDie = parseConfig >>= either (die . show) pure - -parseConfig :: IO (Either (NonEmpty String) Config) -parseConfig = do - -- todo: wenn man hier ein kluges fold macht, könnte man short-circuiten - -- und ggf gar nicht erst versuchen eine source zu lesen wenn man sie eh nicht braucht - mkConfig <$> sequence [parseConfigFromOptions, parseFromEnv, readPartialOptionsFromLocalDir, readPartialOptionsFromHomeDir] - -mkConfig :: [PartialConfig (Compose Maybe S.First)] -> Either (NonEmpty String) Config -mkConfig = mkConfig' . mconcat - -mkConfig' :: PartialConfig (Compose Maybe S.First) -> Either (NonEmpty String) Config -mkConfig' inputs = validationToEither . fmap partialConfigToConfig . bsequence' . bzipWith fieldValueOrMissing msgs $ inputs <> defaultValues - where - msgs = PartialConfig (Const "Group ID missing") (Const "Base URL missing") (Const "API-Token missing") (Const "User-Agent missing") (Const "Project exclude list missing") (Const "Command misisng") - fieldValueOrMissing :: Const String a -> Compose Maybe S.First a -> Validation (NonEmpty String) a - fieldValueOrMissing (Const err) (Compose Nothing) = Failure $ err :| [] - fieldValueOrMissing _ (Compose (Just (S.First a))) = Success a - -defaultValues :: PartialConfig (Compose Maybe S.First) -defaultValues = PartialConfig mempty mempty mempty (pure (UserAgent "gitlab-helper")) (pure []) mempty diff --git a/src/Config/Env.hs b/src/Config/Env.hs deleted file mode 100644 index a2a3935..0000000 --- a/src/Config/Env.hs +++ /dev/null @@ -1,34 +0,0 @@ -module Config.Env (parseFromEnv) where - -import Barbies (TraversableB (btraverse)) -import Config.Types (PartialConfig (PartialConfig)) -import qualified Data.Semigroup as S (First (..)) -import qualified Env as E -import Gitlab.Client.MTL (ApiToken (ApiToken), BaseUrl (BaseUrl), UserAgent (UserAgent)) -import Gitlab.Lib (Id (..)) -import Network.URI (URI, parseAbsoluteURI) -import Relude hiding (Reader) -import System.Environment (getEnvironment) - -parseFromEnv :: IO (PartialConfig (Compose Maybe S.First)) -parseFromEnv = do - environment <- getEnvironment - let partialConfig = E.parsePure partialConfigParser environment - pure $ fromRight mempty partialConfig - -partialConfigParser :: E.Parser E.Error (PartialConfig (Compose Maybe S.First)) -partialConfigParser = - btraverse maybeFirstParser - $ PartialConfig - (Id <$> E.var E.auto "GLH_GROUP_ID" (E.help "ID of the Gitlab group you're working in")) - (BaseUrl <$> E.var (absoluteURIFromEnv <=< E.nonempty) "GLH_BASE_URL" (E.help "Base URL of the Gitlab instance (e.g. `https://gitlab.com/`)")) - (ApiToken <$> E.var (E.str <=< E.nonempty) "GLH_API_TOKEN" (E.help "API Token to use for authorizing requests against the Gitlab API. `api` scope is required.")) - (UserAgent <$> E.var (E.str <=< E.nonempty) "GLH_USER_AGENT" (E.help "User-Agent to use for requests against the Gitlab API.")) - (fmap Id <$> E.var (traverse E.auto <=< E.splitOn ',') "GLH_EXCLUDE_PROJECTS" (E.help "List of projects to exclude")) - empty - -maybeFirstParser :: E.Parser E.Error a -> E.Parser E.Error (Compose Maybe S.First a) -maybeFirstParser parser = Compose . fmap S.First <$> optional parser - -absoluteURIFromEnv :: E.Reader E.Error URI -absoluteURIFromEnv s = maybeToRight (E.UnreadError "") (parseAbsoluteURI s) diff --git a/src/Config/File.hs b/src/Config/File.hs deleted file mode 100644 index b91fbbc..0000000 --- a/src/Config/File.hs +++ /dev/null @@ -1,58 +0,0 @@ -{-# LANGUAGE DerivingVia #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE TypeApplications #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Config.File (readPartialOptionsFromHomeDir, readPartialOptionsFromLocalDir) where - -import Autodocodec -import Barbies (bmap) -import Config.Types -import Data.Aeson -import qualified Data.Semigroup as S -import Data.Yaml -import Gitlab.Client.MTL (BaseUrl (..)) -import Gitlab.Lib (Id) -import Network.URI (parseURI) -import Relude -import System.Directory (doesFileExist, getCurrentDirectory, getHomeDirectory) - -instance FromJSON (PartialConfig Maybe) where - parseJSON = withObject "PartialConfig" $ \o -> - o .: "config" >>= \c -> do - pGroupId <- c .:? "groupId" - pBaseUrl <- c .:? "baseUrl" - pApiToken <- c .:? "apiToken" - pUserAgent <- c .:? "userAgent" - pProjectsExcludeList <- c .:? "excludeProjects" - let pCommand = Nothing - pure $ PartialConfig {..} - -deriving via (Autodocodec (Id a)) instance FromJSON (Id a) - -instance FromJSON (PartialConfig (Compose Maybe S.First)) where - parseJSON = fmap (bmap (Compose . fmap S.First)) . (parseJSON @(PartialConfig Maybe)) - -instance FromJSON BaseUrl where - parseJSON = withText "URI" $ \v -> - maybe - (fail "Bad URI") - (pure . BaseUrl) - (parseURI (toString v)) - -readPartialOptionsFromHomeDir :: (Monoid a, FromJSON a) => IO a -readPartialOptionsFromHomeDir = getHomeDirectory >>= readPartialOptionsFromDir - -readPartialOptionsFromLocalDir :: (Monoid a, FromJSON a) => IO a -readPartialOptionsFromLocalDir = getCurrentDirectory >>= readPartialOptionsFromDir - -readPartialOptionsFromDir :: (Monoid a, FromJSON a) => FilePath -> IO a -readPartialOptionsFromDir dir = do - let configFilePath = dir <> "/.gitlab-helper.yml" - ifM - (doesFileExist configFilePath) - (fromRight mempty <$> decodeFileEither configFilePath) - (pure mempty) diff --git a/src/Config/Optparse.hs b/src/Config/Optparse.hs deleted file mode 100644 index 2cfb9c5..0000000 --- a/src/Config/Optparse.hs +++ /dev/null @@ -1,124 +0,0 @@ -module Config.Optparse (parseConfigFromOptions, parserPrefs, parserInfo) where - -import Barbies (bsequence, bzipWith) -import Config.Types -import Data.List.Split -import qualified Data.Semigroup as S (First (..)) -import Gitlab.Client.MTL (ApiToken (..), BaseUrl (..), UserAgent (..)) -import Gitlab.Lib (Id (..)) -import Network.URI (URI, parseAbsoluteURI) -import Options.Applicative -import Relude - -parseConfigFromOptions :: IO (PartialConfig (Compose Maybe S.First)) -parseConfigFromOptions = customExecParser parserPrefs parserInfo - -parserPrefs :: ParserPrefs -parserPrefs = prefs showHelpOnEmpty - -parserInfo :: ParserInfo (PartialConfig (Compose Maybe S.First)) -parserInfo = - info - (helper <*> parser) - ( fullDesc - <> progDesc "gitlab-helper" - <> header "gitlab-helper - a collection of utilities for dealing with a load of projects in Gitlab" - <> footer "For the commands that are not read-only, use \"-x\" to make them actually do stuff" - ) - -newtype ParserModifier a = ParserModifier (Parser a -> Parser (Compose Maybe S.First a)) - -parser :: Parser (PartialConfig (Compose Maybe S.First)) -parser = bsequence $ bzipWith (\(ParserModifier x) -> Compose . x) partialFunctions partialParsers - where - partialParsers :: PartialConfig Parser - partialParsers = - PartialConfig - (option (Id <$> auto) (long "group-id" <> help "set the ID of the group to look at" <> metavar "ID")) - (option (BaseUrl <$> eitherReader f) (long "base-url" <> help "Base URL of the Gitlab instance (e.g. `https://gitlab.com/`)" <> metavar "URL")) - (option (ApiToken <$> str) (long "api-token" <> help "API Token to use for authorizing requests against the Gitlab API. `api` scope is required." <> metavar "TOKEN")) - (option (UserAgent <$> str) (long "user-agent" <> help "User-Agent to use for requests against the Gitlab API." <> metavar "USER_AGENT")) - (option (fmap Id <$> eitherReader g) (long "exclude-projects" <> help "set the list of projects to exclude as a comma-separated list of IDs" <> metavar "ID1,ID2,ID3")) - commandParser - partialFunctions :: PartialConfig ParserModifier - partialFunctions = - PartialConfig - (ParserModifier optionalParser) - (ParserModifier optionalParser) - (ParserModifier optionalParser) - (ParserModifier optionalParser) - (ParserModifier optionalParser) - (ParserModifier $ fmap (Compose . Just . S.First)) - optionalParser :: Parser a -> Parser (Compose Maybe S.First a) - optionalParser = fmap Compose . optional . fmap S.First - f :: String -> Either String URI - f s = maybeToRight ("\"" <> s <> "\" is not a valid absolute URI") (parseAbsoluteURI s) - g :: String -> Either String [Int] - g "" = Right [] - g s = traverse (\s' -> maybeToRight ("\"" <> s' <> "\" is not a valid Project ID") $ readMaybe s') $ splitOn "," s - -commandParser :: Parser Command -commandParser = - hsubparser - $ mconcat - [ command "version" (info (pure Version) (progDesc "print program version")), - command "show-branches" (info (pure ShowBranches) (progDesc "show branches")), - command "show-projects" (info (pure ShowProjects) (progDesc "show projects")), - command "list-projects-meta" (info (ListProjectsMeta <$> metaScopeParser) (progDesc "list the projects in (almost) meta compatible JSON format")), - command "enable-source-branch-deletion" (info (EnableSourceBranchDeletionAfterMerge <$> executionParser) (progDesc "enable source branch deletion after merge for all projects")), - command "enable-all-discussions-must-be-resolved-for-merge-requirement" (info (EnableAllDiscussionsMustBeResolvedForMergeRequirement <$> executionParser) (progDesc "enable the requirement that all discussions must be resolved for an MR to be merged for all projects")), - command "enable-successful-pipeline-for-merge-requirement" (info (EnableSuccessfulPipelineForMergeRequirement <$> executionParser) (progDesc "enable the requirement that there must be a successful pipeline for an MR to be merged for all projects. CAUTION: Use with care, might not do what you want in projects without pipelines")), - command "show-schedules" (info (pure ShowSchedules) (progDesc "show schedules")), - command "show-merge-requests" (info (ShowMergeRequests <$> recheckMergeStatusParser) (progDesc "show projects with and without enabled merge requests, list merge requests")), - command "count-deployments" (info (CountSuccessfulDeployments <$> argument (Year <$> auto) (metavar "YEAR") <*> withArchivedProjectsParser) (progDesc "count the number of successful deployments per project (a successful push pipeline on the default branch is counted as a deployment)")), - command "set-merge-method-to-fast-forward" (info (SetMergeMethodToFastForward <$> executionParser) (progDesc "Set the merge method for all projects to \"Fast Forward\"")), - command "update-merge-requests" (info mergeRequestUpdateCommandParser (progDesc "Update all MRs from a given user that match a given condition with a given command")) - ] - -mergeRequestUpdateCommandParser :: Parser Command -mergeRequestUpdateCommandParser = - UpdateMergeRequests - <$> mergeRequestUpdateActionParser - <*> optional - (option (AuthorIs <$> auto) (short 'u' <> long "user-id" <> help "only MRs opened by the user with this ID are taken into account" <> metavar "ID")) - <*> optional - ( (Left . SearchTerm <$> strOption (short 's' <> long "search" <> help "Optional. a string that must appear in the MR description or title. Mutually exclusive with --search-title" <> metavar "TXT")) - <|> Right - . SearchTermTitle - <$> strOption (long "search-title" <> help "Optional. a string that must appear in the MR title. Mutually exclusive with --search" <> metavar "TXT") - ) - <*> recheckMergeStatusParser - <*> executionParser - where - mergeRequestUpdateActionParser :: Parser MergeRequestUpdateAction - mergeRequestUpdateActionParser = - hsubparser - $ mconcat - [ command "rebase" (info (pure Rebase) (progDesc "rebase the merge requests")), - command "merge" (info (Merge <$> mergeCiOptionParser) (progDesc "merge the merge requests")), - command "draft" (info (pure SetToDraft) (progDesc "set the merge requests to `draft`")), - command "ready" (info (pure MarkAsReady) (progDesc "mark the merge requests as ready")), - command "list" (info (pure List) (progDesc "list the merge requests")) - ] - - mergeCiOptionParser :: Parser MergeCiOption - mergeCiOptionParser = - flag PipelineMustSucceed SkipCi (long "skip-ci" <> help "don't enforce that a merge request requires a successful pipeline to be merged (also helpful for projects that don't have pipelines on non-default branches)") - -metaScopeParser :: Parser MetaScope -metaScopeParser = argument (eitherReader scopeParser) (metavar "SCOPE" <> showDefault <> value MetaScopeGroup <> help "Scope for the list of projects. Possible values are \"group\" (which is the default and includes all the projects for the current group), or \"all\" (which includes all the projects that are visible with the provided API Token)") - where - scopeParser :: String -> Either String MetaScope - scopeParser "all" = Right MetaScopeAll - scopeParser "group" = Right MetaScopeGroup - scopeParser s = Left $ "\"" <> s <> "\" is not a valid scope" - -executionParser :: Parser Execution -executionParser = - (\b -> if b then Execute else DryRun) <$> switch (long "execute" <> short 'x' <> help "whether to actually change the world via the API. By default, only a dry run will be performed") - -withArchivedProjectsParser :: Parser WithArchivedProjects -withArchivedProjectsParser = flag SkipArchivedProjects IncludeArchivedProjects (long "include-archived" <> help "Include archived projects") - -recheckMergeStatusParser :: Parser MergeStatusRecheck -recheckMergeStatusParser = flag NoRecheckMergeStatus RecheckMergeStatus (long "recheck-merge-status" <> help "Trigger a recheck of the merge status of the merge requests. This is done on the Gitlab server and might have a performance impact so it's not done by default") diff --git a/src/Config/Types.hs b/src/Config/Types.hs deleted file mode 100644 index a1c4a27..0000000 --- a/src/Config/Types.hs +++ /dev/null @@ -1,95 +0,0 @@ -{-# LANGUAGE DeriveAnyClass #-} -{-# LANGUAGE DeriveGeneric #-} -{-# LANGUAGE DerivingVia #-} -{-# LANGUAGE RecordWildCards #-} - -module Config.Types - ( Config (..), - PartialConfig (..), - Command (..), - MergeRequestUpdateAction (..), - MetaScope (..), - MergeCiOption (..), - AuthorIs (..), - SearchTerm (..), - SearchTermTitle (..), - Year (..), - WithArchivedProjects (..), - MergeStatusRecheck (..), - Execution (..), - partialConfigToConfig, - ) -where - -import Barbies -import Gitlab.Client.MTL (ApiToken, BaseUrl, UserAgent) -import Gitlab.Group (Group) -import Gitlab.Lib (Id (..)) -import Gitlab.Project (Project) -import Options.Applicative -import Relude hiding (Reader) - -data Config = Config - { groupId :: Id Group, - baseUrl :: BaseUrl, - apiToken :: ApiToken, - userAgent :: UserAgent, - projectsExcludeList :: [Id Project], - cmd :: Command - } - deriving stock (Show) - -data PartialConfig f = PartialConfig - { pGroupId :: f (Id Group), - pBaseUrl :: f BaseUrl, - pApiToken :: f ApiToken, - pUserAgent :: f UserAgent, - pProjectsExcludeList :: f [Id Project], - pCommand :: f Command - } - deriving stock (Generic) - deriving anyclass (FunctorB, TraversableB, ApplicativeB, ConstraintsB) - -instance (Alternative f) => Semigroup (PartialConfig f) where - (<>) = bzipWith (<|>) - -instance (Alternative f) => Monoid (PartialConfig f) where - mempty = bpure empty - -data Command - = Version - | ShowBranches - | EnableSourceBranchDeletionAfterMerge Execution - | ShowProjects - | ListProjectsMeta MetaScope - | ShowSchedules - | ShowMergeRequests MergeStatusRecheck - | EnableAllDiscussionsMustBeResolvedForMergeRequirement Execution - | EnableSuccessfulPipelineForMergeRequirement Execution - | SetMergeMethodToFastForward Execution - | CountSuccessfulDeployments Year WithArchivedProjects - | UpdateMergeRequests MergeRequestUpdateAction (Maybe AuthorIs) (Maybe (Either SearchTerm SearchTermTitle)) MergeStatusRecheck Execution - deriving stock (Show) - -data Execution = DryRun | Execute deriving stock (Eq, Show) - -data MetaScope = MetaScopeGroup | MetaScopeAll deriving stock (Eq, Show) - -data MergeRequestUpdateAction = List | Rebase | Merge MergeCiOption | SetToDraft | MarkAsReady deriving stock (Show) - -data MergeCiOption = PipelineMustSucceed | SkipCi deriving stock (Show) - -newtype AuthorIs = AuthorIs Int deriving stock (Show) - -newtype SearchTerm = SearchTerm String deriving stock (Show) - -newtype SearchTermTitle = SearchTermTitle String deriving stock (Show) - -newtype Year = Year Int deriving stock (Show) - -data WithArchivedProjects = IncludeArchivedProjects | SkipArchivedProjects deriving stock (Show) - -data MergeStatusRecheck = RecheckMergeStatus | NoRecheckMergeStatus deriving stock (Show) - -partialConfigToConfig :: PartialConfig Identity -> Config -partialConfigToConfig (PartialConfig (Identity groupId) (Identity baseUrl) (Identity apiToken) (Identity userAgent) (Identity projectsExcludeList) (Identity cmd)) = Config {..} diff --git a/src/Effects.hs b/src/Effects.hs index ead107a..a62dccb 100644 --- a/src/Effects.hs +++ b/src/Effects.hs @@ -57,7 +57,7 @@ where import App import Autodocodec import Burrito -import Config.Types (AuthorIs (..), Config (..), MergeCiOption (..), MergeStatusRecheck (..), SearchTerm (..), WithArchivedProjects (..), Year (..)) +import Config (AuthorIs (..), Config (..), MergeCiOption (..), MergeStatusRecheck (..), SearchTerm (..), WithArchivedProjects (..), Year (..)) import Data.Aeson (FromJSON, ToJSON) import Data.Aeson.Types (Object) import Data.Scientific diff --git a/src/MergeRequests.hs b/src/MergeRequests.hs index b968943..995cd6f 100644 --- a/src/MergeRequests.hs +++ b/src/MergeRequests.hs @@ -17,7 +17,7 @@ module MergeRequests where import App (App) -import Config.Types (MergeStatusRecheck, WithArchivedProjects (SkipArchivedProjects)) +import Config (MergeStatusRecheck, WithArchivedProjects (SkipArchivedProjects)) import Data.List (partition) import qualified Data.Text as T (intercalate) import Data.Time hiding (getCurrentTime) diff --git a/src/Program.hs b/src/Program.hs index 3a2ab32..ec56511 100644 --- a/src/Program.hs +++ b/src/Program.hs @@ -1,16 +1,13 @@ -{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} -{-# LANGUAGE TemplateHaskell #-} module Program (run) where import App import Branches (showBranchesForGroup) -import Config.Config (parseConfigOrDie) -import Config.Types -import Effects (write) -import GitHash +import Config import MergeRequests +import OptEnvConf +import Paths_gitlab_helper (version) import Projects import Relude import Schedules (showSchedulesForGroup) @@ -18,11 +15,9 @@ import UpdateMergeRequests (updateMergeRequests) run :: IO () run = do - c@Config {..} <- parseConfigOrDie - let gitCommit = "Version: " <> fromString (giTag $$tGitInfoCwd) + c@Config {..} <- runSettingsParser version "Some utilities for working with GitLab to make your life easier." -- putStrLn $ "running with config: " <> show c let program = case cmd of - Version -> write gitCommit ShowBranches -> showBranchesForGroup (EnableSourceBranchDeletionAfterMerge execution) -> enableSourceBranchDeletionAfterMerge execution ShowProjects -> showProjectsForGroup diff --git a/src/Projects.hs b/src/Projects.hs index 3082eba..4b1835d 100644 --- a/src/Projects.hs +++ b/src/Projects.hs @@ -21,7 +21,7 @@ where import App (App) import Colonnade (asciiCapped, cap, headed) import Colourista (bold, formatWith, red) -import Config.Types +import Config import Data.Aeson (encode) import qualified Data.Map as M import Data.Text (toLower) diff --git a/src/Schedules.hs b/src/Schedules.hs index 3f73e33..12fddd2 100644 --- a/src/Schedules.hs +++ b/src/Schedules.hs @@ -17,7 +17,7 @@ where import App (App) import Colourista.Pure -import Config.Types (Config (..), WithArchivedProjects (SkipArchivedProjects)) +import Config (Config (..), WithArchivedProjects (SkipArchivedProjects)) import Effects import Gitlab.Client.MTL (UpdateError) import Gitlab.Lib (Name (..)) diff --git a/src/UpdateMergeRequests.hs b/src/UpdateMergeRequests.hs index 4780e72..083ea58 100644 --- a/src/UpdateMergeRequests.hs +++ b/src/UpdateMergeRequests.hs @@ -7,7 +7,7 @@ module UpdateMergeRequests where import App (App) -import Config.Types +import Config import Data.Text (isInfixOf, strip, stripPrefix, toLower) import Effects import Gitlab.Lib (Id) diff --git a/stack.yaml b/stack.yaml index e91e75d..e9479f3 100644 --- a/stack.yaml +++ b/stack.yaml @@ -10,6 +10,24 @@ extra-deps: - gitlab-api-http-client - gitlab-api-http-client-mtl - gitlab-api-types + - github: NorfairKing/opt-env-conf + commit: 765d221a8cce565bf35e6b6707d371f1e53347b2 + subdirs: + - opt-env-conf + - opt-env-conf-test + - github: NorfairKing/safe-coloured-text + commit: 67d285394231ab6ec0c6dce5551f546edf8719fc + subdirs: + - safe-coloured-text + - safe-coloured-text-layout + - safe-coloured-text-gen + - github: NorfairKing/autodocodec + commit: 3dbe8d515c9fc0fe6313dfe92e9db1f4af4c50a3 + subdirs: + - autodocodec + - autodocodec-yaml + - autodocodec-nix + - autodocodec-schema system-ghc: true diff --git a/stack.yaml.lock b/stack.yaml.lock index b1d8963..ffec678 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -43,6 +43,123 @@ packages: original: subdir: gitlab-api-types url: https://github.com/L7R7/gitlab-api/archive/44c8a21ebe7607cfcf1bd74c2610e47f5df783fd.tar.gz +- completed: + name: opt-env-conf + pantry-tree: + sha256: e64053928fc8ac59bea91dc9834f79d2c931fa4bd539eb8c37e2e5b53f643d10 + size: 1368 + sha256: 4696c206495f595f976106ef38e158ddcaedc824e029a1e0bc7367823d204d17 + size: 105400 + subdir: opt-env-conf + url: https://github.com/NorfairKing/opt-env-conf/archive/765d221a8cce565bf35e6b6707d371f1e53347b2.tar.gz + version: 0.9.0.0 + original: + subdir: opt-env-conf + url: https://github.com/NorfairKing/opt-env-conf/archive/765d221a8cce565bf35e6b6707d371f1e53347b2.tar.gz +- completed: + name: opt-env-conf-test + pantry-tree: + sha256: 0fca0be42239dede39ea9268871f53755f820ccd1b45ce0fd85f55f14c951444 + size: 28172 + sha256: 4696c206495f595f976106ef38e158ddcaedc824e029a1e0bc7367823d204d17 + size: 105400 + subdir: opt-env-conf-test + url: https://github.com/NorfairKing/opt-env-conf/archive/765d221a8cce565bf35e6b6707d371f1e53347b2.tar.gz + version: 0.0.0.2 + original: + subdir: opt-env-conf-test + url: https://github.com/NorfairKing/opt-env-conf/archive/765d221a8cce565bf35e6b6707d371f1e53347b2.tar.gz +- completed: + name: safe-coloured-text + pantry-tree: + sha256: d7bbf3dbbc8b69a423d3d3d8c111dc4efd343b4bdcf2962266695819a3cdd4a9 + size: 530 + sha256: d709c7bf6f62e947248af470f3a22310a45726fb0bf6624f66a0bf02c1bcaca0 + size: 39203 + subdir: safe-coloured-text + url: https://github.com/NorfairKing/safe-coloured-text/archive/67d285394231ab6ec0c6dce5551f546edf8719fc.tar.gz + version: 0.3.0.2 + original: + subdir: safe-coloured-text + url: https://github.com/NorfairKing/safe-coloured-text/archive/67d285394231ab6ec0c6dce5551f546edf8719fc.tar.gz +- completed: + name: safe-coloured-text-layout + pantry-tree: + sha256: d5b9b48716bb7ca96eae52d91aa64265eeb4aa0659d0838ffbb353947972465e + size: 344 + sha256: d709c7bf6f62e947248af470f3a22310a45726fb0bf6624f66a0bf02c1bcaca0 + size: 39203 + subdir: safe-coloured-text-layout + url: https://github.com/NorfairKing/safe-coloured-text/archive/67d285394231ab6ec0c6dce5551f546edf8719fc.tar.gz + version: 0.2.0.1 + original: + subdir: safe-coloured-text-layout + url: https://github.com/NorfairKing/safe-coloured-text/archive/67d285394231ab6ec0c6dce5551f546edf8719fc.tar.gz +- completed: + name: safe-coloured-text-gen + pantry-tree: + sha256: 54781907c6bc14e5eda07b4b0f8a7037c982cbc65fb33b6adcba02fdf413e7d4 + size: 8960 + sha256: d709c7bf6f62e947248af470f3a22310a45726fb0bf6624f66a0bf02c1bcaca0 + size: 39203 + subdir: safe-coloured-text-gen + url: https://github.com/NorfairKing/safe-coloured-text/archive/67d285394231ab6ec0c6dce5551f546edf8719fc.tar.gz + version: 0.0.0.3 + original: + subdir: safe-coloured-text-gen + url: https://github.com/NorfairKing/safe-coloured-text/archive/67d285394231ab6ec0c6dce5551f546edf8719fc.tar.gz +- completed: + name: autodocodec + pantry-tree: + sha256: ffa0a58cfe46ff16b833bddb814837e86e6b1e409a718d48c9a496d72abac39d + size: 914 + sha256: 33cb89b1f1ccf69c33c124cd6a6546db177e4cdddb9d8d818e7dae005788d35f + size: 109528 + subdir: autodocodec + url: https://github.com/NorfairKing/autodocodec/archive/3dbe8d515c9fc0fe6313dfe92e9db1f4af4c50a3.tar.gz + version: 0.5.0.0 + original: + subdir: autodocodec + url: https://github.com/NorfairKing/autodocodec/archive/3dbe8d515c9fc0fe6313dfe92e9db1f4af4c50a3.tar.gz +- completed: + name: autodocodec-yaml + pantry-tree: + sha256: af90e7449671702d46cb4cb5633e58bdebff9ae957be6f9a23fd2905fe0d5fea + size: 592 + sha256: 33cb89b1f1ccf69c33c124cd6a6546db177e4cdddb9d8d818e7dae005788d35f + size: 109528 + subdir: autodocodec-yaml + url: https://github.com/NorfairKing/autodocodec/archive/3dbe8d515c9fc0fe6313dfe92e9db1f4af4c50a3.tar.gz + version: 0.4.0.2 + original: + subdir: autodocodec-yaml + url: https://github.com/NorfairKing/autodocodec/archive/3dbe8d515c9fc0fe6313dfe92e9db1f4af4c50a3.tar.gz +- completed: + name: autodocodec-nix + pantry-tree: + sha256: 06749946c1e3396c0a55034b3543e8dd63ae1c78a852b1820a7e4c2bcb3c1e59 + size: 595 + sha256: 33cb89b1f1ccf69c33c124cd6a6546db177e4cdddb9d8d818e7dae005788d35f + size: 109528 + subdir: autodocodec-nix + url: https://github.com/NorfairKing/autodocodec/archive/3dbe8d515c9fc0fe6313dfe92e9db1f4af4c50a3.tar.gz + version: 0.1.0.0 + original: + subdir: autodocodec-nix + url: https://github.com/NorfairKing/autodocodec/archive/3dbe8d515c9fc0fe6313dfe92e9db1f4af4c50a3.tar.gz +- completed: + name: autodocodec-schema + pantry-tree: + sha256: 15a412b470f3dfc446ddf067da7e537acb337e3aadd22a588389ba1ec28e8cea + size: 387 + sha256: 33cb89b1f1ccf69c33c124cd6a6546db177e4cdddb9d8d818e7dae005788d35f + size: 109528 + subdir: autodocodec-schema + url: https://github.com/NorfairKing/autodocodec/archive/3dbe8d515c9fc0fe6313dfe92e9db1f4af4c50a3.tar.gz + version: 0.2.0.1 + original: + subdir: autodocodec-schema + url: https://github.com/NorfairKing/autodocodec/archive/3dbe8d515c9fc0fe6313dfe92e9db1f4af4c50a3.tar.gz snapshots: - completed: sha256: 400dfb2174640dd2f9f2ba7cbf9148699f2380ab64faa46f4be298a5d6205316 diff --git a/test/HelpTextSpec.hs b/test/HelpTextSpec.hs index a9da414..dbcef7c 100644 --- a/test/HelpTextSpec.hs +++ b/test/HelpTextSpec.hs @@ -1,23 +1,13 @@ -{-# OPTIONS_GHC -Wno-deprecations #-} +{-# LANGUAGE TypeApplications #-} module HelpTextSpec (spec) where -import Config.Optparse -import Options.Applicative -import Relude +import Config +import OptEnvConf.Test +-- import Relude import Test.Syd spec :: Spec spec = do - it "provides a help text for the main program" $ parserGoldenTest ["--help"] "test_resources/help-text.txt" - it "provides a help text for working with MRs" $ parserGoldenTest ["update-merge-requests", "--help"] "test_resources/help-text-mrs.txt" - it "returns a useful output when no command is provided" $ parserGoldenTest [] "test_resources/help-text.txt" - -parserGoldenTest :: [String] -> FilePath -> GoldenTest String -parserGoldenTest args fp = do - let res = execParserPure parserPrefs parserInfo args - case res of - Failure (ParserFailure f) -> do - let (parserHelp, _, _) = f "gitlab-helper" - goldenShowInstance fp parserHelp - _ -> undefined + settingsLintSpec @Config + goldenSettingsReferenceDocumentationSpec @Config "test_resources/reference.txt" "gitlab-helper" diff --git a/test_resources/help-text-mrs.txt b/test_resources/help-text-mrs.txt deleted file mode 100644 index ce917d9..0000000 --- a/test_resources/help-text-mrs.txt +++ /dev/null @@ -1,28 +0,0 @@ -Usage: gitlab-helper update-merge-requests - COMMAND [-u|--user-id ID] [(-s|--search TXT) | --search-title TXT] - [--recheck-merge-status] [-x|--execute] - - Update all MRs from a given user that match a given condition with a given - command - -Available options: - -u,--user-id ID only MRs opened by the user with this ID are taken - into account - -s,--search TXT Optional. a string that must appear in the MR - description or title. Mutually exclusive with - --search-title - --search-title TXT Optional. a string that must appear in the MR title. - Mutually exclusive with --search - --recheck-merge-status Trigger a recheck of the merge status of the merge - requests. This is done on the Gitlab server and might - have a performance impact so it's not done by default - -x,--execute whether to actually change the world via the API. By - default, only a dry run will be performed - -h,--help Show this help text - -Available commands: - rebase rebase the merge requests - merge merge the merge requests - draft set the merge requests to `draft` - ready mark the merge requests as ready - list list the merge requests \ No newline at end of file diff --git a/test_resources/help-text.txt b/test_resources/help-text.txt index a53f991..e69de29 100644 --- a/test_resources/help-text.txt +++ b/test_resources/help-text.txt @@ -1,52 +0,0 @@ -gitlab-helper - a collection of utilities for dealing with a load of projects in -Gitlab - -Usage: gitlab-helper [--group-id ID] [--base-url URL] [--api-token TOKEN] - [--user-agent USER_AGENT] [--exclude-projects ID1,ID2,ID3] - COMMAND - - gitlab-helper - -Available options: - -h,--help Show this help text - --group-id ID set the ID of the group to look at - --base-url URL Base URL of the Gitlab instance (e.g. - `https://gitlab.com/`) - --api-token TOKEN API Token to use for authorizing requests against the - Gitlab API. `api` scope is required. - --user-agent USER_AGENT User-Agent to use for requests against the Gitlab - API. - --exclude-projects ID1,ID2,ID3 - set the list of projects to exclude as a - comma-separated list of IDs - -Available commands: - version print program version - show-branches show branches - show-projects show projects - list-projects-meta list the projects in (almost) meta compatible JSON - format - enable-source-branch-deletion - enable source branch deletion after merge for all - projects - enable-all-discussions-must-be-resolved-for-merge-requirement - enable the requirement that all discussions must be - resolved for an MR to be merged for all projects - enable-successful-pipeline-for-merge-requirement - enable the requirement that there must be a - successful pipeline for an MR to be merged for all - projects. CAUTION: Use with care, might not do what - you want in projects without pipelines - show-schedules show schedules - show-merge-requests show projects with and without enabled merge - requests, list merge requests - count-deployments count the number of successful deployments per - project (a successful push pipeline on the default - branch is counted as a deployment) - set-merge-method-to-fast-forward - Set the merge method for all projects to "Fast - Forward" - update-merge-requests Update all MRs from a given user that match a given - condition with a given command - -For the commands that are not read-only, use "-x" to make them actually do stuff \ No newline at end of file diff --git a/test_resources/reference.txt b/test_resources/reference.txt new file mode 100644 index 0000000..3d8a4e6 --- /dev/null +++ b/test_resources/reference.txt @@ -0,0 +1,224 @@ +Usage: gitlab-helper [--config-file FILE_PATH] --group-id GROUP_ID --base-url URL --api-token TOKEN [--user-agent USER_AGENT] [--exclude-projects ID1,ID2,ID3] COMMAND + +All settings: + Show this help text + switch: -h|--help + + Output version information + switch: --version + + Path to the configuration file + option: --config-file FILE_PATH + env: CONFIG_FILE FILE_PATH + + ID of the GitLab group to work with + option: --group-id GROUP_ID + env: GLH_GROUP_ID GROUP_ID + config: + config.groupId: # or null +  # 64 bit signed integer + + Base URL of your GitLab instance (e.g. `https://my.gitlab.com`) + option: --base-url URL + env: GLH_BASE_URL URL + config: + config.baseUrl: # or null +  + + API Token to use for authorizing requests against the Gitlab API. `api` scope is required. + option: --api-token TOKEN + env: GLH_API_TOKEN TOKEN + config: + config.apiToken: # or null +  + + User agent to use for requests against the Gitlab API + option: --user-agent USER_AGENT + env: GLH_USER_AGENT USER_AGENT + config: + config.userAgent: # or null +  + default: "gitlab-helper" + + set the list of projects to exclude as a comma-separated list of IDs + option: --exclude-projects ID1,ID2,ID3 + env: GLH_EXCLUDE_PROJECTS ID1,ID2,ID3 + config: + config.excludeProjects: # or null + -  # 64 bit signed integer + default: [] + +All commands: + Show branches + command: show-branches + + Show projects + command: show-projects + + List projects in (almost) meta compatible JSON format + command: list-projects-meta + list projects in the group that is configured in the config file + command: group + + list all projects in all groups that are visible with the provided API token + command: all + + + Enable source branch deletion after merge for all projects in the group + command: enable-source-branch-deletion + whether to actually change the world via the API. By default, only a dry run will be performed + switch: --execute|-x + default: DryRun + + + Enable the requirement that all discussions must be resolved for an MR to be merged for all projects + command: enable-all-discussions-must-be-resolved-for-merge-requirement + whether to actually change the world via the API. By default, only a dry run will be performed + switch: --execute|-x + default: DryRun + + + Enable the requirement that there must be a successful pipeline for an MR to be merged for all projects. CAUTION: Use with care, might not do what you want in projects without pipelines + command: enable-successful-pipeline-for-merge-requirement + whether to actually change the world via the API. By default, only a dry run will be performed + switch: --execute|-x + default: DryRun + + + Show Pipeline Schedules + command: show-schedules + + Show projects with and without enabled merge requests, list open merge requests + command: show-merge-requests + Trigger a recheck of the merge status of the merge requests. This is done on the Gitlab server and might have a performance impact so it's not done by default + switch: --[no-]recheck-merge-status + + + Count the number of successful deployments per project (a successful push pipeline on the default branch is counted as a deployment) + command: count-deployments + Set the year + option: --year YEAR + + Include or skip archived projects + switch: --[no-]include-archived-projects + + + Set the merge method for all projects to "Fast Forward" + command: set-merge-method-to-fast-forward + whether to actually change the world via the API. By default, only a dry run will be performed + switch: --execute|-x + default: DryRun + + + Update all MRs from a given user that match a given condition with a given command + command: update-merge-requests + rebase the merge requests + command: rebase + + merge the merge requests + command: merge + don't enforce that a merge request requires a successful pipeline to be merged (also helpful for projects that don't have pipelines on non-default branches) + switch: --skip-ci + default: PipelineMustSucceed + + + set the merge requests to `draft` + command: draft + + mark the merge requests as ready + command: ready + + list the merge requests + command: list + + only MRs opened by the user with this ID are taken into account + option: -u|--user-id USER_ID + + A string that must appear in the MR title. + option: --search-title TXT + + A string that must appear in the MR description or title. + option: -s|--search TXT + + Trigger a recheck of the merge status of the merge requests. This is done on the Gitlab server and might have a performance impact so it's not done by default + switch: --[no-]recheck-merge-status + + whether to actually change the world via the API. By default, only a dry run will be performed + switch: --execute|-x + default: DryRun + + + +Options: + -h|--help Show this help text + --version Output version information + --config-file Path to the configuration file + --group-id ID of the GitLab group to work with + --base-url Base URL of your GitLab instance (e.g. `https://my.gitlab.com`) + --api-token API Token to use for authorizing requests against the Gitlab API. `api` scope is required. + --user-agent User agent to use for requests against the Gitlab API default: "gitlab-helper" + --exclude-projects set the list of projects to exclude as a comma-separated list of IDs default: [] + show-branches Show branches + show-projects Show projects + list-projects-meta List projects in (almost) meta compatible JSON format + group list projects in the group that is configured in the config file + all list all projects in all groups that are visible with the provided API token + enable-source-branch-deletion Enable source branch deletion after merge for all projects in the group + --execute|-x whether to actually change the world via the API. By default, only a dry run will be performed default: DryRun + enable-all-discussions-must-be-resolved-for-merge-requirement Enable the requirement that all discussions must be resolved for an MR to be merged for all projects + --execute|-x whether to actually change the world via the API. By default, only a dry run will be performed default: DryRun + enable-successful-pipeline-for-merge-requirement Enable the requirement that there must be a successful pipeline for an MR to be merged for all projects. CAUTION: Use with care, might not do what you want in projects without pipelines + --execute|-x whether to actually change the world via the API. By default, only a dry run will be performed default: DryRun + show-schedules Show Pipeline Schedules + show-merge-requests Show projects with and without enabled merge requests, list open merge requests + --[no-]recheck-merge-status Trigger a recheck of the merge status of the merge requests. This is done on the Gitlab server and might have a performance impact so it's not done by default + count-deployments Count the number of successful deployments per project (a successful push pipeline on the default branch is counted as a deployment) + --year Set the year + --[no-]include-archived-projects Include or skip archived projects + set-merge-method-to-fast-forward Set the merge method for all projects to "Fast Forward" + --execute|-x whether to actually change the world via the API. By default, only a dry run will be performed default: DryRun + update-merge-requests Update all MRs from a given user that match a given condition with a given command + rebase rebase the merge requests + merge merge the merge requests + --skip-ci don't enforce that a merge request requires a successful pipeline to be merged (also helpful for projects that don't have pipelines on non-default branches) default: PipelineMustSucceed + draft set the merge requests to `draft` + ready mark the merge requests as ready + list list the merge requests + -u|--user-id only MRs opened by the user with this ID are taken into account + --search-title A string that must appear in the MR title. + -s|--search A string that must appear in the MR description or title. + --[no-]recheck-merge-status Trigger a recheck of the merge status of the merge requests. This is done on the Gitlab server and might have a performance impact so it's not done by default + --execute|-x whether to actually change the world via the API. By default, only a dry run will be performed default: DryRun + +Environment Variables: + CONFIG_FILE FILE_PATH Path to the configuration file + GLH_GROUP_ID GROUP_ID ID of the GitLab group to work with + GLH_BASE_URL URL Base URL of your GitLab instance (e.g. `https://my.gitlab.com`) + GLH_API_TOKEN TOKEN API Token to use for authorizing requests against the Gitlab API. `api` scope is required. + GLH_USER_AGENT USER_AGENT User agent to use for requests against the Gitlab API default: "gitlab-helper" + GLH_EXCLUDE_PROJECTS ID1,ID2,ID3 set the list of projects to exclude as a comma-separated list of IDs default: [] + +Configuration Values: + ID of the GitLab group to work with + config.groupId: + # or null +  # 64 bit signed integer + Base URL of your GitLab instance (e.g. `https://my.gitlab.com`) + config.baseUrl: + # or null +  + API Token to use for authorizing requests against the Gitlab API. `api` scope is required. + config.apiToken: + # or null +  + User agent to use for requests against the Gitlab API + default: "gitlab-helper" + config.userAgent: + # or null +  + set the list of projects to exclude as a comma-separated list of IDs + default: [] + config.excludeProjects: + # or null + -  # 64 bit signed integer + diff --git a/weeder.toml b/weeder.toml index 4b4a0c8..43916d9 100644 --- a/weeder.toml +++ b/weeder.toml @@ -1,2 +1,4 @@ -roots = [ "^Main.main$", "^Program.run$", "^Paths_gitlab_helper.*", "Spec.spec$" ] -type-class-roots = true \ No newline at end of file +roots = [ "^Main.main", "^Program.run", "^Paths_" ] +root-instances = [ {class = 'IsString'}, {class = 'IsList'} ] +type-class-roots = true +unused-types = true