Hapistrano is a deployment library for Haskell applications similar to Ruby’s Capistrano.
We created Hapistrano because:
- Deploys should be simple, but as close to atomic as possible (eg, they shouldn’t require much application downtime).
- Rollback should be trivial to achieve to bring the application back to the last-deployed state.
- Deploys shouldn’t fail because of dependency problems.
How it Works
Hapistrano (like Capistrano for Ruby) deploys applications to a new directory marked with a timestamp on the remote host. It creates this new directory quickly by placing a git repository for caching purposes on the remote server.
When the build process completes, it switches a symlink to the
release directory, and optionally restarts the web server.
By default, Hapistrano keeps the last five releases on the target host filesystem and deletes previous releases to avoid filling up the disk.
Hapistrano 0.3.0.0 looks for a configuration file called
typically looks like this:
deploy_path: '/var/projects/my-project' host: myserver.com port: 2222 repo: 'https://github.com/stackbuilders/hapistrano.git' revision: origin/master build_script: - stack setup - stack build restart_command: systemd restart my-app-service
The following parameters are required:
deploy_path— the root of the deploy target on the remote host.
repo— the origin repository.
revision— the SHA1 or branch to deploy. If a branch, you will need to specify it as
origin/branch_namedue to the way that the cache repo is configured.
The following parameters are optional:
host— the target host, if missing,
localhostwill be assumed (which is useful for testing and playing with
port— SSH port number to use. If missing, 22 will be used.
build_script— instructions how to build the application in the form of shell commands.
restart_command— if you need to restart a remote web server after a successful rollback, specify the command that you use in this variable. It will be run after both deploy and rollback.
vc_action- Controls if version control related activity should take place. It defaults to true. When you don’t want activity like cloning, fetching etc. to take place, set this to
linux- Specify, whether or not, the target system where Hapistrano will deploy to is a GNU/Linux or other UNIX (g.e. BSD, Mac). This is set to
trueby default so unless the target system is not GNU/Linux, this should not be necessary. The platform where Hapistrano is running won’t affect the available options for commands (g.e. A Mac deploying to a Ubuntu machine, doesn’t need this flag)
run_locally:- Instructions to run locally on your machine in the form of shell commands. Example:
run_locally: - pwd - bash deploy.sh
Note how we are even able to execute a bash script named
above. Be sure to use
set -e in your bash script to avoid
headaches. Hapistrano will stop the execution on non zero exit
codes. Without the usage of
set -e, there is a possiblity that your
bash script may return a zero exit code even if your intermediate
command resulted in an error.
After creating a configuration file as above, deploying is as simple as:
$ hap deploy
Rollback is also trivial:
$ hap rollback # to rollback to previous successful deploy $ hap rollback -n 2 # go two deploys back in time, etc.
What to do when compiling on server is not viable
Sometimes the target machine (server) is not capable of compiling your
application because e.g. it has not enough memory and GHC exhausts it all.
You can copy pre-compiled files from local machine or CI server using
copy_files: - src: '/home/stackbuilders/my-file.txt' dest: 'my-file.txt' copy_dirs: - src: .stack-work dest: .stack-work
src maybe absolute or relative, it’s path to file or directory on local
dest may only be relative (it’s expanded relatively to cloned
repo) and specifies where to put the files/directories on target machine.
Directories and files with clashing names will be overwritten. Directories
are copied recursively.
Deploying to multiple machines concurrently
Beginning with Hapistrano 0.3.1.0 it’s possible to deploy to several
machines concurrently. The only things you need to do is to adjust your
configuration file and use
targets parameter instead of
targets: - host: myserver-a.com port: 2222 - host: myserver-b.com # the rest is the same…
A few things to note here:
hostitem is required for every target, but
portmay be omitted and then it defaults to
The deployment will run concurrently and finish when interactions with all targets have finished either successfully or not. If at least one interaction was unsuccessful, the
haptool will exit with non-zero exit code.
The log is printed in such a way that messages from several machines get intermixed, but it’s guaranteed that they won’t overlap (printing itself is sequential) and the headers will tell you exactly which machine was executing which command.
If you don’t specify
hap will assume
usually, which is mainly useful for testing.
If you would like to use Docker, there is a lightweight image available on Docker Hub.
MIT, see the LICENSE file.
Pull requests for modifications to this program are welcome. Fork and open a PR. Feel free to email me if you have questions about what may be accepted before working on a PR.
If you’re looking for a place to start, you may want to check the open issue.
- Loose upper bound for yaml 0.9
- Loose upper bound for aeson 1.4
- Add Dockerfile
- Adding tested compatibility with GHC 8.4
- Support for temporary 1.3
- Support for aeson 1.3
- Loose uppers bounds for async
- Standarize style
- When showing version information also show git branch and commit
- Add support for deploying to other Unix systems, besides GNU/Linux which didn’t supported all the flags that Hapistrano was using. See issue #63
git checkoutinstead of
git resetto set the release revision
- Correct bounds for base. GHC support for versions older than 7.10 was dropped on 0.3.0.0
run_locallyto run user defined commands locally before deployment. Thanks to Sibi (GitHub: psibi) for this contribution
- Allow time 1.8
- Allow process 1.6
- Allow path-io 1.3
- Allow optparse-applicative 0.14
- Add support for help in subcommands. Thanks to Vanessa McHale (GitHub: vmchale) for this contribution
-vswitch for hap. Thanks to Sibi (GitHub: psibi) for this contribution
vc_actionto control version control related tasks. Thanks to Sibi (GitHub: psibi) for this contribution
- Fixed a bug with repos not being fetched properly.
- Implemented concurrent deployment to multiple hosts.
- Now completion tokens are dropped automatically like old releases.
- Reduced verbosity of some commands to make reading logs easier.
- Restart command is now invoked after activation of new release (as it should).
- Fix a typo in flag that specifies SSH port for
- Ensure that containing directories for files and directories to copy
exist before invoking
- Add proper set of dependency version constraints.
optparse-applicativeto parse arguments.
- Allow to specify non-standard SSH port.
- Drop support for GHCs older than 7.10 (because Chris Done’s
pathdoes not compile with them, see: https://github.com/chrisdone/path/issues/46).
- Now Hapistrano uses
hap.yamlfile for all its configuration.
- Added the ability to copy arbitrary files and directories verbatim from local machine to target host.
- Add change log (#23).
README.mdto extra source files.
- Handle missing environment variables more graciously.
- Allow GHC 8 and base 4.9.
- Fix tests (#31).
- Use Stack (#17).
- Clean up package (#20).
- Fix tests (#25).
- GHC 7.10 support.
- Refactoring and documentation improvements.
- Various refactoring and relaxed dependency constraints.
- Print error messages to
stderr, return non-zero exit code on failure.
- Initial release.