language-dockerfile-0.2.3.0: src/Language/Dockerfile/EDSL.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
module Language.Dockerfile.EDSL
where
-- import Control.Exception
-- import Data.List (isInfixOf)
import Control.Monad.Free
import Control.Monad.Free.TH
import Control.Monad.Writer
import Data.ByteString (ByteString)
import qualified Language.Dockerfile.PrettyPrint as PrettyPrint
import qualified Language.Dockerfile.Syntax as Syntax
import Language.Dockerfile.EDSL.Types
type EInstructionM = Free EInstruction
makeFree ''EInstruction
runDockerWriter
:: (MonadWriter [Syntax.Instruction] m)
=> EInstructionM a -> m a
runDockerWriter = iterM runD
where
runDef f a n = tell [ f a ] >> n
runDef2 f a b n = tell [ f a b ] >> n
runD (From bi n) = case bi of
EUntaggedImage bi' -> runDef Syntax.From (Syntax.UntaggedImage bi') n
ETaggedImage bi' tg -> runDef Syntax.From (Syntax.TaggedImage bi' tg) n
EDigestedImage bi' d -> runDef Syntax.From (Syntax.DigestedImage bi' d) n
runD (CmdArgs as n) = runDef Syntax.Cmd as n
runD (Add s d n) = runDef2 Syntax.Add s d n
runD (User u n) = runDef Syntax.User u n
runD (Label ps n) = runDef Syntax.Label ps n
runD (StopSignal s n) = runDef Syntax.Stopsignal s n
runD (Copy s d n) = runDef2 Syntax.Copy s d n
runD (RunArgs as n) = runDef Syntax.Run as n
runD (Workdir d n) = runDef Syntax.Workdir d n
runD (Expose ps n) = runDef Syntax.Expose ps n
runD (Volume v n) = runDef Syntax.Volume v n
runD (EntrypointArgs e n) = runDef Syntax.Entrypoint e n
runD (Maintainer m n) = runDef Syntax.Maintainer m n
runD (Env ps n) = runDef Syntax.Env ps n
runD (Arg s n) = runDef Syntax.Arg s n
runD (Comment c n) = runDef Syntax.Comment c n
runD (OnBuildRaw i n) = runDef Syntax.OnBuild i n
runD (Embed is n) = do
tell (map Syntax.instruction is)
n
instructionPos :: Syntax.Instruction -> Syntax.InstructionPos
instructionPos i = Syntax.InstructionPos i "" 0
-- | Runs the Dockerfile EDSL and returns a 'Dockerfile' you can pretty print
-- or manipulate
toDockerfile :: EInstructionM a -> Syntax.Dockerfile
toDockerfile e =
let (_, w) = runWriter (runDockerWriter e)
in map instructionPos w
-- | Runs the Dockerfile EDSL and returns a 'String' using
-- 'Language.Dockerfile.PrettyPrint'
--
-- @
-- import Language.Dockerfile
--
-- main :: IO ()
-- main = writeFile "something.dockerfile" $ toDockerfileStr $ do
-- from (tagged "fpco/stack-build" "lts-6.9")
-- add "." "/app/language-dockerfile"
-- workdir "/app/language-dockerfile"
-- run (words "stack build --test --only-dependencies")
-- cmd (words "stack test")
-- @
toDockerfileStr :: EInstructionM a -> String
toDockerfileStr = PrettyPrint.prettyPrint . toDockerfile
untagged :: String -> EBaseImage
untagged = EUntaggedImage
tagged :: String -> String -> EBaseImage
tagged = ETaggedImage
digested :: String -> ByteString -> EBaseImage
digested = EDigestedImage
run :: MonadFree EInstruction m => String -> m ()
run = runArgs . words
entrypoint :: MonadFree EInstruction m => String -> m ()
entrypoint = entrypointArgs . words
cmd :: MonadFree EInstruction m => String -> m ()
cmd = cmdArgs . words
-- | ONBUILD Dockerfile instruction
--
-- Each nested instruction gets emitted as a separate @ONBUILD@ block
--
-- @
-- 'toDockerfile' $ do
-- from "node"
-- run "apt-get update"
-- onBuild $ do
-- run "echo more-stuff"
-- run "echo here"
-- @
onBuild
:: MonadFree EInstruction m
=> EInstructionM a
-> m ()
onBuild b = mapM_ (onBuildRaw . Syntax.instruction) (toDockerfile b)
-- dockerIgnore :: String -> IO ()
-- dockerIgnore str = do
-- let str' = str ++ "\n"
-- -- TODO - Use projectroot
-- di <- readFile "./.dockerignore" `catch` (\(SomeException _) -> return "")
-- unless (str' `isInfixOf` di) $
-- appendFile "./.dockerignore" str'
-- dockerBuild :: FilePath -> IO ()
-- dockerBuild _dir = undefined