module Ecluse.CLI (
AppCommand (..),
commandParser,
execCLI,
) where
import Options.Applicative
import Ecluse.Composition.Types (MirrorRole (MirrorOnly, ServeAndMirror, ServeOnly))
import Ecluse.Core.BuildIdentity (productVersion)
import Ecluse.Dredger.Plan (
DredgerOptions (DredgerOptions),
SweepMode (SweepDeletes, SweepPreviews),
SweepRepetition (SweepContinuously, SweepOnce),
)
import Ecluse.Pilot (PilotCompileOptions (..))
data AppCommand
=
RunService MirrorRole
| RunPilot
| RunPilotCompile PilotCompileOptions
|
RunDredger DredgerOptions
| RunCheckConfig
deriving stock (AppCommand -> AppCommand -> Bool
(AppCommand -> AppCommand -> Bool)
-> (AppCommand -> AppCommand -> Bool) -> Eq AppCommand
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: AppCommand -> AppCommand -> Bool
== :: AppCommand -> AppCommand -> Bool
$c/= :: AppCommand -> AppCommand -> Bool
/= :: AppCommand -> AppCommand -> Bool
Eq, Int -> AppCommand -> ShowS
[AppCommand] -> ShowS
AppCommand -> FilePath
(Int -> AppCommand -> ShowS)
-> (AppCommand -> FilePath)
-> ([AppCommand] -> ShowS)
-> Show AppCommand
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AppCommand -> ShowS
showsPrec :: Int -> AppCommand -> ShowS
$cshow :: AppCommand -> FilePath
show :: AppCommand -> FilePath
$cshowList :: [AppCommand] -> ShowS
showList :: [AppCommand] -> ShowS
Show)
commandParser :: Parser AppCommand
commandParser :: Parser AppCommand
commandParser =
Mod CommandFields AppCommand -> Parser AppCommand
forall a. Mod CommandFields a -> Parser a
hsubparser
( FilePath -> ParserInfo AppCommand -> Mod CommandFields AppCommand
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"proxy" (Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info (MirrorRole -> AppCommand
RunService (MirrorRole -> AppCommand)
-> Parser MirrorRole -> Parser AppCommand
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser MirrorRole
proxyRoleParser) (FilePath -> InfoMod AppCommand
forall a. FilePath -> InfoMod a
progDesc FilePath
"Run the Écluse proxy server"))
Mod CommandFields AppCommand
-> Mod CommandFields AppCommand -> Mod CommandFields AppCommand
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo AppCommand -> Mod CommandFields AppCommand
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"mirror" (Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info (AppCommand -> Parser AppCommand
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (MirrorRole -> AppCommand
RunService MirrorRole
MirrorOnly)) (FilePath -> InfoMod AppCommand
forall a. FilePath -> InfoMod a
progDesc FilePath
"Run the Écluse mirror worker alone, for a worker fleet scaled on queue depth"))
Mod CommandFields AppCommand
-> Mod CommandFields AppCommand -> Mod CommandFields AppCommand
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo AppCommand -> Mod CommandFields AppCommand
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"pilot" (Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info Parser AppCommand
pilotCommandParser (FilePath -> InfoMod AppCommand
forall a. FilePath -> InfoMod a
progDesc FilePath
"Run the Écluse Pilot (OSV ingestion pipeline)"))
Mod CommandFields AppCommand
-> Mod CommandFields AppCommand -> Mod CommandFields AppCommand
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo AppCommand -> Mod CommandFields AppCommand
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"dredger" (Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info (DredgerOptions -> AppCommand
RunDredger (DredgerOptions -> AppCommand)
-> Parser DredgerOptions -> Parser AppCommand
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser DredgerOptions
dredgerOptionsParser) (FilePath -> InfoMod AppCommand
forall a. FilePath -> InfoMod a
progDesc FilePath
"Run the Écluse Dredger (mirror pruning worker)"))
Mod CommandFields AppCommand
-> Mod CommandFields AppCommand -> Mod CommandFields AppCommand
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo AppCommand -> Mod CommandFields AppCommand
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"check-config" (Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info (AppCommand -> Parser AppCommand
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure AppCommand
RunCheckConfig) (FilePath -> InfoMod AppCommand
forall a. FilePath -> InfoMod a
progDesc FilePath
"Validate the configuration and print the resolved posture, then exit (0 valid, 2 refused)"))
)
Parser AppCommand -> Parser AppCommand -> Parser AppCommand
forall a. Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> AppCommand -> Parser AppCommand
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (MirrorRole -> AppCommand
RunService MirrorRole
ServeAndMirror)
proxyRoleParser :: Parser MirrorRole
proxyRoleParser :: Parser MirrorRole
proxyRoleParser =
MirrorRole
-> MirrorRole -> Mod FlagFields MirrorRole -> Parser MirrorRole
forall a. a -> a -> Mod FlagFields a -> Parser a
flag
MirrorRole
ServeAndMirror
MirrorRole
ServeOnly
(FilePath -> Mod FlagFields MirrorRole
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"no-worker" Mod FlagFields MirrorRole
-> Mod FlagFields MirrorRole -> Mod FlagFields MirrorRole
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod FlagFields MirrorRole
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Serve without the embedded mirror worker; needs a durable ECLUSE_QUEUE__URL and an 'ecluse mirror' fleet to drain it")
dredgerOptionsParser :: Parser DredgerOptions
dredgerOptionsParser :: Parser DredgerOptions
dredgerOptionsParser =
SweepMode -> SweepRepetition -> DredgerOptions
DredgerOptions
(SweepMode -> SweepRepetition -> DredgerOptions)
-> Parser SweepMode -> Parser (SweepRepetition -> DredgerOptions)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SweepMode
-> SweepMode -> Mod FlagFields SweepMode -> Parser SweepMode
forall a. a -> a -> Mod FlagFields a -> Parser a
flag
SweepMode
SweepDeletes
SweepMode
SweepPreviews
(FilePath -> Mod FlagFields SweepMode
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"dry-run" Mod FlagFields SweepMode
-> Mod FlagFields SweepMode -> Mod FlagFields SweepMode
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod FlagFields SweepMode
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Report what a cycle would delete and delete nothing; it holds no delete, writes no walk marker, and needs no deletion consent")
Parser (SweepRepetition -> DredgerOptions)
-> Parser SweepRepetition -> Parser DredgerOptions
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> SweepRepetition
-> SweepRepetition
-> Mod FlagFields SweepRepetition
-> Parser SweepRepetition
forall a. a -> a -> Mod FlagFields a -> Parser a
flag
SweepRepetition
SweepContinuously
SweepRepetition
SweepOnce
(FilePath -> Mod FlagFields SweepRepetition
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"once" Mod FlagFields SweepRepetition
-> Mod FlagFields SweepRepetition -> Mod FlagFields SweepRepetition
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod FlagFields SweepRepetition
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Run one cycle, then exit: 0 when it completed, 1 when it halted")
pilotCommandParser :: Parser AppCommand
pilotCommandParser :: Parser AppCommand
pilotCommandParser =
Mod CommandFields AppCommand -> Parser AppCommand
forall a. Mod CommandFields a -> Parser a
hsubparser
( FilePath -> ParserInfo AppCommand -> Mod CommandFields AppCommand
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command
FilePath
"compile"
(Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info (PilotCompileOptions -> AppCommand
RunPilotCompile (PilotCompileOptions -> AppCommand)
-> Parser PilotCompileOptions -> Parser AppCommand
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser PilotCompileOptions
pilotCompileOptionsParser) (FilePath -> InfoMod AppCommand
forall a. FilePath -> InfoMod a
progDesc FilePath
"Compile one ecosystem's OSV export into a local osv.db artifact, then exit"))
)
Parser AppCommand -> Parser AppCommand -> Parser AppCommand
forall a. Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> AppCommand -> Parser AppCommand
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure AppCommand
RunPilot
pilotCompileOptionsParser :: Parser PilotCompileOptions
pilotCompileOptionsParser :: Parser PilotCompileOptions
pilotCompileOptionsParser =
Text -> FilePath -> Bool -> PilotCompileOptions
PilotCompileOptions
(Text -> FilePath -> Bool -> PilotCompileOptions)
-> Parser Text -> Parser (FilePath -> Bool -> PilotCompileOptions)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Mod OptionFields Text -> Parser Text
forall s. IsString s => Mod OptionFields s -> Parser s
strOption (FilePath -> Mod OptionFields Text
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"ecosystem" Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields Text
forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"ECOSYSTEM" Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> Text -> Mod OptionFields Text
forall (f :: * -> *) a. HasValue f => a -> Mod f a
value Text
"npm" Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> Mod OptionFields Text
forall a (f :: * -> *). Show a => Mod f a
showDefault Mod OptionFields Text
-> Mod OptionFields Text -> Mod OptionFields Text
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields Text
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Ecosystem whose OSV export to compile")
Parser (FilePath -> Bool -> PilotCompileOptions)
-> Parser FilePath -> Parser (Bool -> PilotCompileOptions)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Mod OptionFields FilePath -> Parser FilePath
forall s. IsString s => Mod OptionFields s -> Parser s
strOption (FilePath -> Mod OptionFields FilePath
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"out" Mod OptionFields FilePath
-> Mod OptionFields FilePath -> Mod OptionFields FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields FilePath
forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"DIR" Mod OptionFields FilePath
-> Mod OptionFields FilePath -> Mod OptionFields FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields FilePath
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Directory the artifact is written into")
Parser (Bool -> PilotCompileOptions)
-> Parser Bool -> Parser PilotCompileOptions
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Mod FlagFields Bool -> Parser Bool
switch (FilePath -> Mod FlagFields Bool
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"upload" Mod FlagFields Bool -> Mod FlagFields Bool -> Mod FlagFields Bool
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod FlagFields Bool
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"After compiling, upload the artifact to the configured advisory store (one full sync cycle)")
execCLI :: IO AppCommand
execCLI :: IO AppCommand
execCLI =
ParserInfo AppCommand -> IO AppCommand
forall a. ParserInfo a -> IO a
execParser (ParserInfo AppCommand -> IO AppCommand)
-> ParserInfo AppCommand -> IO AppCommand
forall a b. (a -> b) -> a -> b
$
Parser AppCommand -> InfoMod AppCommand -> ParserInfo AppCommand
forall a. Parser a -> InfoMod a -> ParserInfo a
info
(Parser AppCommand
commandParser Parser AppCommand
-> Parser (AppCommand -> AppCommand) -> Parser AppCommand
forall (f :: * -> *) a b. Applicative f => f a -> f (a -> b) -> f b
<**> Parser (AppCommand -> AppCommand)
forall a. Parser (a -> a)
helper Parser AppCommand
-> Parser (AppCommand -> AppCommand) -> Parser AppCommand
forall (f :: * -> *) a b. Applicative f => f a -> f (a -> b) -> f b
<**> Parser (AppCommand -> AppCommand)
forall a. Parser (a -> a)
versionOption)
( InfoMod AppCommand
forall a. InfoMod a
fullDesc
InfoMod AppCommand -> InfoMod AppCommand -> InfoMod AppCommand
forall a. Semigroup a => a -> a -> a
<> FilePath -> InfoMod AppCommand
forall a. FilePath -> InfoMod a
progDesc FilePath
"Écluse - supply-chain resilience proxy"
InfoMod AppCommand -> InfoMod AppCommand -> InfoMod AppCommand
forall a. Semigroup a => a -> a -> a
<> FilePath -> InfoMod AppCommand
forall a. FilePath -> InfoMod a
header FilePath
"ecluse - a configurable policy gate for package registries"
)
where
versionOption :: Parser (a -> a)
versionOption = FilePath -> Mod OptionFields (a -> a) -> Parser (a -> a)
forall a. FilePath -> Mod OptionFields (a -> a) -> Parser (a -> a)
infoOption (Text -> FilePath
forall a. ToString a => a -> FilePath
toString Text
productVersion) (FilePath -> Mod OptionFields (a -> a)
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"version" Mod OptionFields (a -> a)
-> Mod OptionFields (a -> a) -> Mod OptionFields (a -> a)
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields (a -> a)
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Show version")