Bashistrano is a remote server automation tool heavily inspired by Capistrano. It is written purely in bash without any external dependency.
The main use-case of Bashistrano is to deliver containerized (Docker-based) projects to remote servers. Once it's set up, you can deploy your code as easily as:
$ cd project
$ ./bashistrano deploy uat v1.0.2
- Download the
bashistrano
script and commit in your project's source code. - Add a
/tmp
entry in your.gitignore
file - Follow the instructions below on how to use and configure Bashistrano.
$ ./bashistrano
Usage: ./bashistrano COMMAND
A bash deployment automation tool
Options:
-h, --help Display this help message
-v, --version Print version information and quit
Commands:
deploy Deploys the application end-to-end
only:prepare Prepares the environment for deployment
only:deliver Delivers from an already prepared environment
Run './bashistrano COMMAND --help' for more information on a command
The following parameters can be set in config.sh
or any stage specific configuration file like config/uat.sh
.
Stage specific configuration takes precedence over the default defined in config.sh
.
Parameter | Description |
---|---|
application |
application name |
deploy_to |
remote directory for deployments |
keep_releases |
number of releases to keep on remote servers |
servers |
array of remote servers in user@host:port format (default port: 22) |
images |
array of dependent Docker images (see more below) |
In Bashistrano terminology different deployment environments are called stages. The required folder structure is the following for a project with uat
and prod
stages.
project/
├── config/ <-- stage specific configuration
│ ├── uat.sh
│ └── prod.sh
├── stages/ <-- stage specific code
│ ├── uat/
| │ └── ...
│ └── prod/
| └── ...
├── bashistrano <-- bashistrano script
└── config.sh <-- main configuration
It's recommended to use public key authentication on your servers. If it's not possible for some reason, you can supply your SSH password when running Bashinstrano.
SSH_PASS=secret ./bashistrano deploy uat v1.0.0
When the SSH_PASS
variable is set Bashistrano is using the sshpass utility under the hood.
NOTE: this method is not recommended since a simple
ps
command can expose your SSH password to all users on the same host.
Bashistrano allows you to specify a list of Docker images which need to be downloaded and packaged together with the deployment code. This way remote servers don't need to have access to any private Docker registries.
The images
parameter is an array with pairs of Docker tags with the following structure.
images=(
<first image remote> <first image local>
<second image remote> <second image local>
)
The first value is the remote Docker image which need to be downloaded (possibly from a private registry). The second value is the desired tag which will be used on the remote servers.
Bashistrano is matchint local and remote Docker image IDs and performs uploading only if necessary.
For a typical multi-stage use-case the configuration will look like the following:
Default configuration in config.sh
#!/bin/bash -e
application="myapp"
deploy_to="~/apps/$application/$stage"
keep_releases=5
images=(
"registry.example.com/myapp:1.0.0" "local/myapp:1.0.0"
"registry.example.com/dep:1.2.1" "local/dep:1.2.1"
)
Stage-specific configuration in config/uat.sh
#!/bin/bash -e
servers=( "[email protected]" "[email protected]:2022" )
The deployment process consists of multiple steps grouped into two phases. The two phases together make up the deploy process.
STEP PHASE
──┐
──┐ │
clean_local │ prepare │
pull_images │ │
──┤ │
clean_remote │ │
push_images │ │ deploy
push_code │ │
publish_release │ deliver │
cleanup_releases │ │
log_revision │ │
──┘ │
──┘
Step | Description |
---|---|
clean_local |
Clean up local workspace |
pull_images |
Download specified Docker images and save them locally |
clean_remote |
Clean workspace on remote servers |
push_images |
Upload locally stored Docker images to remote servers |
push_code |
Upload current version of stage specific code |
publish_release |
Switch out previous release to the newly uploaded one |
cleanup_releases |
Keep only the last N release specified by keep_releases parameter |
log_revision |
Add new entry to revision log file |
The deployment process can be customized using hooks. Hooks are triggered before or after phases and steps.
The hooks are executed in the following order.
start
├── before:deploy
│ ├── before:prepare
│ │ ├── before:clean_local
│ │ ├── ...
│ │ ├── ...
│ │ └── after:pull_images
│ ├── after:prepare
│ ├── before:deliver
│ │ ├── before:clean_remote
│ │ ├── ...
│ │ ├── ...
│ │ └── after:log_revision
│ └── after:deliver
└── after:deploy
You can simply add your custom hook by implementing a method named after the given hook in any of the configuration files.
#!/bin/bash -e
application="myapp"
keep_releases=5
# ...
after:deliver() {
run_locally 'echo Yaay'
}
NOTE: if you implement a hook function you must specify a body, otherwise the process will fail
If you want to have custom behaviour only on a specific stage, you have two options.
Let's assume you want to add an uat
specific after:deliver
hook.
The hook is already in use in config.sh
:
#!/bin/bash -e
after:deliver() {
run_locally 'echo Hello'
}
You can add stage-specific behaviour in config/uat.sh
using a hook function postfixed with the stage name. It will run after the global hook:
#!/bin/bash -e
after:deliver:uat() {
run_locally 'echo World!'
}
In addition to the parameters you've explicitly set in the configuration the following Bash variables are accessible in hooks:
Variable name | Description |
---|---|
$stage |
name of currently running stage |
$version |
VERSION parameter passed on the command line |
$release_id |
unique identifier of current run (date+time) |
$release_path |
remote path for the release being uploaded |
$current_path |
remote path for the currently active release |
$tmp_path |
remote path for temporary files (cleaned on clean_remote step) |
$local_tmp_path |
local path for temporary files (cleaned on clean_local step) |
$local_port |
random local port opened after calling the open_tunnel function |
$output |
captured standard output of the last invoked function (see Functions) |
When writing your hooks you can you can leverage the utility functions listed below.
run_locally <command>
-- Run command on the local machine. The command being executed is displayed properly on the output.
run_remotely <command>
-- Run command on every remote machine specified in the servers
array.
copy_to_remote <local_path> <remote_path>
-- Use scp
to recursively copy files to every remote machine.
The open_tunnel
function can be used to open-close SSH tunnels in a safe way.
open_tunnel <server> <remote_addr>
-- Open an SSH tunnel for a random local port which can be accessed through the $local_port
variable. You script has 10 seconds to open a connection to $local_port
and after disconnection the SSH tunnel will be automatically terminated.
Let's take the following example: you would like to create a tunnel to access Postgres DB running on 192.168.100.100
port 5432
which is only reachable through appserver.com
.
open_tunnel "appserver.com" "192.168.100.100:5432"
run_locally "PGPASSWORD=pw psql -h localhost -p $local_port -U user -d db < script.sql"
Bashistrano is not yet and (as almost all open-source projects) probably never will be complete. The following features can provide significant improvements and are planned to be implemented in the future.
If you consider to lend a helping hand visit the Contributing section.
Planned features:
- parse and handle command line flags
- user input validation
- error handling
- rollback functionality
- check code integrity
- check for new versions
- generate initial folder structure
- generate additional stages
Bashistrano is an open-source project, contributions and feedbacks are welcome as always!
- Fork it ( http://github.com/team-supercharge/bashistrano/fork )
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
Copyright (c) 2018 Supercharge
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.