hapistrano-0.4.5.0: src/System/Hapistrano/Types.hs
-- |
-- Module : System.Hapistrano.Types
-- Copyright : © 2015-Present Stack Builders
-- License : MIT
--
-- Stability : experimental
-- Portability : portable
--
-- Type definitions for the Hapistrano tool.
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module System.Hapistrano.Types
( Hapistrano
, Failure(..)
, Config(..)
, Source(..)
, Task(..)
, ReleaseFormat(..)
, SshOptions(..)
, OutputDest(..)
, Release
, TargetSystem(..)
, DeployState(..)
, Shell(..)
, Opts(..)
, Command(..)
, MaintenanceOptions(..)
-- * Types helpers
, mkRelease
, releaseTime
, renderRelease
, parseRelease
, fromMaybeReleaseFormat
, fromMaybeKeepReleases
, toMaybePath
) where
import Control.Applicative
import Control.Monad.Except
import Control.Monad.Reader
import Data.Aeson
import Data.Maybe
import Data.Time
import Numeric.Natural
import Path
-- | Hapistrano monad.
type Hapistrano a = ExceptT (Failure, Maybe Release) (ReaderT Config IO) a
-- | Failure with status code and a message.
data Failure =
Failure Int (Maybe String)
-- | Hapistrano configuration options.
data Config =
Config
{ configSshOptions :: !(Maybe SshOptions)
-- ^ 'Nothing' if we are running locally, or SSH options to use.
, configShellOptions :: !Shell
-- ^ One of the supported 'Shell's
, configPrint :: !(OutputDest -> String -> IO ())
-- ^ How to print messages
}
-- | The source of the repository. It can be from a version control provider
-- like GitHub or a local directory.
data Source
= GitRepository
{ gitRepositoryURL :: String
-- ^ The URL of remote Git repository to deploy
, gitRepositoryRevision :: String
-- ^ The SHA1 or branch to release
}
| LocalDirectory
{ localDirectoryPath :: Path Abs Dir
-- ^ The local repository to deploy
}
deriving (Eq, Ord, Show)
-- | The records describes deployment task.
data Task =
Task
{ taskDeployPath :: Path Abs Dir
-- ^ The root of the deploy target on the remote host
, taskSource :: Source
-- ^ The 'Source' to deploy
, taskReleaseFormat :: ReleaseFormat
-- ^ The 'ReleaseFormat' to use
}
deriving (Show, Eq, Ord)
-- | Release format mode.
data ReleaseFormat
= ReleaseShort -- ^ Standard release path following Capistrano's format
| ReleaseLong -- ^ Long release path including picoseconds
deriving (Show, Read, Eq, Ord, Enum, Bounded)
instance FromJSON ReleaseFormat where
parseJSON =
withText "release format" $ \case
"short" -> return ReleaseShort
"long" -> return ReleaseLong
_ -> fail "expected 'short' or 'long'"
-- | Current shells supported.
data Shell
= Bash
| Zsh
deriving (Show, Eq, Ord)
instance FromJSON Shell where
parseJSON =
withText "shell" $ \case
"bash" -> return Bash
"zsh" -> return Zsh
_ -> fail "supported shells: 'bash' or 'zsh'"
-- | SSH options.
data SshOptions =
SshOptions
{ sshHost :: String -- ^ Host to use
, sshPort :: Word -- ^ Port to use
, sshArgs :: [String] -- ^ Arguments for ssh
}
deriving (Show, Read, Eq, Ord)
-- | Output destination.
data OutputDest
= StdoutDest
| StderrDest
deriving (Eq, Show, Read, Ord, Bounded, Enum)
-- | Release indentifier.
data Release =
Release ReleaseFormat UTCTime
deriving (Eq, Show, Ord)
-- | Target's system where application will be deployed.
data TargetSystem
= GNULinux
| BSD
deriving (Eq, Show, Read, Ord, Bounded, Enum)
-- | State of the deployment after running @hap deploy@.
-- __note:__ the 'Unknown' value is not intended to be
-- written to the @.hapistrano_deploy_state@ file; instead,
-- it's intended to represent whenever Hapistrano couldn't
-- get the information on the deployment state (e.g. the file is not present).
data DeployState
= Fail
| Success
| Unknown
deriving (Eq, Show, Read, Ord, Bounded, Enum)
-- | Maintenance options
data MaintenanceOptions = Enable | Disable
-- | Command line options.
data Opts = Opts
{ optsCommand :: Command
, optsConfigFile :: FilePath
}
-- | Command to execute and command-specific options.
data Command
= Deploy (Maybe ReleaseFormat) (Maybe Natural) Bool -- ^ Deploy a new release (with timestamp
-- format, how many releases to keep, and whether the failed releases except the latest one
-- get deleted or not)
| Rollback Natural -- ^ Rollback to Nth previous release
| Maintenance MaintenanceOptions
-- | Create a 'Release' indentifier.
mkRelease :: ReleaseFormat -> UTCTime -> Release
mkRelease = Release
-- | Extract deployment time from 'Release'.
releaseTime :: Release -> UTCTime
releaseTime (Release _ time) = time
-- | Render 'Release' indentifier as a 'String'.
renderRelease :: Release -> String
renderRelease (Release rfmt time) = formatTime defaultTimeLocale fmt time
where
fmt =
case rfmt of
ReleaseShort -> releaseFormatShort
ReleaseLong -> releaseFormatLong
----------------------------------------------------------------------------
-- Types helpers
-- | Parse 'Release' identifier from a 'String'.
parseRelease :: String -> Maybe Release
parseRelease s =
(Release ReleaseLong <$> p releaseFormatLong s) <|>
(Release ReleaseShort <$> p releaseFormatShort s)
where
p = parseTimeM False defaultTimeLocale
releaseFormatShort, releaseFormatLong :: String
releaseFormatShort = "%Y%m%d%H%M%S"
releaseFormatLong = "%Y%m%d%H%M%S%q"
-- | Get release format based on the CLI and file configuration values.
fromMaybeReleaseFormat ::
Maybe ReleaseFormat -> Maybe ReleaseFormat -> ReleaseFormat
fromMaybeReleaseFormat cliRF configRF =
fromMaybe ReleaseShort (cliRF <|> configRF)
-- | Get keep releases based on the CLI and file configuration values.
fromMaybeKeepReleases :: Maybe Natural -> Maybe Natural -> Natural
fromMaybeKeepReleases cliKR configKR =
fromMaybe defaultKeepReleases (cliKR <|> configKR)
defaultKeepReleases :: Natural
defaultKeepReleases = 5
-- | Get the local path to copy from the 'Source' configuration value.
toMaybePath :: Source -> Maybe (Path Abs Dir)
toMaybePath (LocalDirectory path) = Just path
toMaybePath _ = Nothing