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
|
||||
* Update upper bounds for `path` and `path-io` packages.
|
||||
|
||||
|
11
README.md
11
README.md
@ -32,15 +32,18 @@ filesystem and deletes previous releases to avoid filling up the disk.
|
||||
|
||||
## 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:
|
||||
|
||||
```yaml
|
||||
deploy_path: '/var/projects/my-project'
|
||||
host: myserver.com
|
||||
port: 2222
|
||||
# To perform version control operations
|
||||
repo: 'https://github.com/stackbuilders/hapistrano.git'
|
||||
revision: origin/master
|
||||
# To copy the contents of the directory
|
||||
local_directory: '/tmp/my-project'
|
||||
build_script:
|
||||
- stack setup
|
||||
- stack build
|
||||
@ -50,10 +53,16 @@ restart_command: systemd restart my-app-service
|
||||
The following parameters are required:
|
||||
|
||||
* `deploy_path` — the root of the deploy target on the remote host.
|
||||
* Related to the `source` of the repository, you have the following options:
|
||||
- _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
|
||||
configured.
|
||||
* `local_directory` — when this parameter is set, hapistrano will copy the
|
||||
contents of the directory.
|
||||
|
||||
The following parameters are *optional*:
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
module Main (main) where
|
||||
|
||||
import qualified Config as C
|
||||
import Control.Concurrent.Async
|
||||
import Control.Concurrent.STM
|
||||
import Control.Monad
|
||||
@ -21,6 +20,7 @@ import Paths_hapistrano (version)
|
||||
import System.Exit
|
||||
import qualified System.Hapistrano as Hap
|
||||
import qualified System.Hapistrano.Commands as Hap
|
||||
import qualified System.Hapistrano.Config as C
|
||||
import qualified System.Hapistrano.Core as Hap
|
||||
import System.Hapistrano.Types
|
||||
import System.IO
|
||||
@ -125,8 +125,7 @@ main = do
|
||||
C.Config{..} <- Yaml.loadYamlSettings [optsConfigFile] [] Yaml.useEnv
|
||||
chan <- newTChanIO
|
||||
let task rf = Task { taskDeployPath = configDeployPath
|
||||
, taskRepository = configRepo
|
||||
, taskRevision = configRevision
|
||||
, taskSource = configSource
|
||||
, taskReleaseFormat = rf }
|
||||
let printFnc dest str = atomically $
|
||||
writeTChan chan (PrintMsg dest str)
|
||||
@ -141,6 +140,8 @@ main = do
|
||||
then Hap.pushRelease (task releaseFormat)
|
||||
else Hap.pushReleaseWithoutVc (task releaseFormat)
|
||||
rpath <- Hap.releasePath configDeployPath release
|
||||
forM_ (toMaybePath configSource) $ \src ->
|
||||
Hap.scpDir src rpath
|
||||
forM_ configCopyFiles $ \(C.CopyThing src dest) -> do
|
||||
srcPath <- resolveFile' src
|
||||
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
|
||||
version: 0.3.10.1
|
||||
version: 0.4.0.0
|
||||
synopsis: A deployment library for Haskell applications
|
||||
description:
|
||||
.
|
||||
@ -48,6 +48,7 @@ library
|
||||
hs-source-dirs: src
|
||||
exposed-modules: System.Hapistrano
|
||||
, System.Hapistrano.Commands
|
||||
, System.Hapistrano.Config
|
||||
, System.Hapistrano.Core
|
||||
, System.Hapistrano.Types
|
||||
, System.Hapistrano.Commands.Internal
|
||||
@ -64,6 +65,7 @@ library
|
||||
, typed-process >= 0.2 && < 0.3
|
||||
, time >= 1.5 && < 1.10
|
||||
, transformers >= 0.4 && < 0.6
|
||||
, yaml >= 0.8.16 && < 0.12
|
||||
if flag(dev)
|
||||
ghc-options: -Wall -Werror
|
||||
else
|
||||
@ -73,8 +75,7 @@ library
|
||||
executable hap
|
||||
hs-source-dirs: app
|
||||
main-is: Main.hs
|
||||
other-modules: Config
|
||||
, Paths_hapistrano
|
||||
other-modules: Paths_hapistrano
|
||||
build-depends: aeson >= 0.11 && < 1.5
|
||||
, async >= 2.0.1.6 && < 2.4
|
||||
, base >= 4.8 && < 5.0
|
||||
@ -100,6 +101,7 @@ test-suite test
|
||||
hs-source-dirs: spec
|
||||
main-is: Spec.hs
|
||||
other-modules: System.HapistranoSpec
|
||||
, System.HapistranoConfigSpec
|
||||
, System.HapistranoPropsSpec
|
||||
build-depends: base >= 4.8 && < 5.0
|
||||
, directory >= 1.2.5 && < 1.4
|
||||
@ -113,6 +115,7 @@ test-suite test
|
||||
, QuickCheck >= 2.5.1 && < 3.0
|
||||
, silently >= 1.2 && < 1.3
|
||||
, temporary >= 1.1 && < 1.4
|
||||
, yaml >= 0.8.16 && < 0.12
|
||||
build-tools: hspec-discover >= 2.0 && < 3.0
|
||||
|
||||
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 =
|
||||
Task
|
||||
{ taskDeployPath = deployPath
|
||||
, taskRepository = fromAbsDir repoPath
|
||||
, taskRevision = revision
|
||||
, taskSource =
|
||||
GitRepository
|
||||
{ gitRepositoryURL = fromAbsDir repoPath
|
||||
, gitRepositoryRevision = revision
|
||||
}
|
||||
, taskReleaseFormat = ReleaseLong
|
||||
}
|
||||
|
||||
|
@ -55,11 +55,18 @@ import System.Hapistrano.Types
|
||||
pushRelease :: Task -> Hapistrano Release
|
||||
pushRelease Task {..} = do
|
||||
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
|
||||
cloneToRelease taskDeployPath release
|
||||
setReleaseRevision taskDeployPath release taskRevision
|
||||
setReleaseRevision taskDeployPath release gitRepositoryRevision
|
||||
return release
|
||||
pushReleaseForRepository LocalDirectory {..} =
|
||||
newRelease taskReleaseFormat
|
||||
|
||||
-- | Same as 'pushRelease' but doesn't perform any version control
|
||||
-- related operations.
|
||||
|
@ -4,12 +4,13 @@
|
||||
|
||||
{-# OPTIONS_GHC -fno-warn-orphans #-}
|
||||
|
||||
module Config
|
||||
module System.Hapistrano.Config
|
||||
( Config (..)
|
||||
, CopyThing (..)
|
||||
, Target (..))
|
||||
where
|
||||
|
||||
import Control.Applicative ((<|>))
|
||||
import Data.Aeson
|
||||
import Data.Function (on)
|
||||
import Data.List (nubBy)
|
||||
@ -20,6 +21,7 @@ import Path
|
||||
import System.Hapistrano.Commands
|
||||
import System.Hapistrano.Types (Shell(..),
|
||||
ReleaseFormat (..),
|
||||
Source(..),
|
||||
TargetSystem (..))
|
||||
|
||||
-- | Hapistrano configuration typically loaded from @hap.yaml@ file.
|
||||
@ -29,10 +31,8 @@ data Config = Config
|
||||
-- ^ Top-level deploy directory on target machine
|
||||
, configHosts :: ![Target]
|
||||
-- ^ Hosts\/ports\/shell\/ssh args to deploy to. If empty, localhost will be assumed.
|
||||
, configRepo :: !String
|
||||
-- ^ Location of repository that contains the source code to deploy
|
||||
, configRevision :: !String
|
||||
-- ^ Revision to use
|
||||
, configSource :: !Source
|
||||
-- ^ Location of the 'Source' that contains the code to deploy
|
||||
, configRestartCommand :: !(Maybe GenericCommand)
|
||||
-- ^ The command to execute when switching to a different release
|
||||
-- (usually after a deploy or rollback).
|
||||
@ -97,8 +97,10 @@ instance FromJSON Config where
|
||||
let first Target{..} = host
|
||||
configHosts = nubBy ((==) `on` first)
|
||||
(maybeToList (Target <$> host <*> pure port <*> pure shell <*> pure sshArgs) ++ hs)
|
||||
configRepo <- o .: "repo"
|
||||
configRevision <- o .: "revision"
|
||||
source m =
|
||||
GitRepository <$> m .: "repo" <*> m .: "revision"
|
||||
<|> LocalDirectory <$> m .: "local_directory"
|
||||
configSource <- source o
|
||||
configRestartCommand <- (o .:? "restart_command") >>=
|
||||
maybe (return Nothing) (fmap Just . mkCmd)
|
||||
configBuildScript <- o .:? "build_script" >>=
|
@ -15,6 +15,7 @@ module System.Hapistrano.Types
|
||||
( Hapistrano
|
||||
, Failure(..)
|
||||
, Config(..)
|
||||
, Source(..)
|
||||
, Task(..)
|
||||
, ReleaseFormat(..)
|
||||
, SshOptions(..)
|
||||
@ -22,12 +23,14 @@ module System.Hapistrano.Types
|
||||
, Release
|
||||
, TargetSystem(..)
|
||||
, Shell(..)
|
||||
-- * Types helpers
|
||||
, mkRelease
|
||||
, releaseTime
|
||||
, renderRelease
|
||||
, parseRelease
|
||||
, fromMaybeReleaseFormat
|
||||
, fromMaybeKeepReleases
|
||||
, toMaybePath
|
||||
) where
|
||||
|
||||
import Control.Applicative
|
||||
@ -57,15 +60,28 @@ data Config =
|
||||
-- ^ 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
|
||||
, taskRepository :: String
|
||||
-- ^ The URL of remote Git repo to deploy
|
||||
, taskRevision :: String
|
||||
-- ^ A SHA1 or branch to release
|
||||
, taskSource :: Source
|
||||
-- ^ The 'Source' to deploy
|
||||
, taskReleaseFormat :: ReleaseFormat
|
||||
-- ^ The 'ReleaseFormat' to use
|
||||
}
|
||||
@ -140,6 +156,9 @@ renderRelease (Release rfmt time) = formatTime defaultTimeLocale fmt time
|
||||
ReleaseShort -> releaseFormatShort
|
||||
ReleaseLong -> releaseFormatLong
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
-- Types helpers
|
||||
|
||||
-- | Parse 'Release' identifier from a 'String'.
|
||||
parseRelease :: String -> Maybe Release
|
||||
parseRelease s =
|
||||
@ -166,3 +185,8 @@ fromMaybeKeepReleases 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
|
||||
|
Loading…
Reference in New Issue
Block a user