{-| Defines the entry point of the @feed-gipeda@ executable and the command line
parsing associated with that.
-}
import Control.Applicative
import Control.Logging as Logging
import Control.Monad (join)
import Data.Functor
import Data.List (intercalate, last, init)
import Data.List.Extra (split)
import Data.Monoid ((<>))
import FeedGipeda
import Options.Applicative
import System.Directory (getAppUserDataDirectory)
import System.FilePath ((</>))
import Text.Read (readMaybe)
paths :: FilePath -> Parser Paths
paths defaultConfig =
Paths
<$> option str
(long "config"
<> value defaultConfig
<> metavar "FILEPATH"
<> help ("Path to the YAML file containing a list of watched repositories. Will be watched for changes. Defaults to " ++ defaultConfig ++ "."))
<*> option str
(long "gipeda"
<> value "gipeda"
<> metavar "FILEPATH"
<> help "Custom path to the gipeda executable")
cmd :: Parser Command
cmd =
flag'
Check
(long "check"
<> help "Verify that the given config file is well-formed and exit")
<|>
Build
<$> option
(WatchForChanges . fromIntegral <$> auto)
(long "watch"
<> metavar "SECONDS"
<> value Once
<> help "Don't quit when done, watch the config file for changes and refetch watched repositories every SECONDS seconds and benchmark new commits.")
<*> option
(fromIntegral <$> auto)
(long "timeout"
<> metavar "SECONDS"
<> value (fromIntegral (30*60))
<> help "Timeout for a benchmark run. A slave is canceled if it needs more than SECONDS seconds. Defaults to 30 minutes.")
deployment :: Parser Deployment
deployment =
option
(Deploy <$> str)
(long "deploy-to"
<> metavar "SSH_PATH"
<> value NoDeployment
<> help "ssh or local path under which to deploy site/ folders with rsync")
slave :: ReadM ProcessRole
slave = do
s <- str
case split (== ':') s of
sport : rest | length rest > 1 -> -- support IPv6
case (readMaybe sport, readMaybe mport) of
(Just sp, Just mp) -> return (Slave sp (Endpoint mhost mp))
(Nothing, _) -> readerError "Slave port was not integral"
(_, Nothing) -> readerError "Master port was not integral"
where
mport = last rest
mhost = intercalate ":" (init rest)
_ -> readerError "Expected 3 sections separated by a colon"
processRole :: Parser ProcessRole
processRole =
impl
<$> optional (option auto
(long "master"
<> metavar "PORT"
<> help "Start in master mode, distributing work items. Identified via the given TCP port number."))
<*> optional (option slave
(long "slave"
<> metavar "PORT:HOST:PORT"
<> help "Start in slave mode, requesting work items from a master node. Identified via the given local slave port and the name and port of the master node."))
where
impl Nothing Nothing = Both 1337 1338
impl (Just port) Nothing = Master port
impl Nothing (Just s) = s
impl (Just master) (Just (Slave slave ep)) =
if port ep == master && host ep `elem` ["localhost", "127.0.0.1", "::1"]
then Both master slave
else error "Contradiction in --master and --slave args!" -- not proud of this
verbosity :: Parser Verbosity
verbosity =
flag
NotVerbose
Verbose
(long "verbose"
<> short 'v'
<> help "Show log messages intended for debugging")
parser :: IO (Parser (IO ()))
parser = do
defaultConfig <- getAppUserDataDirectory ("feed-gipeda" </> "feed-gipeda.yaml")
return $
FeedGipeda.feedGipeda
<$> paths defaultConfig
<*> cmd
<*> deployment
<*> processRole
<*> verbosity
main :: IO ()
main = Logging.withStdoutLogging $ do
p <- parser
join $ execParser $ info (helper <*> p) $
fullDesc
<> header "feed-gipeda - watch git repositories and feed benchmark results to gipeda"
-- <> progDesc "" -- TODO