summaryrefslogtreecommitdiff
path: root/src/Command
diff options
context:
space:
mode:
Diffstat (limited to 'src/Command')
-rw-r--r--src/Command/Run.hs156
1 files changed, 131 insertions, 25 deletions
diff --git a/src/Command/Run.hs b/src/Command/Run.hs
index 14341cd..52b70f3 100644
--- a/src/Command/Run.hs
+++ b/src/Command/Run.hs
@@ -9,11 +9,13 @@ import Control.Monad
import Control.Monad.Reader
import Data.List
-import Data.Maybe
import Data.Text (Text)
import Data.Text qualified as T
+import Data.Text.IO qualified as T
+import System.Console.GetOpt
import System.Exit
+import System.IO
import System.Process
import Command
@@ -23,13 +25,18 @@ import Repo
import Terminal
-data RunCommand = RunCommand Text
+data RunCommand = RunCommand RunOptions [ Text ]
+
+data RunOptions = RunOptions
+ { roRanges :: [ Text ]
+ , roNewCommitsOn :: [ Text ]
+ }
instance Command RunCommand where
commandName _ = "run"
commandDescription _ = "Execude jobs per minici.yaml for given commits"
- type CommandArguments RunCommand = Maybe Text
+ type CommandArguments RunCommand = [ Text ]
commandUsage _ = T.pack $ unlines $
[ "Usage: minici run"
@@ -38,36 +45,121 @@ instance Command RunCommand where
, " run jobs for commits on <ref> not yet in its upstream ref"
, " or: minici run <commit>..<commit>"
, " run jobs for commits in given range"
+ , " or: minici run <option>..."
+ , " run jobs based on given options (see below)"
+ ]
+
+ type CommandOptions RunCommand = RunOptions
+ defaultCommandOptions _ = RunOptions
+ { roRanges = []
+ , roNewCommitsOn = []
+ }
+
+ commandOptions _ =
+ [ Option [] [ "range" ]
+ (ReqArg (\val opts -> opts { roRanges = T.pack val : roRanges opts }) "<range>")
+ "run jobs for commits in given range"
+ , Option [] [ "new-commits-on" ]
+ (ReqArg (\val opts -> opts { roNewCommitsOn = T.pack val : roNewCommitsOn opts }) "<branch>")
+ "run jobs for new commits on given branch"
]
- commandInit _ _ = RunCommand . fromMaybe "HEAD"
+ commandInit _ = RunCommand
commandExec = cmdRun
+
+data JobSource = JobSource (TMVar (Maybe ( [ JobSet ], JobSource )))
+
+takeJobSource :: JobSource -> STM (Maybe ( [ JobSet ], JobSource ))
+takeJobSource (JobSource tmvar) = takeTMVar tmvar
+
+mergeSources :: [ JobSource ] -> IO JobSource
+mergeSources sources = do
+ let go tmvar [] = do
+ atomically (putTMVar tmvar Nothing)
+ go tmvar cur = do
+ ( jobsets, next ) <- atomically (select cur)
+ if null next
+ then do
+ go tmvar next
+ else do
+ nextvar <- newEmptyTMVarIO
+ atomically $ putTMVar tmvar (Just ( jobsets, JobSource nextvar ))
+ go nextvar next
+
+ tmvar <- newEmptyTMVarIO
+ void $ forkIO $ go tmvar sources
+ return $ JobSource tmvar
+
+ where
+ select :: [ JobSource ] -> STM ( [ JobSet ], [ JobSource ] )
+ select [] = retry
+ select (x@(JobSource tmvar) : xs) = do
+ tryTakeTMVar tmvar >>= \case
+ Nothing -> fmap (x :) <$> select xs
+ Just Nothing -> return ( [], xs )
+ Just (Just ( jobsets, x' )) -> return ( jobsets, x' : xs )
+
+
+rangeSource :: Repo -> Text -> Text -> IO JobSource
+rangeSource repo base tip = do
+ commits <- listCommits repo (base <> ".." <> tip)
+ jobsets <- mapM loadJobSetForCommit commits
+ next <- JobSource <$> newTMVarIO Nothing
+ JobSource <$> newTMVarIO (Just ( jobsets, next ))
+
+watchBranchSource :: Repo -> Text -> IO JobSource
+watchBranchSource repo branch = do
+ getCurrentTip <- watchBranch repo branch
+ let go prev tmvar = do
+ cur <- atomically $ do
+ getCurrentTip >>= \case
+ Just cur -> do
+ when (cur == prev) retry
+ return cur
+ Nothing -> retry
+
+ commits <- listCommits repo (textCommitId (commitId prev) <> ".." <> textCommitId (commitId cur))
+ jobsets <- mapM loadJobSetForCommit commits
+ nextvar <- newEmptyTMVarIO
+ atomically $ putTMVar tmvar $ Just ( jobsets, JobSource nextvar )
+ go cur nextvar
+
+ tmvar <- newEmptyTMVarIO
+ atomically getCurrentTip >>= \case
+ Just commit ->
+ void $ forkIO $ go commit tmvar
+ Nothing -> do
+ T.hPutStrLn stderr $ "Branch `" <> branch <> "' not found"
+ atomically $ putTMVar tmvar Nothing
+ return $ JobSource tmvar
+
cmdRun :: RunCommand -> CommandExec ()
-cmdRun (RunCommand changeset) = do
+cmdRun (RunCommand RunOptions {..} args) = do
CommonOptions {..} <- getCommonOptions
- ( base, tip ) <- case T.splitOn (T.pack "..") changeset of
- base : tip : _ -> return ( T.unpack base, T.unpack tip )
- [ param ] -> liftIO $ do
- [ deref ] <- readProcessWithExitCode "git" [ "symbolic-ref", "--quiet", T.unpack param ] "" >>= \case
- ( ExitSuccess, out, _ ) -> return $ lines out
- ( _, _, _ ) -> return [ T.unpack param ]
- [ _, tip ] : _ <- fmap words . lines <$> readProcess "git" [ "show-ref", deref ] ""
- [ base ] <- lines <$> readProcess "git" [ "for-each-ref", "--format=%(upstream)", tip ] ""
- return ( base, tip )
- [] -> error "splitOn should not return empty list"
-
tout <- getTerminalOutput
+
liftIO $ do
- mngr <- newJobManager "./.minici" optJobs
Just repo <- openRepo "."
- commits <- listCommits repo (base <> ".." <> tip)
- jobssets <- mapM loadJobSetForCommit commits
- let names = nub $ map jobName $ concatMap jobsetJobs jobssets
+ ranges <- forM (args ++ roRanges) $ \changeset -> do
+ ( base, tip ) <- case T.splitOn ".." changeset of
+ base : tip : _ -> return ( base, tip )
+ [ param ] -> liftIO $ do
+ [ deref ] <- readProcessWithExitCode "git" [ "symbolic-ref", "--quiet", T.unpack param ] "" >>= \case
+ ( ExitSuccess, out, _ ) -> return $ lines out
+ ( _, _, _ ) -> return [ T.unpack param ]
+ [ _, tip ] : _ <- fmap words . lines <$> readProcess "git" [ "show-ref", deref ] ""
+ [ base ] <- lines <$> readProcess "git" [ "for-each-ref", "--format=%(upstream)", tip ] ""
+ return ( T.pack base, T.pack tip )
+ [] -> error "splitOn should not return empty list"
+ rangeSource repo base tip
+
+ branches <- mapM (watchBranchSource repo) roNewCommitsOn
- void $ newLine tout $ T.concat $
- T.replicate (8 + 50) " " :
- map ((" "<>) . fitToLength 7 . textJobName) names
+ mngr <- newJobManager "./.minici" optJobs
+
+ source <- mergeSources $ concat [ ranges, branches ]
+ headerLine <- newLine tout ""
threadCount <- newTVarIO (0 :: Int)
let changeCount f = atomically $ do
@@ -75,11 +167,21 @@ cmdRun (RunCommand changeset) = do
let waitForJobs = atomically $ do
flip when retry . (0 <) =<< readTVar threadCount
- handle @SomeException (\_ -> cancelAllJobs mngr) $ do
- forM_ jobssets $ \jobset -> do
+ let loop _ Nothing = return ()
+ loop names (Just ( [], next )) = do
+ loop names =<< atomically (takeJobSource next)
+
+ loop pnames (Just ( jobset : rest, next )) = do
+ let names = nub $ (pnames ++) $ map jobName $ jobsetJobs jobset
+ when (names /= pnames) $ do
+ redrawLine headerLine $ T.concat $
+ T.replicate (8 + 50) " " :
+ map ((" " <>) . fitToLength 7 . textJobName) names
+
let commit = jobsetCommit jobset
shortCid = T.pack $ take 7 $ showCommitId $ commitId commit
shortDesc <- fitToLength 50 <$> getCommitTitle commit
+
case jobsetJobsEither jobset of
Right jobs -> do
outs <- runJobs mngr commit jobs
@@ -93,6 +195,10 @@ cmdRun (RunCommand changeset) = do
Left err -> do
void $ newLine tout $
"\ESC[91m" <> shortCid <> "\ESC[0m" <> " " <> shortDesc <> " \ESC[91m" <> T.pack err <> "\ESC[0m"
+ loop names (Just ( rest, next ))
+
+ handle @SomeException (\_ -> cancelAllJobs mngr) $ do
+ loop [] =<< atomically (takeJobSource source)
waitForJobs
waitForJobs