diff options
-rw-r--r-- | src/Command/Run.hs | 4 | ||||
-rw-r--r-- | src/Job.hs | 31 | ||||
-rw-r--r-- | src/Terminal.hs | 23 |
3 files changed, 41 insertions, 17 deletions
diff --git a/src/Command/Run.hs b/src/Command/Run.hs index b297ec1..fd5b6d7 100644 --- a/src/Command/Run.hs +++ b/src/Command/Run.hs @@ -261,7 +261,7 @@ cmdRun (RunCommand RunOptions {..} args) = do case jobsetJobsEither jobset of Right jobs -> do - outs <- runJobs mngr commit jobs + outs <- runJobs mngr tout commit jobs let findJob name = snd <$> find ((name ==) . jobName . fst) outs line <- newLine tout "" mask $ \restore -> do @@ -291,7 +291,7 @@ showStatus blink = \case JobWaiting uses -> "\ESC[94m~" <> fitToLength 6 (T.intercalate "," (map textJobName uses)) <> "\ESC[0m" JobSkipped -> "\ESC[0m-\ESC[0m " JobRunning -> "\ESC[96m" <> (if blink then "*" else "•") <> "\ESC[0m " - JobError _ -> "\ESC[91m!!\ESC[0m " + JobError fnote -> "\ESC[91m" <> fitToLength 7 ("!! [" <> T.pack (show (footnoteNumber fnote)) <> "]") <> "\ESC[0m" JobFailed -> "\ESC[91m✗\ESC[0m " JobCancelled -> "\ESC[0mC\ESC[0m " JobDone _ -> "\ESC[92m✓\ESC[0m " @@ -38,6 +38,7 @@ import System.Process import Job.Types import Repo +import Terminal data JobOutput = JobOutput @@ -59,7 +60,7 @@ data JobStatus a = JobQueued | JobWaiting [JobName] | JobRunning | JobSkipped - | JobError Text + | JobError TerminalFootnote | JobFailed | JobCancelled | JobDone a @@ -87,7 +88,7 @@ textJobStatus = \case JobWaiting _ -> "waiting" JobRunning -> "running" JobSkipped -> "skipped" - JobError err -> "error\n" <> err + JobError err -> "error\n" <> footnoteText err JobFailed -> "failed" JobCancelled -> "cancelled" JobDone _ -> "done" @@ -179,8 +180,8 @@ runManagedJob JobManager {..} tid cancel job = bracket acquire release $ \case writeTVar jmRunningTasks . M.delete tid =<< readTVar jmRunningTasks -runJobs :: JobManager -> Maybe Commit -> [ Job ] -> IO [ ( Job, TVar (JobStatus JobOutput) ) ] -runJobs mngr@JobManager {..} commit jobs = do +runJobs :: JobManager -> TerminalOutput -> Maybe Commit -> [ Job ] -> IO [ ( Job, TVar (JobStatus JobOutput) ) ] +runJobs mngr@JobManager {..} tout commit jobs = do tree <- sequence $ fmap getCommitTree commit results <- atomically $ do forM jobs $ \job -> do @@ -197,9 +198,12 @@ runJobs mngr@JobManager {..} commit jobs = do return statusVar forM_ results $ \( job, tid, outVar ) -> void $ forkIO $ do - let handler e = atomically $ writeTVar outVar $ if - | Just JobCancelledException <- fromException e -> JobCancelled - | otherwise -> JobError (T.pack $ displayException e) + let handler e = if + | Just JobCancelledException <- fromException e -> do + atomically $ writeTVar outVar $ JobCancelled + | otherwise -> do + footnote <- newFootnote tout $ T.pack $ displayException e + atomically $ writeTVar outVar $ JobError footnote handle handler $ do res <- runExceptT $ do duplicate <- liftIO $ atomically $ do @@ -211,7 +215,7 @@ runJobs mngr@JobManager {..} commit jobs = do case duplicate of Nothing -> do - uses <- waitForUsedArtifacts job results outVar + uses <- waitForUsedArtifacts tout job results outVar runManagedJob mngr tid (return JobCancelled) $ do liftIO $ atomically $ writeTVar outVar JobRunning prepareJob jmDataDir commit job $ \checkoutPath jdir -> do @@ -233,21 +237,18 @@ runJobs mngr@JobManager {..} commit jobs = do else wait liftIO wait - case res of - Left (JobError err) -> T.putStrLn err - _ -> return () - atomically $ writeTVar outVar $ either id id res return $ map (\( job, _, var ) -> ( job, var )) results waitForUsedArtifacts :: (MonadIO m, MonadError (JobStatus JobOutput) m) => + TerminalOutput -> Job -> [ ( Job, TaskId, TVar (JobStatus JobOutput) ) ] -> TVar (JobStatus JobOutput) -> m [ ArtifactOutput ] -waitForUsedArtifacts job results outVar = do +waitForUsedArtifacts tout job results outVar = do origState <- liftIO $ atomically $ readTVar outVar ujobs <- forM (jobUses job) $ \(ujobName@(JobName tjobName), uartName) -> do case find (\( j, _, _ ) -> jobName j == ujobName) results of Just ( _, _, var ) -> return ( var, ( ujobName, uartName )) - Nothing -> throwError $ JobError $ "Job '" <> tjobName <> "' not found" + Nothing -> throwError . JobError =<< liftIO (newFootnote tout $ "Job '" <> tjobName <> "' not found") let loop prev = do ustatuses <- atomically $ do @@ -266,7 +267,7 @@ waitForUsedArtifacts job results outVar = do case ustatus of JobDone out -> case find ((==uartName) . aoutName) $ outArtifacts out of Just art -> return art - Nothing -> throwError $ JobError $ "Artifact '" <> tjobName <> "." <> tartName <> "' not found" + Nothing -> throwError . JobError =<< liftIO (newFootnote tout $ "Artifact '" <> tjobName <> "." <> tartName <> "' not found") _ -> throwError JobSkipped updateStatusFile :: MonadIO m => FilePath -> TVar (JobStatus JobOutput) -> m () diff --git a/src/Terminal.hs b/src/Terminal.hs index 84dfb91..aa7335c 100644 --- a/src/Terminal.hs +++ b/src/Terminal.hs @@ -1,9 +1,11 @@ module Terminal ( TerminalOutput, TerminalLine, + TerminalFootnote(..), initTerminalOutput, newLine, redrawLine, + newFootnote, terminalBlinkStatus, ) where @@ -11,6 +13,7 @@ import Control.Concurrent import Control.Concurrent.STM import Control.Monad +import Data.Function import Data.Text (Text) import Data.Text qualified as T import Data.Text.IO qualified as T @@ -20,17 +23,30 @@ import System.IO data TerminalOutput = TerminalOutput { outNumLines :: MVar Int + , outNextFootnote :: MVar Int , outBlinkVar :: TVar Bool } +instance Eq TerminalOutput where + (==) = (==) `on` outNumLines + data TerminalLine = TerminalLine { lineOutput :: TerminalOutput , lineNum :: Int } + deriving (Eq) + +data TerminalFootnote = TerminalFootnote + { footnoteLine :: TerminalLine + , footnoteNumber :: Int + , footnoteText :: Text + } + deriving (Eq) initTerminalOutput :: IO TerminalOutput initTerminalOutput = do outNumLines <- newMVar 0 + outNextFootnote <- newMVar 1 outBlinkVar <- newTVarIO False void $ forkIO $ forever $ do threadDelay 500000 @@ -52,5 +68,12 @@ redrawLine TerminalLine {..} text = do T.putStr $ "\ESC[s\ESC[" <> T.pack (show moveBy) <> "F" <> text <> "\ESC[u" hFlush stdout +newFootnote :: TerminalOutput -> Text -> IO TerminalFootnote +newFootnote tout@TerminalOutput {..} footnoteText = do + modifyMVar outNextFootnote $ \footnoteNumber -> do + footnoteLine <- newLine tout $ "[" <> T.pack (show footnoteNumber) <> "] " <> footnoteText + hFlush stdout + return ( footnoteNumber + 1, TerminalFootnote {..} ) + terminalBlinkStatus :: TerminalOutput -> STM Bool terminalBlinkStatus TerminalOutput {..} = readTVar outBlinkVar |