mirror of
https://github.com/stackbuilders/hapistrano.git
synced 2024-12-23 03:21:51 +03:00
Copy directory instead of cloning an entire repository (#135)
* Copy directory instead of cloning an entire repository * Improve haddock of types * Rename new ADT to Source * Add tests for new configuration * Update changelog and readme * Addres PR comments * Updated changelog and hapistrano's version Co-authored-by: Juan Paucar <jpaucar@stackbuilders.com>
This commit is contained in:
parent
b321391410
commit
a6183c01fb
@ -1,4 +1,7 @@
|
|||||||
## master
|
## 0.4.0.0
|
||||||
|
### Added
|
||||||
|
* Copy a directory's contents with `local_directory` instead of using _git_ with `repo` and `revision`.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
* Update upper bounds for `path` and `path-io` packages.
|
* Update upper bounds for `path` and `path-io` packages.
|
||||||
|
|
||||||
|
15
README.md
15
README.md
@ -32,15 +32,18 @@ filesystem and deletes previous releases to avoid filling up the disk.
|
|||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Hapistrano 0.3.0.0 looks for a configuration file called `hap.yaml` that
|
Hapistrano 0.4.0.0 looks for a configuration file called `hap.yaml` that
|
||||||
typically looks like this:
|
typically looks like this:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
deploy_path: '/var/projects/my-project'
|
deploy_path: '/var/projects/my-project'
|
||||||
host: myserver.com
|
host: myserver.com
|
||||||
port: 2222
|
port: 2222
|
||||||
|
# To perform version control operations
|
||||||
repo: 'https://github.com/stackbuilders/hapistrano.git'
|
repo: 'https://github.com/stackbuilders/hapistrano.git'
|
||||||
revision: origin/master
|
revision: origin/master
|
||||||
|
# To copy the contents of the directory
|
||||||
|
local_directory: '/tmp/my-project'
|
||||||
build_script:
|
build_script:
|
||||||
- stack setup
|
- stack setup
|
||||||
- stack build
|
- stack build
|
||||||
@ -50,10 +53,16 @@ restart_command: systemd restart my-app-service
|
|||||||
The following parameters are required:
|
The following parameters are required:
|
||||||
|
|
||||||
* `deploy_path` — the root of the deploy target on the remote host.
|
* `deploy_path` — the root of the deploy target on the remote host.
|
||||||
* `repo` — the origin repository.
|
* Related to the `source` of the repository, you have the following options:
|
||||||
* `revision` — the SHA1 or branch to deploy. If a branch, you will need to
|
- _Git repository_ **default** — consists of two parameters. When these are set,
|
||||||
|
hapistrano will perform version control related operations.
|
||||||
|
**Note:** Only GitHub is supported.
|
||||||
|
* `repo` — the origin repository.
|
||||||
|
* `revision` — the SHA1 or branch to deploy. If a branch, you will need to
|
||||||
specify it as `origin/branch_name` due to the way that the cache repo is
|
specify it as `origin/branch_name` due to the way that the cache repo is
|
||||||
configured.
|
configured.
|
||||||
|
* `local_directory` — when this parameter is set, hapistrano will copy the
|
||||||
|
contents of the directory.
|
||||||
|
|
||||||
The following parameters are *optional*:
|
The following parameters are *optional*:
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
module Main (main) where
|
module Main (main) where
|
||||||
|
|
||||||
import qualified Config as C
|
|
||||||
import Control.Concurrent.Async
|
import Control.Concurrent.Async
|
||||||
import Control.Concurrent.STM
|
import Control.Concurrent.STM
|
||||||
import Control.Monad
|
import Control.Monad
|
||||||
@ -21,6 +20,7 @@ import Paths_hapistrano (version)
|
|||||||
import System.Exit
|
import System.Exit
|
||||||
import qualified System.Hapistrano as Hap
|
import qualified System.Hapistrano as Hap
|
||||||
import qualified System.Hapistrano.Commands as Hap
|
import qualified System.Hapistrano.Commands as Hap
|
||||||
|
import qualified System.Hapistrano.Config as C
|
||||||
import qualified System.Hapistrano.Core as Hap
|
import qualified System.Hapistrano.Core as Hap
|
||||||
import System.Hapistrano.Types
|
import System.Hapistrano.Types
|
||||||
import System.IO
|
import System.IO
|
||||||
@ -125,8 +125,7 @@ main = do
|
|||||||
C.Config{..} <- Yaml.loadYamlSettings [optsConfigFile] [] Yaml.useEnv
|
C.Config{..} <- Yaml.loadYamlSettings [optsConfigFile] [] Yaml.useEnv
|
||||||
chan <- newTChanIO
|
chan <- newTChanIO
|
||||||
let task rf = Task { taskDeployPath = configDeployPath
|
let task rf = Task { taskDeployPath = configDeployPath
|
||||||
, taskRepository = configRepo
|
, taskSource = configSource
|
||||||
, taskRevision = configRevision
|
|
||||||
, taskReleaseFormat = rf }
|
, taskReleaseFormat = rf }
|
||||||
let printFnc dest str = atomically $
|
let printFnc dest str = atomically $
|
||||||
writeTChan chan (PrintMsg dest str)
|
writeTChan chan (PrintMsg dest str)
|
||||||
@ -141,6 +140,8 @@ main = do
|
|||||||
then Hap.pushRelease (task releaseFormat)
|
then Hap.pushRelease (task releaseFormat)
|
||||||
else Hap.pushReleaseWithoutVc (task releaseFormat)
|
else Hap.pushReleaseWithoutVc (task releaseFormat)
|
||||||
rpath <- Hap.releasePath configDeployPath release
|
rpath <- Hap.releasePath configDeployPath release
|
||||||
|
forM_ (toMaybePath configSource) $ \src ->
|
||||||
|
Hap.scpDir src rpath
|
||||||
forM_ configCopyFiles $ \(C.CopyThing src dest) -> do
|
forM_ configCopyFiles $ \(C.CopyThing src dest) -> do
|
||||||
srcPath <- resolveFile' src
|
srcPath <- resolveFile' src
|
||||||
destPath <- parseRelFile dest
|
destPath <- parseRelFile dest
|
||||||
|
4
fixtures/git_repository_config.yaml
Normal file
4
fixtures/git_repository_config.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
deploy_path: '/'
|
||||||
|
host: www.example.com
|
||||||
|
repo: 'my-repo'
|
||||||
|
revision: 'my-revision'
|
3
fixtures/local_directory_config.yaml
Normal file
3
fixtures/local_directory_config.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
deploy_path: '/'
|
||||||
|
host: www.example.com
|
||||||
|
local_directory: '/'
|
@ -1,5 +1,5 @@
|
|||||||
name: hapistrano
|
name: hapistrano
|
||||||
version: 0.3.10.1
|
version: 0.4.0.0
|
||||||
synopsis: A deployment library for Haskell applications
|
synopsis: A deployment library for Haskell applications
|
||||||
description:
|
description:
|
||||||
.
|
.
|
||||||
@ -48,6 +48,7 @@ library
|
|||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
exposed-modules: System.Hapistrano
|
exposed-modules: System.Hapistrano
|
||||||
, System.Hapistrano.Commands
|
, System.Hapistrano.Commands
|
||||||
|
, System.Hapistrano.Config
|
||||||
, System.Hapistrano.Core
|
, System.Hapistrano.Core
|
||||||
, System.Hapistrano.Types
|
, System.Hapistrano.Types
|
||||||
, System.Hapistrano.Commands.Internal
|
, System.Hapistrano.Commands.Internal
|
||||||
@ -64,6 +65,7 @@ library
|
|||||||
, typed-process >= 0.2 && < 0.3
|
, typed-process >= 0.2 && < 0.3
|
||||||
, time >= 1.5 && < 1.10
|
, time >= 1.5 && < 1.10
|
||||||
, transformers >= 0.4 && < 0.6
|
, transformers >= 0.4 && < 0.6
|
||||||
|
, yaml >= 0.8.16 && < 0.12
|
||||||
if flag(dev)
|
if flag(dev)
|
||||||
ghc-options: -Wall -Werror
|
ghc-options: -Wall -Werror
|
||||||
else
|
else
|
||||||
@ -73,8 +75,7 @@ library
|
|||||||
executable hap
|
executable hap
|
||||||
hs-source-dirs: app
|
hs-source-dirs: app
|
||||||
main-is: Main.hs
|
main-is: Main.hs
|
||||||
other-modules: Config
|
other-modules: Paths_hapistrano
|
||||||
, Paths_hapistrano
|
|
||||||
build-depends: aeson >= 0.11 && < 1.5
|
build-depends: aeson >= 0.11 && < 1.5
|
||||||
, async >= 2.0.1.6 && < 2.4
|
, async >= 2.0.1.6 && < 2.4
|
||||||
, base >= 4.8 && < 5.0
|
, base >= 4.8 && < 5.0
|
||||||
@ -100,6 +101,7 @@ test-suite test
|
|||||||
hs-source-dirs: spec
|
hs-source-dirs: spec
|
||||||
main-is: Spec.hs
|
main-is: Spec.hs
|
||||||
other-modules: System.HapistranoSpec
|
other-modules: System.HapistranoSpec
|
||||||
|
, System.HapistranoConfigSpec
|
||||||
, System.HapistranoPropsSpec
|
, System.HapistranoPropsSpec
|
||||||
build-depends: base >= 4.8 && < 5.0
|
build-depends: base >= 4.8 && < 5.0
|
||||||
, directory >= 1.2.5 && < 1.4
|
, directory >= 1.2.5 && < 1.4
|
||||||
@ -113,6 +115,7 @@ test-suite test
|
|||||||
, QuickCheck >= 2.5.1 && < 3.0
|
, QuickCheck >= 2.5.1 && < 3.0
|
||||||
, silently >= 1.2 && < 1.3
|
, silently >= 1.2 && < 1.3
|
||||||
, temporary >= 1.1 && < 1.4
|
, temporary >= 1.1 && < 1.4
|
||||||
|
, yaml >= 0.8.16 && < 0.12
|
||||||
build-tools: hspec-discover >= 2.0 && < 3.0
|
build-tools: hspec-discover >= 2.0 && < 3.0
|
||||||
|
|
||||||
if flag(dev)
|
if flag(dev)
|
||||||
|
59
spec/System/HapistranoConfigSpec.hs
Normal file
59
spec/System/HapistranoConfigSpec.hs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
{-# LANGUAGE TemplateHaskell #-}
|
||||||
|
module System.HapistranoConfigSpec
|
||||||
|
( spec
|
||||||
|
) where
|
||||||
|
|
||||||
|
import System.Hapistrano.Config (Config (..), Target (..))
|
||||||
|
import System.Hapistrano.Types (Shell (..),
|
||||||
|
Source (..), TargetSystem (..))
|
||||||
|
|
||||||
|
import qualified Data.Yaml.Config as Yaml
|
||||||
|
import Path (mkAbsDir)
|
||||||
|
import Test.Hspec
|
||||||
|
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec =
|
||||||
|
describe "Hapistrano's configuration file" $ do
|
||||||
|
context "when the key 'local-repository' is present" $
|
||||||
|
it "loads LocalRepository as the configuration's source" $
|
||||||
|
Yaml.loadYamlSettings ["fixtures/local_directory_config.yaml"] [] Yaml.useEnv
|
||||||
|
>>=
|
||||||
|
(`shouldBe`
|
||||||
|
(defaultConfiguration
|
||||||
|
{ configSource = LocalDirectory { localDirectoryPath = $(mkAbsDir "/") } }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
context "when the keys 'repo' and 'revision' are present" $
|
||||||
|
it "loads GitRepository as the configuration's source" $
|
||||||
|
Yaml.loadYamlSettings ["fixtures/git_repository_config.yaml"] [] Yaml.useEnv
|
||||||
|
>>= (`shouldBe` defaultConfiguration)
|
||||||
|
|
||||||
|
|
||||||
|
defaultConfiguration :: Config
|
||||||
|
defaultConfiguration =
|
||||||
|
Config
|
||||||
|
{ configDeployPath = $(mkAbsDir "/")
|
||||||
|
, configHosts =
|
||||||
|
[ Target
|
||||||
|
{ targetHost = "www.example.com"
|
||||||
|
, targetPort = 22
|
||||||
|
, targetShell = Bash
|
||||||
|
, targetSshArgs = []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
, configSource = GitRepository "my-repo" "my-revision"
|
||||||
|
, configRestartCommand = Nothing
|
||||||
|
, configBuildScript = Nothing
|
||||||
|
, configCopyFiles = []
|
||||||
|
, configCopyDirs = []
|
||||||
|
, configLinkedFiles = []
|
||||||
|
, configLinkedDirs = []
|
||||||
|
, configVcAction = True
|
||||||
|
, configRunLocally = Nothing
|
||||||
|
, configTargetSystem = GNULinux
|
||||||
|
, configReleaseFormat = Nothing
|
||||||
|
, configKeepReleases = Nothing
|
||||||
|
}
|
@ -361,8 +361,11 @@ mkTaskWithCustomRevision :: Path Abs Dir -> Path Abs Dir -> String -> Task
|
|||||||
mkTaskWithCustomRevision deployPath repoPath revision =
|
mkTaskWithCustomRevision deployPath repoPath revision =
|
||||||
Task
|
Task
|
||||||
{ taskDeployPath = deployPath
|
{ taskDeployPath = deployPath
|
||||||
, taskRepository = fromAbsDir repoPath
|
, taskSource =
|
||||||
, taskRevision = revision
|
GitRepository
|
||||||
|
{ gitRepositoryURL = fromAbsDir repoPath
|
||||||
|
, gitRepositoryRevision = revision
|
||||||
|
}
|
||||||
, taskReleaseFormat = ReleaseLong
|
, taskReleaseFormat = ReleaseLong
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,11 +55,18 @@ import System.Hapistrano.Types
|
|||||||
pushRelease :: Task -> Hapistrano Release
|
pushRelease :: Task -> Hapistrano Release
|
||||||
pushRelease Task {..} = do
|
pushRelease Task {..} = do
|
||||||
setupDirs taskDeployPath
|
setupDirs taskDeployPath
|
||||||
ensureCacheInPlace taskRepository taskDeployPath
|
pushReleaseForRepository taskSource
|
||||||
|
where
|
||||||
|
-- When the configuration is set for a local directory, it will only create
|
||||||
|
-- the release directory without any version control operations.
|
||||||
|
pushReleaseForRepository GitRepository {..} = do
|
||||||
|
ensureCacheInPlace gitRepositoryURL taskDeployPath
|
||||||
release <- newRelease taskReleaseFormat
|
release <- newRelease taskReleaseFormat
|
||||||
cloneToRelease taskDeployPath release
|
cloneToRelease taskDeployPath release
|
||||||
setReleaseRevision taskDeployPath release taskRevision
|
setReleaseRevision taskDeployPath release gitRepositoryRevision
|
||||||
return release
|
return release
|
||||||
|
pushReleaseForRepository LocalDirectory {..} =
|
||||||
|
newRelease taskReleaseFormat
|
||||||
|
|
||||||
-- | Same as 'pushRelease' but doesn't perform any version control
|
-- | Same as 'pushRelease' but doesn't perform any version control
|
||||||
-- related operations.
|
-- related operations.
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
|
|
||||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||||
|
|
||||||
module Config
|
module System.Hapistrano.Config
|
||||||
( Config (..)
|
( Config (..)
|
||||||
, CopyThing (..)
|
, CopyThing (..)
|
||||||
, Target (..))
|
, Target (..))
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Control.Applicative ((<|>))
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
import Data.Function (on)
|
import Data.Function (on)
|
||||||
import Data.List (nubBy)
|
import Data.List (nubBy)
|
||||||
@ -20,6 +21,7 @@ import Path
|
|||||||
import System.Hapistrano.Commands
|
import System.Hapistrano.Commands
|
||||||
import System.Hapistrano.Types (Shell(..),
|
import System.Hapistrano.Types (Shell(..),
|
||||||
ReleaseFormat (..),
|
ReleaseFormat (..),
|
||||||
|
Source(..),
|
||||||
TargetSystem (..))
|
TargetSystem (..))
|
||||||
|
|
||||||
-- | Hapistrano configuration typically loaded from @hap.yaml@ file.
|
-- | Hapistrano configuration typically loaded from @hap.yaml@ file.
|
||||||
@ -29,10 +31,8 @@ data Config = Config
|
|||||||
-- ^ Top-level deploy directory on target machine
|
-- ^ Top-level deploy directory on target machine
|
||||||
, configHosts :: ![Target]
|
, configHosts :: ![Target]
|
||||||
-- ^ Hosts\/ports\/shell\/ssh args to deploy to. If empty, localhost will be assumed.
|
-- ^ Hosts\/ports\/shell\/ssh args to deploy to. If empty, localhost will be assumed.
|
||||||
, configRepo :: !String
|
, configSource :: !Source
|
||||||
-- ^ Location of repository that contains the source code to deploy
|
-- ^ Location of the 'Source' that contains the code to deploy
|
||||||
, configRevision :: !String
|
|
||||||
-- ^ Revision to use
|
|
||||||
, configRestartCommand :: !(Maybe GenericCommand)
|
, configRestartCommand :: !(Maybe GenericCommand)
|
||||||
-- ^ The command to execute when switching to a different release
|
-- ^ The command to execute when switching to a different release
|
||||||
-- (usually after a deploy or rollback).
|
-- (usually after a deploy or rollback).
|
||||||
@ -97,8 +97,10 @@ instance FromJSON Config where
|
|||||||
let first Target{..} = host
|
let first Target{..} = host
|
||||||
configHosts = nubBy ((==) `on` first)
|
configHosts = nubBy ((==) `on` first)
|
||||||
(maybeToList (Target <$> host <*> pure port <*> pure shell <*> pure sshArgs) ++ hs)
|
(maybeToList (Target <$> host <*> pure port <*> pure shell <*> pure sshArgs) ++ hs)
|
||||||
configRepo <- o .: "repo"
|
source m =
|
||||||
configRevision <- o .: "revision"
|
GitRepository <$> m .: "repo" <*> m .: "revision"
|
||||||
|
<|> LocalDirectory <$> m .: "local_directory"
|
||||||
|
configSource <- source o
|
||||||
configRestartCommand <- (o .:? "restart_command") >>=
|
configRestartCommand <- (o .:? "restart_command") >>=
|
||||||
maybe (return Nothing) (fmap Just . mkCmd)
|
maybe (return Nothing) (fmap Just . mkCmd)
|
||||||
configBuildScript <- o .:? "build_script" >>=
|
configBuildScript <- o .:? "build_script" >>=
|
@ -15,6 +15,7 @@ module System.Hapistrano.Types
|
|||||||
( Hapistrano
|
( Hapistrano
|
||||||
, Failure(..)
|
, Failure(..)
|
||||||
, Config(..)
|
, Config(..)
|
||||||
|
, Source(..)
|
||||||
, Task(..)
|
, Task(..)
|
||||||
, ReleaseFormat(..)
|
, ReleaseFormat(..)
|
||||||
, SshOptions(..)
|
, SshOptions(..)
|
||||||
@ -22,12 +23,14 @@ module System.Hapistrano.Types
|
|||||||
, Release
|
, Release
|
||||||
, TargetSystem(..)
|
, TargetSystem(..)
|
||||||
, Shell(..)
|
, Shell(..)
|
||||||
|
-- * Types helpers
|
||||||
, mkRelease
|
, mkRelease
|
||||||
, releaseTime
|
, releaseTime
|
||||||
, renderRelease
|
, renderRelease
|
||||||
, parseRelease
|
, parseRelease
|
||||||
, fromMaybeReleaseFormat
|
, fromMaybeReleaseFormat
|
||||||
, fromMaybeKeepReleases
|
, fromMaybeKeepReleases
|
||||||
|
, toMaybePath
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import Control.Applicative
|
import Control.Applicative
|
||||||
@ -57,15 +60,28 @@ data Config =
|
|||||||
-- ^ How to print messages
|
-- ^ 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.
|
-- | The records describes deployment task.
|
||||||
data Task =
|
data Task =
|
||||||
Task
|
Task
|
||||||
{ taskDeployPath :: Path Abs Dir
|
{ taskDeployPath :: Path Abs Dir
|
||||||
-- ^ The root of the deploy target on the remote host
|
-- ^ The root of the deploy target on the remote host
|
||||||
, taskRepository :: String
|
, taskSource :: Source
|
||||||
-- ^ The URL of remote Git repo to deploy
|
-- ^ The 'Source' to deploy
|
||||||
, taskRevision :: String
|
|
||||||
-- ^ A SHA1 or branch to release
|
|
||||||
, taskReleaseFormat :: ReleaseFormat
|
, taskReleaseFormat :: ReleaseFormat
|
||||||
-- ^ The 'ReleaseFormat' to use
|
-- ^ The 'ReleaseFormat' to use
|
||||||
}
|
}
|
||||||
@ -140,6 +156,9 @@ renderRelease (Release rfmt time) = formatTime defaultTimeLocale fmt time
|
|||||||
ReleaseShort -> releaseFormatShort
|
ReleaseShort -> releaseFormatShort
|
||||||
ReleaseLong -> releaseFormatLong
|
ReleaseLong -> releaseFormatLong
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
-- Types helpers
|
||||||
|
|
||||||
-- | Parse 'Release' identifier from a 'String'.
|
-- | Parse 'Release' identifier from a 'String'.
|
||||||
parseRelease :: String -> Maybe Release
|
parseRelease :: String -> Maybe Release
|
||||||
parseRelease s =
|
parseRelease s =
|
||||||
@ -166,3 +185,8 @@ fromMaybeKeepReleases cliKR configKR =
|
|||||||
|
|
||||||
defaultKeepReleases :: Natural
|
defaultKeepReleases :: Natural
|
||||||
defaultKeepReleases = 5
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user