packages feed

b9 0.5.68.1 → 0.5.68.2

raw patch · 10 files changed

+200/−101 lines, 10 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- B9: b9ConfigToCPDocument :: B9Config -> Either CPError CPDocument
+ B9: b9ConfigToCPDocument :: HasCallStack => B9Config -> Either CPError CPDocument
- B9: modifyPermanentConfig :: Member B9ConfigWriter e => Endo B9Config -> Eff e ()
+ B9: modifyPermanentConfig :: (HasCallStack, Member B9ConfigWriter e) => Endo B9Config -> Eff e ()
- B9: openOrCreateB9Config :: MonadIO m => FilePath -> m CPDocument
+ B9: openOrCreateB9Config :: (HasCallStack, MonadIO m) => FilePath -> m CPDocument
- B9: parseB9Config :: CPDocument -> Either CPError B9Config
+ B9: parseB9Config :: HasCallStack => CPDocument -> Either CPError B9Config
- B9: preferredSourceImageTypes :: ImageDestination -> [ImageType]
+ B9: preferredSourceImageTypes :: HasCallStack => ImageDestination -> [ImageType]
- B9: readB9Config :: MonadIO m => Maybe SystemPath -> m CPDocument
+ B9: readB9Config :: (HasCallStack, MonadIO m) => Maybe SystemPath -> m CPDocument
- B9: runB9 :: B9 a -> B9ConfigAction a
+ B9: runB9 :: HasCallStack => B9 a -> B9ConfigAction a
- B9: runB9ConfigAction :: B9ConfigAction a -> IO a
+ B9: runB9ConfigAction :: HasCallStack => B9ConfigAction a -> IO a
- B9: runB9ConfigActionWithOverrides :: B9ConfigAction a -> B9ConfigOverride -> IO a
+ B9: runB9ConfigActionWithOverrides :: HasCallStack => B9ConfigAction a -> B9ConfigOverride -> IO a
- B9: runB9ConfigReader :: B9Config -> Eff (B9ConfigReader : e) a -> Eff e a
+ B9: runB9ConfigReader :: HasCallStack => B9Config -> Eff (B9ConfigReader : e) a -> Eff e a
- B9: substImageTarget :: forall e. (Member EnvironmentReader e, Member ExcB9 e) => ImageTarget -> Eff e ImageTarget
+ B9: substImageTarget :: forall e. (HasCallStack, Member EnvironmentReader e, Member ExcB9 e) => ImageTarget -> Eff e ImageTarget
- B9: type IsB9 e = (Lifted IO e, CommandIO e, B9Eff <:: e)
+ B9: type IsB9 e = (HasCallStack, Lifted IO e, CommandIO e, B9Eff <:: e)
- B9: withBuildInfo :: (Lifted IO e, MonadBaseControl IO (Eff e), Member B9ConfigReader e, Member ExcB9 e, Member EnvironmentReader e, Member LoggerReader e) => Eff (BuildInfoReader : e) a -> Eff e a
+ B9: withBuildInfo :: (Lifted IO e, MonadBaseControl IO (Eff e), Member B9ConfigReader e, Member ExcB9 e, Member EnvironmentReader e, Member LoggerReader e, HasCallStack) => Eff (BuildInfoReader : e) a -> Eff e a
- B9: writeB9CPDocument :: MonadIO m => Maybe SystemPath -> CPDocument -> m ()
+ B9: writeB9CPDocument :: (HasCallStack, MonadIO m) => Maybe SystemPath -> CPDocument -> m ()
- B9.B9Config: b9ConfigToCPDocument :: B9Config -> Either CPError CPDocument
+ B9.B9Config: b9ConfigToCPDocument :: HasCallStack => B9Config -> Either CPError CPDocument
- B9.B9Config: modifyPermanentConfig :: Member B9ConfigWriter e => Endo B9Config -> Eff e ()
+ B9.B9Config: modifyPermanentConfig :: (HasCallStack, Member B9ConfigWriter e) => Endo B9Config -> Eff e ()
- B9.B9Config: openOrCreateB9Config :: MonadIO m => FilePath -> m CPDocument
+ B9.B9Config: openOrCreateB9Config :: (HasCallStack, MonadIO m) => FilePath -> m CPDocument
- B9.B9Config: parseB9Config :: CPDocument -> Either CPError B9Config
+ B9.B9Config: parseB9Config :: HasCallStack => CPDocument -> Either CPError B9Config
- B9.B9Config: readB9Config :: MonadIO m => Maybe SystemPath -> m CPDocument
+ B9.B9Config: readB9Config :: (HasCallStack, MonadIO m) => Maybe SystemPath -> m CPDocument
- B9.B9Config: runB9ConfigAction :: B9ConfigAction a -> IO a
+ B9.B9Config: runB9ConfigAction :: HasCallStack => B9ConfigAction a -> IO a
- B9.B9Config: runB9ConfigActionWithOverrides :: B9ConfigAction a -> B9ConfigOverride -> IO a
+ B9.B9Config: runB9ConfigActionWithOverrides :: HasCallStack => B9ConfigAction a -> B9ConfigOverride -> IO a
- B9.B9Config: runB9ConfigReader :: B9Config -> Eff (B9ConfigReader : e) a -> Eff e a
+ B9.B9Config: runB9ConfigReader :: HasCallStack => B9Config -> Eff (B9ConfigReader : e) a -> Eff e a
- B9.B9Config: writeB9CPDocument :: MonadIO m => Maybe SystemPath -> CPDocument -> m ()
+ B9.B9Config: writeB9CPDocument :: (HasCallStack, MonadIO m) => Maybe SystemPath -> CPDocument -> m ()
- B9.B9Monad: runB9 :: B9 a -> B9ConfigAction a
+ B9.B9Monad: runB9 :: HasCallStack => B9 a -> B9ConfigAction a
- B9.B9Monad: type IsB9 e = (Lifted IO e, CommandIO e, B9Eff <:: e)
+ B9.B9Monad: type IsB9 e = (HasCallStack, Lifted IO e, CommandIO e, B9Eff <:: e)
- B9.BuildInfo: withBuildInfo :: (Lifted IO e, MonadBaseControl IO (Eff e), Member B9ConfigReader e, Member ExcB9 e, Member EnvironmentReader e, Member LoggerReader e) => Eff (BuildInfoReader : e) a -> Eff e a
+ B9.BuildInfo: withBuildInfo :: (Lifted IO e, MonadBaseControl IO (Eff e), Member B9ConfigReader e, Member ExcB9 e, Member EnvironmentReader e, Member LoggerReader e, HasCallStack) => Eff (BuildInfoReader : e) a -> Eff e a
- B9.DiskImageBuilder: preferredSourceImageTypes :: ImageDestination -> [ImageType]
+ B9.DiskImageBuilder: preferredSourceImageTypes :: HasCallStack => ImageDestination -> [ImageType]
- B9.DiskImageBuilder: substImageTarget :: forall e. (Member EnvironmentReader e, Member ExcB9 e) => ImageTarget -> Eff e ImageTarget
+ B9.DiskImageBuilder: substImageTarget :: forall e. (HasCallStack, Member EnvironmentReader e, Member ExcB9 e) => ImageTarget -> Eff e ImageTarget
- B9.Shake.Actions: b9InvocationAction :: B9ConfigAction a -> B9ConfigOverride -> Action a
+ B9.Shake.Actions: b9InvocationAction :: HasCallStack => B9ConfigAction a -> B9ConfigOverride -> Action a
- B9.Shake.Actions: buildB9File :: FilePath -> FilePath -> [String] -> Action String
+ B9.Shake.Actions: buildB9File :: HasCallStack => FilePath -> FilePath -> [String] -> Action String
- B9.Shake.SharedImageRules: customSharedImageAction :: SharedImageName -> Action () -> Rules ()
+ B9.Shake.SharedImageRules: customSharedImageAction :: HasCallStack => SharedImageName -> Action () -> Rules ()
- B9.Shake.SharedImageRules: enableSharedImageRules :: B9ConfigOverride -> Rules ()
+ B9.Shake.SharedImageRules: enableSharedImageRules :: HasCallStack => B9ConfigOverride -> Rules ()
- B9.Shake.SharedImageRules: needSharedImage :: SharedImageName -> Action SharedImageBuildId
+ B9.Shake.SharedImageRules: needSharedImage :: HasCallStack => SharedImageName -> Action SharedImageBuildId

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # Changelog for B9 +## 0.5.68.2++* Fix a bug in the shared image Shake rule causing unnecessary rebuilds++* Rename the `buildDirRoot` string template environment variable to `buildDir`+ ## 0.5.68.1  * Fix positional argument enumeration in the `Enviromnent`; CLI arguments 
README.md view
@@ -1,9 +1,91 @@-# B9 - A Benign VM-Build Tool+# B9 - A Benign VM-Build Tool [![changelog](https://img.shields.io/badge/changelog-green.svg?style=flat)](CHANGELOG.md) -[![Build Status](https://travis-ci.org/sheyll/b9-vm-image-builder.svg?branch=0.5)](https://travis-ci.org/sheyll/b9-vm-image-builder) [![Hackage](https://img.shields.io/badge/hackage-B9-green.svg?style=flat)](http://hackage.haskell.org/package/b9) [![b9 LTS](http://stackage.org/package/b9/badge/lts)](http://stackage.org/lts/package/b9)+[![Build Status](https://travis-ci.org/sheyll/b9-vm-image-builder.svg?branch=0.5)](https://travis-ci.org/sheyll/b9-vm-image-builder) [![b9 LTS](http://stackage.org/package/b9/badge/lts)](http://stackage.org/lts/package/b9)[![Hackage](https://img.shields.io/badge/hackage-B9-green.svg?style=flat)](http://hackage.haskell.org/package/b9) +**An algebraic data type to describe virtual machine images generation.**++### Overview++There is a *DSL* that is described by a few benign Haskell `data` types that can +be `B9` can `read` and `show`. It can be put into a **text file** and be interpreted+by a **command line invokation**. ++Also, `B9` can be used as **Haskell library**. ++### Installation on NixOS++* As **command line utility** in current directory:++      $ nix-build -E 'import ((fetchTarball https://github.com/sheyll/b9-vm-image-builder/archive/0.5.tar.gz) + "/release.nix") {}'+    +  Now the executable `b9c` is in `./result/bin/`:++      $ result/bin/b9c      ++### Installation as Hackage Library with Stack++**Add a dependency to `b9` in your `.cabal` file.**    ++Update the `extra-deps` section in your `stack.yaml`:+    +    extra-deps:+    - b9-0.5.68.2++### Installation of custom version as Library with Stack ++**Instead** of adding to `extra-deps` one can use version **not on Hackage**.+Update the `packages` section in your `stack.yaml`:++    # Local packages, usually specified by relative directory name+    packages:+    +    - location:+        git: git@github.com:sheyll/b9-vm-image-builder.git+        tag: 0.5.68.2+        extra-dep: true++### Features++* Disk image creation+  * Growing shrinking of disk images and file systems+  * File system creation+  * Using existing Disk/File system as input+  * Libvirt/LXC based shell script execution+  * ISO/VFAT cloud-init images without Libvirt/LXC+  * **Artifact Caching and Sharing**+     * Shake rules+ * (Source-) File Creation+   * Example: Merge yaml config snippets, with template parameterers replaced into a text file+              included on the output image.    +   * text-files+   * structured text files with merging:+      * raw+      * yaml+      * erlang+      * Syntax trees for *objects* that can be converted to raw text, yaml or erlang+   * base64 encoded binaries+   * content fetched via HTTP+   * simple template support with `${variableName}`+* Repetition and Reuse++### Supported Formats++Supported Disk Images and File system types:++  * Cloud-init +    * ISOs+    * VFAT floppys+    * raw directories+  * VMDK files+  * QCOW images+  * RAW images +  * MBR partitioned disk images+  * EXT-4++### Old Readme+ Use B9 to compile your software into a deployable set of Linux-VM- or-configuration images, from a set of scripts and input files and templates+configuration images, from a set of scripts and input files and templates.  The main goal of this tool is to provide a build tool to increase automation and reduce redundancy when creating configured, ready-to-run VM-images.
b9.cabal view
@@ -1,6 +1,6 @@ cabal-version:     2.2 name:                b9-version:             0.5.68.1+version:             0.5.68.2  synopsis:            A tool and library for building virtual machine images. 
src/lib/B9/Artifact.hs view
@@ -1,4 +1,4 @@--- | Programmatic Interaface to b9 artifact generation.+-- | Programmatic Interface to b9 artifact generation. -- -- An extensible approach to vm deployment configuration management. --@@ -6,5 +6,20 @@ -- -- @since 1.0.0 module B9.Artifact () where+++---- | Build Environment+--disks = let+--  rootImg = loadSharedImage "prod-19.2" (Resize (GB 8))+--+--  dataImgWithContent =+--    let foo = "http://test.localdomain/data-foo.zip"+--        bar = "http://test.localdomain/data-bar.zip"+--        emptyImg = emptyExt4FileSystem "data" (GB 4)+--    in onFileSystem emptyImg $+--         directory "foo" $ do+--          unZipped (remoteBinary foo)+--          fileAttributes (\_f -> UnixFilePermissions 0 7 5 5 "root" "root")+--  
src/lib/B9/B9Config.hs view
@@ -91,6 +91,7 @@ import System.FilePath ((<.>)) import System.IO.B9Extras (SystemPath(..), ensureDir, resolve) import Text.Printf (printf)+import GHC.Stack  data ExecEnvType =   LibVirtLXC@@ -148,7 +149,7 @@ -- | Run a 'B9ConfigReader'. -- -- @since 0.5.65-runB9ConfigReader :: B9Config -> Eff (B9ConfigReader ': e) a -> Eff e a+runB9ConfigReader :: HasCallStack => B9Config -> Eff (B9ConfigReader ': e) a -> Eff e a runB9ConfigReader = runReader  -- | Return the runtime configuration, that should be the configuration merged@@ -265,7 +266,7 @@ type B9ConfigWriter = Writer (Semigroup.Endo B9Config)  -- | Add a modification to the permanent configuration file.-modifyPermanentConfig :: Member B9ConfigWriter e => Endo B9Config -> Eff e ()+modifyPermanentConfig :: (HasCallStack, Member B9ConfigWriter e) => Endo B9Config -> Eff e () modifyPermanentConfig = tell  -- | Execute a 'B9ConfigAction'.@@ -282,7 +283,7 @@ -- See also 'runB9ConfigAction', which does not need the 'B9ConfigOverride' parameter. -- -- @since 0.5.65-runB9ConfigActionWithOverrides :: B9ConfigAction a -> B9ConfigOverride -> IO a+runB9ConfigActionWithOverrides :: HasCallStack => B9ConfigAction a -> B9ConfigOverride -> IO a runB9ConfigActionWithOverrides act cfg = do   configuredCfgPath <- traverse resolve (cfg ^. customB9ConfigPath)   fallbackCfgPath <- resolve defaultB9ConfigFile@@ -320,13 +321,13 @@ -- See 'runB9ConfigActionWithOverrides' for more details. -- -- @since 0.5.65-runB9ConfigAction :: B9ConfigAction a -> IO a+runB9ConfigAction :: HasCallStack => B9ConfigAction a -> IO a runB9ConfigAction = flip runB9ConfigActionWithOverrides noB9ConfigOverride  -- | Open the configuration file that contains the 'B9Config'. -- If the configuration does not exist, write a default configuration file, -- and create a all missing directories.-openOrCreateB9Config :: MonadIO m => FilePath -> m CPDocument+openOrCreateB9Config :: (HasCallStack, MonadIO m) => FilePath -> m CPDocument openOrCreateB9Config cfgFile = do   ensureDir cfgFile   liftIO $ do@@ -341,7 +342,7 @@ -- | Write the configuration in the 'CPDocument' to either the user supplied -- configuration file path or to 'defaultB9ConfigFile'. -- Create all missing (parent) directories.-writeB9CPDocument :: MonadIO m => Maybe SystemPath -> CPDocument -> m ()+writeB9CPDocument :: (HasCallStack, MonadIO m) => Maybe SystemPath -> CPDocument -> m () writeB9CPDocument cfgFileIn cp = do   cfgFile <- resolve (fromMaybe defaultB9ConfigFile cfgFileIn)   ensureDir cfgFile@@ -408,7 +409,7 @@   return (mergeCP cp cp2)  -- | Append a config file section for the 'B9Config' to an empty 'CPDocument'.-b9ConfigToCPDocument :: B9Config -> Either CPError CPDocument+b9ConfigToCPDocument :: HasCallStack => B9Config -> Either CPError CPDocument b9ConfigToCPDocument c = do   cp1 <- addSectionCP emptyCP cfgFileSection   cp2 <- setShowCP cp1 cfgFileSection verbosityK (_verbosity c)@@ -423,10 +424,10 @@   cpFinal <- foldr (>=>) return (remoteRepoToCPDocument <$> _remoteRepos c) cpA   setShowCP cpFinal cfgFileSection repositoryK (_repository c) -readB9Config :: MonadIO m => Maybe SystemPath -> m CPDocument+readB9Config :: (HasCallStack, MonadIO m) => Maybe SystemPath -> m CPDocument readB9Config cfgFile = readCPDocument (fromMaybe defaultB9ConfigFile cfgFile) -parseB9Config :: CPDocument -> Either CPError B9Config+parseB9Config :: HasCallStack => CPDocument -> Either CPError B9Config parseB9Config cp =   let getr :: (CPGet a) => CPOptionSpec -> Either CPError a       getr = readCP cp cfgFileSection
src/lib/B9/B9Monad.hs view
@@ -14,6 +14,7 @@ import           B9.Repository import           Control.Eff import           Data.Functor                   ( )+import           GHC.Stack  -- | Definition of the B9 monad. See 'B9Eff'. --@@ -36,13 +37,13 @@ -- | A constraint that contains all effects of 'B9Eff' -- -- @since 0.5.65-type IsB9 e = (Lifted IO e, CommandIO e, B9Eff <:: e)+type IsB9 e = (HasCallStack, Lifted IO e, CommandIO e, B9Eff <:: e)  -- | Execute a 'B9' effect and return an action that needs -- the 'B9Config'. -- -- @since 0.5.65-runB9 :: B9 a -> B9ConfigAction a+runB9 :: HasCallStack => B9 a -> B9ConfigAction a runB9 action = do   cfg <- getB9Config   env <- askEnvironment
src/lib/B9/BuildInfo.hs view
@@ -34,6 +34,7 @@ import           System.Directory import           System.FilePath import           Text.Printf+import           GHC.Stack  -- | Build meta information. --@@ -52,8 +53,11 @@  -- | Create the build directories, generate (hash) the build-id and execute the given action. ----- Export the @projectRoot@ 'Environment' variable.+-- Bindings added to the text template parameter environment: --+-- * @projectRoot@ the directory that contains the sources of the project to build+-- * @buildDir@ the temporary directory used store the build artifacts passed into- or outof the build+-- -- Unless '_keepTempDirs' is @True@ clean up the build directories after the actions -- returns - even if the action throws a runtime exception. --@@ -65,6 +69,7 @@      , Member ExcB9 e      , Member EnvironmentReader e      , Member LoggerReader e+     , HasCallStack      )   => Eff (BuildInfoReader ': e) a   -> Eff e a@@ -113,7 +118,7 @@       traceL (printf "Project Root Directory: %s" rootD)       buildD <- getBuildDir       traceL (printf "Build Directory:        %s" buildD)-      r       <-  addLocalStringBinding ("buildDirRoot", buildD) action+      r       <-  addLocalStringBinding ("buildDir", buildD) action       tsAfter <- liftIO getCurrentTime       let duration = show (tsAfter `diffUTCTime` startTime)       infoL (printf "DURATION: %s" duration)
src/lib/B9/DiskImageBuilder.hs view
@@ -56,6 +56,7 @@ import           System.IO.Error                ( isDoesNotExistError ) import           Text.Printf                    ( printf ) import           Text.Show.Pretty               ( ppShow )+import           GHC.Stack  -- -- | Convert relative file paths of images, sources and mounted host directories -- -- to absolute paths relative to '_projectRoot'.@@ -68,7 +69,7 @@ -- | Replace $... variables inside an 'ImageTarget' substImageTarget   :: forall e-   . (Member EnvironmentReader e, Member ExcB9 e)+   . (HasCallStack, Member EnvironmentReader e, Member ExcB9 e)   => ImageTarget   -> Eff e ImageTarget substImageTarget = everywhereM gsubst@@ -124,7 +125,7 @@  -- | Return all supported source 'ImageType's compatible to a 'ImageDestinaion' -- in the preferred order.-preferredSourceImageTypes :: ImageDestination -> [ImageType]+preferredSourceImageTypes :: HasCallStack => ImageDestination -> [ImageType] preferredSourceImageTypes dest = case dest of   (Share _ fmt resize) ->     nub [fmt, Raw, QCow2, Vmdk] `intersect` allowedImageTypesForResize resize@@ -133,7 +134,7 @@   Transient -> [Raw, QCow2, Vmdk]   (LiveInstallerImage _name _repo _imgResize) -> [Raw] -allowedImageTypesForResize :: ImageResize -> [ImageType]+allowedImageTypesForResize :: HasCallStack => ImageResize -> [ImageType] allowedImageTypesForResize r = case r of   Resize _        -> [Raw]   ShrinkToMinimum -> [Raw]
src/lib/B9/Shake/Actions.hs view
@@ -8,18 +8,19 @@ import           B9 import           Control.Lens                   ( (?~) ) import           Development.Shake+import           GHC.Stack --- | Convert a 'B9Invocation' action into a Shake 'Action'. This is just+-- | Convert a 'B9ConfigAction' action into a Shake 'Action'. This is just -- an alias for 'runB9ConfigActionWithOverrides' since 'Action' is an instance of 'MonadIO' -- and 'runB9ConfigActionWithOverrides' work on any .-b9InvocationAction :: B9ConfigAction a -> B9ConfigOverride -> Action a+b9InvocationAction :: HasCallStack =>  B9ConfigAction a -> B9ConfigOverride -> Action a b9InvocationAction x y = liftIO (runB9ConfigActionWithOverrides x y)  -- | An action that does the equivalent of -- @b9c build -f <b9file> -- (args !! 0) (args !! 1) ... (args !! (length args - 1))@ -- with the current working directory changed to @b9Root@. -- The return value is the buildid, see 'getBuildId'-buildB9File :: FilePath -> FilePath -> [String] -> Action String+buildB9File :: HasCallStack => FilePath -> FilePath -> [String] -> Action String buildB9File b9Root b9File args = do   let f = b9Root </> b9File   need [f]
src/lib/B9/Shake/SharedImageRules.hs view
@@ -4,95 +4,82 @@   ( customSharedImageAction   , needSharedImage   , enableSharedImageRules-  )-where+  ) where -import           B9-import qualified Data.Binary                   as Binary-import qualified Data.ByteString               as ByteString-import qualified Data.ByteString.Builder       as Builder-import           Data.ByteString.Builder        ( stringUtf8 )-import qualified Data.ByteString.Lazy          as LazyByteString-import           Development.Shake-import           Development.Shake.Classes-import           Development.Shake.Rule+import B9+import qualified Data.Binary as Binary+import qualified Data.ByteString.Char8 as ByteString+import qualified Data.ByteString.Lazy.Char8 as LazyByteString+import Development.Shake+import Development.Shake.Classes+import Development.Shake.Rule+import GHC.Stack  -- | In order to use 'needSharedImage' and 'customSharedImageAction' you need to -- call this action before using any of the aforementioned 'Rules'.-enableSharedImageRules :: B9ConfigOverride -> Rules ()-enableSharedImageRules b9inv = addBuiltinRule noLint sharedImageIdentity go- where-  sharedImageIdentity :: BuiltinIdentity SharedImageName SharedImageBuildId-  sharedImageIdentity (SharedImageName k) (SharedImageBuildId v) = Just-    (LazyByteString.toStrict-      (Builder.toLazyByteString (stringUtf8 k <> stringUtf8 v))-    )-  go :: BuiltinRun SharedImageName SharedImageBuildId-  go nameQ mOldBIdBinary dependenciesChanged = do-    mCurrentBId <- getImgBuildId-    case mCurrentBId of-      Just currentBId ->-        let currentBIdBinary = encodeBuildId currentBId-        in  if dependenciesChanged-                 == RunDependenciesChanged-                 && mOldBIdBinary-                 == Just currentBIdBinary-              then return $ RunResult ChangedNothing currentBIdBinary currentBId-              else rebuild (Just currentBIdBinary)-      _ -> rebuild Nothing-   where-    getImgBuildId = liftIO-      (runB9ConfigActionWithOverrides (runLookupLocalSharedImage nameQ) b9inv)-    encodeBuildId :: SharedImageBuildId -> ByteString.ByteString-    encodeBuildId = LazyByteString.toStrict . Binary.encode-    rebuild-      :: Maybe ByteString.ByteString -> Action (RunResult SharedImageBuildId)-    rebuild mCurrentBIdBinary = do-      (_, act) <- getUserRuleOne nameQ (const Nothing) imgMatch-      act b9inv-      mNewBId <- getImgBuildId-      newBId  <- maybe-        (error-          (  "failed to get SharedImageBuildId for "-          ++ show nameQ-          ++ " in context of "-          ++ show b9inv-          )-        )-        return-        mNewBId-      let newBIdBinary = encodeBuildId newBId-      let change = if Just newBIdBinary == mCurrentBIdBinary-            then ChangedRecomputeSame-            else ChangedRecomputeDiff-      return $ RunResult change newBIdBinary newBId-     where-      imgMatch (SharedImageCustomActionRule name mkImage) =-        if name == nameQ then Just mkImage else Nothing+enableSharedImageRules :: HasCallStack => B9ConfigOverride -> Rules ()+enableSharedImageRules b9inv = addBuiltinRule noLint noIdentity go+  where+    go :: BuiltinRun SharedImageName SharedImageBuildId+    go nameQ mOldBIdBinary dependenciesChanged = do+      mCurrentBId <- getImgBuildId+      let mCurrentBIdBinary = encodeBuildId <$> mCurrentBId+      putLoud $+        "share image rule for: " +++        show nameQ +++        ". Deps: " +++        show dependenciesChanged +++        ", current BId: " +++        show mCurrentBId ++ " Binary: " ++ show mCurrentBIdBinary ++ ", old BId: " ++ show mOldBIdBinary+      case mCurrentBIdBinary of+        Just currentBIdBinary ->+          if dependenciesChanged == RunDependenciesSame && mOldBIdBinary == Just currentBIdBinary+            then return $ RunResult ChangedNothing currentBIdBinary (fromJust mCurrentBId)+            else rebuild (Just currentBIdBinary)+        Nothing -> rebuild Nothing+      where+        getImgBuildId = liftIO (runB9ConfigActionWithOverrides (runLookupLocalSharedImage nameQ) b9inv)+        encodeBuildId :: SharedImageBuildId -> ByteString.ByteString+        encodeBuildId = LazyByteString.toStrict . Binary.encode+        rebuild :: Maybe ByteString.ByteString -> Action (RunResult SharedImageBuildId)+        rebuild mCurrentBIdBinary = do+          (_, act) <- getUserRuleOne nameQ (const Nothing) imgMatch+          act b9inv+          mNewBId <- getImgBuildId+          newBId <-+            maybe+              (error ("failed to get SharedImageBuildId for " ++ show nameQ ++ " in context of " ++ show b9inv))+              return+              mNewBId+          let newBIdBinary = encodeBuildId newBId+          let change =+                if Just newBIdBinary == mCurrentBIdBinary+                  then ChangedRecomputeSame+                  else ChangedRecomputeDiff+          return $ RunResult change newBIdBinary newBId+          where+            imgMatch (SharedImageCustomActionRule name mkImage) =+              if name == nameQ+                then Just mkImage+                else Nothing  -- | Add a dependency to the creation of a 'SharedImage'. The build action -- for the shared image must have been supplied by e.g. 'customSharedImageAction'. -- NOTE: You must call 'enableSharedImageRules' before this action works.-needSharedImage :: SharedImageName -> Action SharedImageBuildId+needSharedImage :: HasCallStack => SharedImageName -> Action SharedImageBuildId needSharedImage = apply1  -- | Specify an arbitrary action that is supposed to build the given shared -- image identified by a 'SharedImageName'. -- NOTE: You must call 'enableSharedImageRules' before this action works.-customSharedImageAction :: SharedImageName -> Action () -> Rules ()-customSharedImageAction b9img customAction = addUserRule-  (SharedImageCustomActionRule b9img customAction')- where-  customAction' b9inv = do-    customAction-    mCurrentBuildId <- liftIO-      (runB9ConfigActionWithOverrides (runLookupLocalSharedImage b9img) b9inv)-    putLoud-      (printf "Finished custom action, for %s, build-id is: %s"-              (show b9img)-              (show mCurrentBuildId)-      )-    maybe (errorSharedImageNotFound b9img) return mCurrentBuildId+customSharedImageAction :: HasCallStack => SharedImageName -> Action () -> Rules ()+customSharedImageAction b9img customAction = addUserRule (SharedImageCustomActionRule b9img customAction')+  where+    customAction' b9inv = do+      customAction+      mCurrentBuildId <- liftIO (runB9ConfigActionWithOverrides (runLookupLocalSharedImage b9img) b9inv)+      putLoud (printf "Finished custom action, for %s, build-id is: %s" (show b9img) (show mCurrentBuildId))+      maybe (errorSharedImageNotFound b9img) return mCurrentBuildId  type instance RuleResult SharedImageName = SharedImageBuildId @@ -101,5 +88,5 @@                               (B9ConfigOverride -> Action SharedImageBuildId)   deriving (Typeable) -errorSharedImageNotFound :: Monad m => SharedImageName -> m a+errorSharedImageNotFound :: (HasCallStack, Monad m) => SharedImageName -> m a errorSharedImageNotFound = fail . printf "Error: %s not found." . show