{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} -- Module : Main -- Copyright : (c) 2013-2014 Brendan Hay -- License : This Source Code Form is subject to the terms of -- the Mozilla Public License, v. 2.0. -- A copy of the MPL can be found in the LICENSE file or -- you can obtain it at http://mozilla.org/MPL/2.0/. -- Maintainer : Brendan Hay -- Stability : experimental -- Portability : non-portable (GHC extensions) module Main ( main ) where import Control.Applicative import Control.Arrow import Control.Monad import qualified Data.ByteString.Char8 as BS import Data.List import Data.Monoid import Data.Text (Text) import qualified Data.Text as Text import qualified Data.Text.Encoding as Text import Keiretsu.Config import Keiretsu.Log import Keiretsu.Process import Keiretsu.Types import Options.Applicative import System.Directory import System.Environment import System.Exit data Start = Start { sDir :: !FilePath , sEnvs :: [FilePath] , sRuns :: [Text] , sDelay :: !Int , sExclude :: [Text] , sPorts :: !Int , sDryRun :: !Bool , sDebug :: !Bool } start :: Parser Start start = Start <$> strOption ( long "dir" <> short 'd' <> metavar "DIR" <> value "./" <> help "Path to the directory containing the root Intfile. (default: ./)" ) <*> many (strOption ( long "env" <> short 'e' <> metavar "FILE" <> help "Additional .env files to merge into the environment. (default: none)" )) <*> many (textOption ( long "run" <> short 'r' <> metavar "CMD" <> help "Additional commands to run in the environment. (default: none)" )) <*> option auto ( long "delay" <> short 'n' <> metavar "MS" <> value 1000 <> help "Millisecond delay between run commands start. (default: 1000)" ) <*> many (textOption ( long "exclude" <> short 'x' <> metavar "PROC" <> help "Prefixed name of a proctype to exclude. (default: none)" )) <*> option auto ( long "ports" <> short 'p' <> metavar "INT" <> value 2 <> help "Number of ports to allocate to a single proctype. (default: 2)" ) <*> switch ( long "dry-run" <> help "Execute the orchestration plan without starting any processes. (default: false)" ) <*> switch ( long "debug" <> help "Show debug output. (default: false)" ) main :: IO () main = do s@Start{..} <- customExecParser (prefs $ showHelpOnError <> columns 100) (info start idm) setLogging True check s l <- depLocal ds <- (l :) <$> dependencies sDir ps <- nub . excludeProcs sExclude . concat <$> mapM (proctypes sPorts) ds pe <- environment ps sEnvs le <- (pe ++) . map (Text.pack *** Text.pack) <$> getEnvironment let cs = map (setLocalEnv pe) ps rs = zipWith (procLocal l le sDelay) [1..] sRuns procs = cs ++ excludeProcs sExclude rs when sDebug $ dump procs unless sDryRun $ run procs check :: Start -> IO () check Start{..} = do when (sPorts < 0) $ throwError "--ports must be greater-than 0." when (null sDir) $ throwError "--dir must be specified." d <- doesDirectoryExist sDir unless d . throwError $ "Directory " ++ sDir ++ " specified by --dir doesn't exist." forM_ sEnvs $ \e -> do f <- doesFileExist e unless f . throwError $ "File " ++ e ++ " specified by --env doesn't exist." throwError :: String -> IO () throwError s = logError s >> exitFailure dump :: [Proc] -> IO () dump = zipWithM_ (\c -> mapM_ BS.putStrLn . format c) colours where format c Proc{..} = map (colourise c procPrefix . Text.encodeUtf8) $ "command: " <> procCmd : "delay: " <> Text.pack (show procDelay ++ "ms") : maybe [] (\x -> ["check: " <> x]) procCheck ++ map f procEnv f (k, v) = k <> ": " <> v textOption :: Mod OptionFields String -> Parser Text textOption = fmap Text.pack . strOption