module Main (main) where import Converge import Data.Maybe (fromMaybe) import Data.Text (Text) import Options.Applicative import System.IO (hPutStrLn, stderr) data CliArgs = CliArgs { cliConfig :: !(Maybe FilePath) -- ^ Optional path to a YAML config file. , cliRepo :: !(Maybe FilePath) -- ^ Optional single repo path (requires --config to be absent). , cliRemote :: !Text -- ^ Remote name (used only in single-repo mode). , cliBranch :: !Text -- ^ Branch name (used only in single-repo mode). , cliDebounce :: !(Maybe Int) -- ^ Debounce override for both modes. } cliParser :: Parser CliArgs cliParser = CliArgs <$> optional ( strOption ( long "config" <> short 'c' <> metavar "FILE" <> help "Path to YAML config file listing repositories" ) ) <*> optional ( strOption ( long "repo" <> short 'r' <> metavar "PATH" <> help "Path to a single git repository to watch (default: cwd)" ) ) <*> strOption ( long "remote" <> metavar "NAME" <> value "origin" <> showDefault <> help "Remote name to pull from (single-repo mode)" ) <*> strOption ( long "branch" <> short 'b' <> metavar "NAME" <> value "main" <> showDefault <> help "Branch to pull (single-repo mode)" ) <*> optional ( option auto ( long "debounce" <> short 'd' <> metavar "MS" <> help "Debounce window in milliseconds (overrides config file)" ) ) parserInfo :: ParserInfo CliArgs parserInfo = info (cliParser <**> helper) ( fullDesc <> progDesc "Auto-sync git repos by watching, committing, and pulling" <> header "converge - keep git repos in sync" ) main :: IO () main = do cli <- execParser parserInfo let mDebounceOverride = cliDebounce cli (options, configSource) <- case cliConfig cli of Just cfgPath -> do cfg <- loadConfig cfgPath let opts = applyDebounce mDebounceOverride (configToOptions cfg) pure (opts, Just cfgPath) Nothing -> do exists <- configFileExists if exists then do cfgPath <- defaultConfigPath cfg <- loadConfig cfgPath let opts = applyDebounce mDebounceOverride (configToOptions cfg) pure (opts, Just cfgPath) else pure ( defaultOptions { optRepos = [ defaultRepoConfig { rcPath = fromMaybe "." (cliRepo cli) , rcRemote = cliRemote cli , rcBranch = cliBranch cli } ] , optDebounceMs = fromMaybe 5000 mDebounceOverride } , Nothing ) case configSource of Just path -> hPutStrLn stderr ("Using config: " <> path) Nothing -> hPutStrLn stderr "No config file found, using single-repo mode" hPutStrLn stderr $ "Watching " <> show (length (optRepos options)) <> " repo(s)" <> " (debounce: " <> show (optDebounceMs options) <> "ms)" mapM_ (\r -> hPutStrLn stderr (" - " <> rcPath r)) (optRepos options) runConverge options applyDebounce :: Maybe Int -> Options -> Options applyDebounce (Just ms) opts = opts{optDebounceMs = ms} applyDebounce Nothing opts = opts