on master help sign in
Report task status (success vs fail) after spinner

In colors.

commitade2c21003b16c2f9d1d7c205c01eaa1c5b861bd authorSridhar Ratnakumar <srid@srid.ca> dateSat 12 May 2018, 16:00 (8 years ago) parent736f708
6 files changed+47−46
lib/command/src/Obelisk/CLI/Logging.hs+10−10 lib/command/src/Obelisk/CLI/Spinner.hs+28−24 lib/command/src/Obelisk/Command.hs+5−6 lib/command/src/Obelisk/Command/Nix.hs+1−1 lib/command/src/Obelisk/Command/Project.hs+1−2 lib/command/src/Obelisk/Command/Thunk.hs+2−3
lib/command/src/Obelisk/CLI/Logging.hs+10−10
@@ -29,40 +29,40 @@ import Control.Monad.Log (MonadLog, Severity (..), WithSeverity (..), logMessage
2929
3030data LoggingConfig = LoggingConfig
3131 { _loggingConfig_level :: Severity -- TODO: Start using this.
32− , _loggingConfig_lock :: MVar Bool -- Whether the last message was raw
32+ , _loggingConfig_lock :: MVar Bool -- Whether the last message was an Overwrite output
3333 }
3434
3535data Output
3636 = Output_Log (WithSeverity Text) -- Regular logging message (with colors and newlines)
37− | Output_Raw [String] -- Raw text messages to print using `putStr`
37+ | Output_Overwrite [String] -- Overwrites the current line (i.e. \r followed by `putStr`)
3838 | Output_ClearLine -- Clears the line
3939 deriving (Eq, Show, Ord)
4040
41−isRaw :: Output -> Bool
42−isRaw = \case
43− Output_Raw _ -> True
41+isOverwrite :: Output -> Bool
42+isOverwrite = \case
43+ Output_Overwrite _ -> True
4444 _ -> False
4545
4646handleLog :: MonadIO m => LoggingConfig -> Output -> m ()
4747handleLog c current = do
4848 let lock = _loggingConfig_lock c
49− liftIO $ modifyMVar_ lock $ \raw -> do
49+ liftIO $ modifyMVar_ lock $ \wasOverwriting -> do
5050 case current of
5151 Output_Log (WithSeverity sev _) -> do
5252 -- Discard unless severity is above configured log level
5353 unless (sev > _loggingConfig_level c) $ do
54− -- If the last output was raw first clear it,
55− when raw $ handleLog' Output_ClearLine
54+ -- If the last output was an overwrite (with cursor on same line), first clear it,
55+ when wasOverwriting $ handleLog' Output_ClearLine
5656 -- ... then actually write the log.
5757 handleLog' current
5858 _ ->
5959 handleLog' current
60− return $ isRaw current
60+ return $ isOverwrite current
6161
6262handleLog' :: MonadIO m => Output -> m ()
6363handleLog' = \case
6464 Output_Log m -> liftIO $ writeLog m
65− Output_Raw xs -> liftIO $ putStrs xs
65+ Output_Overwrite xs -> liftIO $ putStrs $ "\r" : xs
6666 Output_ClearLine -> liftIO $ do
6767 -- Go to the first column and clear the whole line
6868 putStrs ["\r"]
lib/command/src/Obelisk/CLI/Spinner.hs+28−24
@@ -1,16 +1,17 @@
11{-# LANGUAGE FlexibleContexts #-}
22{-# LANGUAGE MultiParamTypeClasses #-}
3+{-# LANGUAGE OverloadedStrings #-}
34module Obelisk.CLI.Spinner (withSpinner') where
45
56import Control.Concurrent (forkIO, killThread, threadDelay)
67import Control.Monad (forM_)
7−import Control.Monad.Catch (MonadMask, bracket)
8+import Control.Monad.Catch (MonadMask, mask, onException)
89import Control.Monad.IO.Class (liftIO)
910import Control.Monad.Log (MonadLog, logMessage, runLoggingT)
1011import Control.Monad.Reader (MonadIO)
1112import Data.Text (Text)
1213import qualified Data.Text as T
13−import System.Console.ANSI (Color (Magenta), ColorIntensity (Vivid), ConsoleLayer (Foreground),
14+import System.Console.ANSI (Color (Green, Magenta, Red), ColorIntensity (Vivid), ConsoleLayer (Foreground),
1415 SGR (Reset, SetColor), setSGRCode)
1516
1617import Obelisk.CLI.Logging (LoggingConfig, Output (..), handleLog)
@@ -20,17 +21,20 @@ import Obelisk.CLI.Logging (LoggingConfig, Output (..), handleLog)
2021withSpinner'
2122 :: (MonadIO m, MonadMask m, MonadLog Output m)
2223 => LoggingConfig -> Text -> m a -> m a
23−withSpinner' c s = bracket run cleanup . const
24+withSpinner' c s = bracket' run cleanup . const
2425 where
2526 -- TODO: Can we obviate passing LoggingConfig just to use forkIO?
2627 run = liftIO $ forkIO $ flip runLoggingT (handleLog c) $ do
27− forM_ (coloredSpinner spinnerPoint) $ \e -> do
28− logMessage $ Output_Raw ["\r", e, " ", T.unpack s]
28+ forM_ spin $ \e -> do
29+ logMessage $ Output_Overwrite [e, " ", T.unpack s]
2930 delay
30− cleanup tid = do
31+ cleanup failed tid = do
3132 liftIO $ killThread tid
3233 logMessage Output_ClearLine
34+ let mark = if failed then withColor Red "✖" else withColor Green "✔"
35+ logMessage $ Output_Overwrite [mark, " ", T.unpack s, "\n"]
3336 delay = liftIO $ threadDelay 100000 -- A shorter delay ensures that we update promptly.
37+ spin = coloredSpinner spinnerCircleHalves
3438
3539type Spinner = [String]
3640
@@ -38,27 +42,27 @@ spinner :: SpinnerTheme -> Spinner
3842spinner = cycle
3943
4044coloredSpinner :: SpinnerTheme -> Spinner
41−coloredSpinner = spinner . fmap withColor
42− where
43− withColor s = mconcat
44− [ setSGRCode [SetColor Foreground Vivid color]
45− , s
46− , setSGRCode [Reset]
47− ]
48− color = Magenta
45+coloredSpinner = spinner . fmap (withColor Magenta)
46+
47+withColor :: Color -> String -> String
48+withColor color s = mconcat
49+ [ setSGRCode [SetColor Foreground Vivid color]
50+ , s
51+ , setSGRCode [Reset]
52+ ]
53+
4954
5055-- Some spinners from https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json
5156
5257type SpinnerTheme = [String]
5358
54−spinnerPoint :: SpinnerTheme
55−spinnerPoint =
56− [ "∙∙∙"
57− , "●∙∙"
58− , "∙●∙"
59− , "∙∙●"
60− , "∙∙∙"
61− ]
59+spinnerCircleHalves :: SpinnerTheme
60+spinnerCircleHalves = ["◐", "◓", "◑", "◒"]
6261
63−-- spinnerCircleHalves :: SpinnerTheme
64−-- spinnerCircleHalves = ["◐", "◓", "◑", "◒"]
62+-- | Like `bracket` but the `release` function can know whether an exception was raised
63+bracket' :: MonadMask m => m a -> (Bool -> a -> m b) -> (a -> m c) -> m c
64+bracket' acquire release use = mask $ \unmasked -> do
65+ resource <- acquire
66+ result <- unmasked (use resource) `onException` release True resource
67+ _ <- release False resource
68+ return result
lib/command/src/Obelisk/Command.hs+5−6
@@ -259,18 +259,17 @@ ob = \case
259259 Just p -> liftIO $ deRefStaticPtr p
260260 ObInternal_TestLogging -> do -- TODO: Remove this command once thoroughly satisfied with logging/spinner.
261261 putLog Notice "We will now log a warning, and then start a spinner"
262− putLog Warning "Warnings are outputed in color"
263− withSpinner "Just spinning!" $ do
262+ withSpinner "Running some long-running task" $ do
264263 liftIO $ threadDelay 1000000
265264 putLog Error "Some user error while spinning"
266265 liftIO $ threadDelay 1000000
267266 putLog Notice "This is some info mesage"
268267 putLog Warning "And now a warning as well"
269268 liftIO $ threadDelay 1000000
270− -- failWith "Failing inside spinner"
271− putLog Notice "Done spinning."
272− putLog Warning "Going to fail now..."
273− failWith "This is fail message"
269+ putLog Notice "Now we start a 2nd spinner and fail inside of that:"
270+ withSpinner "Doing something dangerous" $ do
271+ liftIO $ threadDelay 1000000
272+ failWith "Something dangerous happened"
274273
275274--TODO: Clean up all the magic strings throughout this codebase
276275
lib/command/src/Obelisk/Command/Nix.hs+1−1
@@ -76,7 +76,7 @@ nixBuild cfg = do
7676 { std_out = CreatePipe
7777 , std_err = CreatePipe
7878 }
79− let msg = T.pack $ "Running nix-build [" <> _target_path (_nixBuildConfig_target cfg) <> "] ..."
79+ let msg = T.pack $ "Running nix-build [" <> _target_path (_nixBuildConfig_target cfg) <> "]"
8080 withSpinner msg $ do
8181 liftIO (waitForProcess p) >>= \case
8282 ExitSuccess -> return ()
lib/command/src/Obelisk/Command/Project.hs+1−2
@@ -70,7 +70,7 @@ initProject source = do
7070 _ <- nixBuildAttrWithCache implDir "command"
7171 --TODO: We should probably handoff to the impl here
7272 skeleton <- nixBuildAttrWithCache implDir "skeleton" --TODO: I don't think there's actually any reason to cache this
73− withSpinner "Copying project skeleton ..." $ do
73+ withSpinner "Copying project skeleton" $ do
7474 liftIO $ cp --TODO: Make this package depend on nix-prefetch-url properly
7575 [ "-r"
7676 , "--no-preserve=mode"
@@ -78,7 +78,6 @@ initProject source = do
7878 , skeleton </> "."
7979 , "."
8080 ]
81− putLog Notice "Created project skeleton."
8281
8382--TODO: Handle errors
8483--TODO: Allow the user to ignore our security concerns
lib/command/src/Obelisk/Command/Thunk.hs+2−3
@@ -471,18 +471,17 @@ unpackThunk thunkDir = readThunk thunkDir >>= \case
471471 githubURI <- liftIO $ either throwIO (return . repoSshUrl) repoResult >>= \case
472472 Nothing -> fail "Cannot determine clone URI for thunk source"
473473 Just c -> return $ T.unpack $ getUrl c
474− withSpinner "Cloning from GitHub ..." $ do
474+ withSpinner ("Cloning " <> T.pack thunkName <> " from GitHub") $ do
475475 liftIO $ callProcess "hub" --TODO: Depend on hub explicitly
476476 [ "clone"
477477 , "-n"
478478 , githubURI
479479 , tmpRepo
480480 ]
481− putLog Notice $ "Cloned " <> T.pack githubURI
482481 let obGitDir = tmpRepo </> ".git" </> "obelisk"
483482 --If this directory already exists then something is weird and we should fail
484483 liftIO $ createDirectory obGitDir
485− withSpinner "Copying thunk ..." $ do
484+ withSpinner "Copying thunk" $ do
486485 liftIO $ cp
487486 [ "-r"
488487 , "-T"