mirror of
https://github.com/stackbuilders/hapistrano.git
synced 2024-12-25 04:23:59 +03:00
219 lines
7.3 KiB
Haskell
219 lines
7.3 KiB
Haskell
module System.HapistranoSpec (spec) where
|
|
|
|
import Test.Hspec (it, describe, shouldBe, Spec)
|
|
|
|
import System.IO.Temp (withSystemTempDirectory)
|
|
|
|
import System.Directory (getDirectoryContents)
|
|
import Control.Monad (void, replicateM_)
|
|
import Control.Monad.Trans.Either (runEitherT)
|
|
|
|
import Control.Monad.Reader (ReaderT(..))
|
|
|
|
import System.FilePath.Posix (joinPath)
|
|
|
|
import qualified System.Hapistrano as Hap
|
|
import Data.List (intercalate, sort)
|
|
|
|
import qualified System.IO as IO
|
|
import qualified System.Process as Process
|
|
|
|
runCommand :: String -> IO ()
|
|
runCommand command = do
|
|
putStrLn ("GIT running: " ++ command)
|
|
let process = Process.shell command
|
|
(_, Just outHandle, Just errHandle, processHandle) <-
|
|
Process.createProcess process { Process.std_err = Process.CreatePipe
|
|
, Process.std_in = Process.CreatePipe
|
|
, Process.std_out = Process.CreatePipe
|
|
}
|
|
exitCode <- fmap show (Process.waitForProcess processHandle)
|
|
out <- IO.hGetContents outHandle
|
|
err <- IO.hGetContents errHandle
|
|
putStrLn ("GIT res: " ++ show (exitCode, out, err))
|
|
|
|
-- | Generate a source git repo as test fixture. Push an initial commit
|
|
-- to the bare repo by making a clone and committing a trivial change and
|
|
-- pushing to the bare repo.
|
|
genSourceRepo :: FilePath -> IO FilePath
|
|
genSourceRepo path = do
|
|
let fullRepoPath = joinPath [path, "testRepo"]
|
|
clonePath = joinPath [path, "testRepoClone"]
|
|
|
|
gitConfigReplace =
|
|
intercalate
|
|
" && "
|
|
[ "git config --local --replace-all push.default simple"
|
|
, "git config --local --replace-all user.email hap@hap"
|
|
, "git config --local --replace-all user.name Hap"
|
|
]
|
|
|
|
gitConfigUnset =
|
|
intercalate
|
|
" && "
|
|
[ "git config --local --unset push.default"
|
|
, "git config --local --unset user.email"
|
|
, "git config --local --unset user.name"
|
|
]
|
|
|
|
commands =
|
|
[ "mkdir -p " ++ fullRepoPath
|
|
, "git init --bare " ++ fullRepoPath
|
|
, "git clone " ++ fullRepoPath ++ " " ++ clonePath
|
|
, "echo testing > " ++ joinPath [clonePath, "README"]
|
|
, "cd " ++ clonePath ++ " && " ++ gitConfigReplace
|
|
, "cd " ++ clonePath ++ " && git add -A"
|
|
, "cd " ++ clonePath ++ " && git commit -m\"First commit\""
|
|
, "cd " ++ clonePath ++ " && git push"
|
|
, "cd " ++ clonePath ++ " && " ++ gitConfigUnset
|
|
]
|
|
|
|
mapM_ runCommand commands
|
|
|
|
return fullRepoPath
|
|
|
|
rollback :: Hap.Config -> IO ()
|
|
rollback cfg =
|
|
Hap.runRC errorHandler successHandler cfg $ do
|
|
|
|
_ <- Hap.rollback
|
|
void Hap.restartServerCommand
|
|
|
|
where
|
|
errorHandler = Hap.defaultErrorHandler
|
|
successHandler = Hap.defaultSuccessHandler
|
|
|
|
|
|
-- | Deploys the current release with Config options.
|
|
deployOnly :: Hap.Config -> IO ()
|
|
deployOnly cfg =
|
|
Hap.runRC errorHandler successHandler cfg $ void Hap.pushRelease
|
|
|
|
where
|
|
errorHandler = Hap.defaultErrorHandler
|
|
successHandler = Hap.defaultSuccessHandler
|
|
|
|
-- | Deploys the current release with Config options.
|
|
deployAndActivate :: Hap.Config -> IO ()
|
|
deployAndActivate cfg =
|
|
Hap.runRC errorHandler successHandler cfg $ do
|
|
rel <- Hap.pushRelease
|
|
_ <- Hap.runBuild rel
|
|
|
|
void $ Hap.activateRelease rel
|
|
|
|
where
|
|
errorHandler = Hap.defaultErrorHandler
|
|
successHandler = Hap.defaultSuccessHandler
|
|
|
|
defaultState :: FilePath -> FilePath -> Hap.Config
|
|
defaultState tmpDir testRepo =
|
|
Hap.Config { Hap.deployPath = tmpDir
|
|
, Hap.host = Nothing
|
|
, Hap.repository = testRepo
|
|
, Hap.releaseFormat = Hap.Long
|
|
, Hap.revision = "master"
|
|
, Hap.buildScript = Nothing
|
|
, Hap.restartCommand = Nothing
|
|
}
|
|
|
|
-- | The 'fromRight' function extracts the element out of a 'Right' and
|
|
-- throws an error if its argument take the form @Left _@.
|
|
fromRight :: Either a b -> b
|
|
fromRight (Left _) = error "fromRight: Argument takes form 'Left _'" -- yuck
|
|
fromRight (Right x) = x
|
|
|
|
spec :: Spec
|
|
spec = describe "hapistrano" $ do
|
|
describe "readCurrentLink" $
|
|
it "trims trailing whitespace" $
|
|
withSystemTempDirectory "hapistranoDeployTest" $ \tmpDir -> do
|
|
|
|
testRepoPath <- genSourceRepo tmpDir
|
|
|
|
deployAndActivate $ defaultState tmpDir testRepoPath
|
|
|
|
ltarget <-
|
|
runReaderT (runEitherT Hap.readCurrentLink) $
|
|
defaultState tmpDir testRepoPath
|
|
|
|
last (fromRight ltarget) /= '\n' `shouldBe` True
|
|
|
|
describe "deploying" $ do
|
|
it "reads a common build script with comments and new lines" $ do
|
|
scriptLines <- lines `fmap` IO.readFile "./script/clean-build.sh"
|
|
let validBBuildScriptLines = Hap.cleanBuildScript scriptLines
|
|
length validBBuildScriptLines `shouldBe` 7
|
|
|
|
it "a simple deploy" $
|
|
withSystemTempDirectory "hapistranoDeployTest" $ \tmpDir -> do
|
|
|
|
testRepoPath <- genSourceRepo tmpDir
|
|
|
|
deployOnly $ defaultState tmpDir testRepoPath
|
|
|
|
contents <- getDirectoryContents (joinPath [tmpDir, "releases"])
|
|
length (filter (Hap.isReleaseString Hap.Long) contents) `shouldBe` 1
|
|
|
|
it "activates the release" $
|
|
withSystemTempDirectory "hapistranoDeployTest" $ \tmpDir -> do
|
|
|
|
testRepoPath <- genSourceRepo tmpDir
|
|
|
|
deployAndActivate $ defaultState tmpDir testRepoPath
|
|
|
|
contents <- getDirectoryContents (joinPath [tmpDir, "releases"])
|
|
length (filter (Hap.isReleaseString Hap.Long) contents) `shouldBe` 1
|
|
|
|
it "cleans up old releases" $
|
|
withSystemTempDirectory "hapistranoDeployTest" $ \tmpDir -> do
|
|
testRepoPath <- genSourceRepo tmpDir
|
|
|
|
replicateM_ 7 $ deployAndActivate $ defaultState tmpDir testRepoPath
|
|
|
|
contents <- getDirectoryContents (joinPath [tmpDir, "releases"])
|
|
length (filter (Hap.isReleaseString Hap.Long) contents) `shouldBe` 5
|
|
|
|
describe "rollback" $
|
|
it "rolls back to the previous release" $
|
|
withSystemTempDirectory "hapistranoDeployTest" $ \tmpDir -> do
|
|
|
|
testRepoPath <- genSourceRepo tmpDir
|
|
let deployState = defaultState tmpDir testRepoPath
|
|
|
|
deployAndActivate deployState
|
|
|
|
-- current symlink should point to the last release directory
|
|
contents <- getDirectoryContents (joinPath [tmpDir, "releases"])
|
|
|
|
let firstRelease = head $ filter (Hap.isReleaseString Hap.Long) contents
|
|
|
|
firstReleaseLinkTarget <-
|
|
runReaderT (runEitherT Hap.readCurrentLink) deployState
|
|
|
|
firstRelease `shouldBe` Hap.pathToRelease (fromRight firstReleaseLinkTarget)
|
|
|
|
-- deploy a second version
|
|
deployAndActivate deployState
|
|
|
|
-- current symlink should point to second release
|
|
|
|
conts <- getDirectoryContents (joinPath [tmpDir, "releases"])
|
|
|
|
let secondRelease =
|
|
sort (filter (Hap.isReleaseString Hap.Long) conts) !! 1
|
|
|
|
secondReleaseLinkTarget <-
|
|
runReaderT (runEitherT Hap.readCurrentLink) deployState
|
|
|
|
secondRelease `shouldBe` Hap.pathToRelease (fromRight secondReleaseLinkTarget)
|
|
|
|
-- roll back, and current symlink should point to first release again
|
|
|
|
rollback deployState
|
|
|
|
afterRollbackLinkTarget <-
|
|
runReaderT (runEitherT Hap.readCurrentLink) deployState
|
|
|
|
Hap.pathToRelease (fromRight afterRollbackLinkTarget) `shouldBe` firstRelease
|