Re-usable, composable Capistrano tasks for git tagging and other work with the git repository you use for your Cap deploys.
- Ensure your local git is committed and pushed, so you are deploying what you think you are.
- Automatically git tag the deploy
- Enforce a multistage workflow where only a tagged staging release can be deployed to production (ala gitflow)
Functionality is split into discrete yet composable tasks, with sensible defaults but configurable, so you can build a cap recipe that fits your requirements and workflow. Ordinary single stage or multi-stage; with or without interactive confirmation prompts; using git however you're already or would like to be using it.
(_Inspired by Alan Pinstein and Josh Nichols' neat gitflow, but refactored for more flexiblity with less hardcoded workflow. Some functionality changed in the process.)
gem install cap_git_tools
Or if in the context of something using bundler such as Rails, add to Gemfile eg:
gem 'cap_git_tools', :group => :development
Add to top of a relevant Capistrano file (such as config/deploy.rb ordinarily):
require 'cap_git_tools/tasks'
This makes cap_git_tool's tasks available to you, but doesn't automatically wire
them up to be used by your cap deploy
. See below.
You probably want to make sure you have an ssh-agent
set up, or you'll have to
enter your private key password to access git several times.
Note: You will need to be running your cap recipes from a directory with a git checkout (usual behavior for cap usage, but not actually required otherwise by cap).
Have you ever deployed the 'wrong' thing, because you forgot to commit and/or push your changes to git? I have.
Have cap make sure you're committed and pushed before deploying by adding to your recipe in deploy.rb:
before "deploy:update_code", "git:guard_committed", "git:guard_upstream"
Or use just one or the other
guard_committed
makes sure you have no uncommitted changes. IF you have a :branch set in your cap recipe, it will also make sure your curent checkout matches that branch.guard_upstream
makes sure the current working copy branch committed tip (or local branch matching Cap :branch, if set) matches the upstream remote version.
Every time you deploy, want to have Capistrano automatically tag exactly what gets deployed, with a tag like "deploy-2012-04-11-1517"?
Add this to your Cap recipe, usefully combining with the tasks to make sure your git copy is 'clean' as discussed above:
before "deploy:update_code", "git:guard_committed", "git:guard_upstream", "git:tag"
That's a date and timestamp, deploy-yyyy-mm-dd-hhmm.
If you are using multistage, instead of "deploy-" as a prefix, it'll use the current stage name like "production" or "staging" (but see below for fancier multi-stage workflow).
Ordinarily what's in your current git checkout will be tagged; but if
you have set cap's :branch
, it'll tag and deploy the HEAD of that branch
even if that's not your current checkout.
You can customize the prefix and other aspects of tagging, both in your recipe
and with command line over-rides, see cap -e git:tag
for more info.
Are you using Capistrano's multistage extension? In one commonly desired multistage workflow (similar to what gitflow enforces):
-
Under staging, you want automatic tagging with staging-yyyy-mm-dd-hhmm, just as above under 'Automatically tag on deploy'. Add to your
config/deploy/staging.rb
:before "deploy:update_code", "git:guard_committed", "git:guard_upstream", "git:tag"
-
Under production, you want to take the most recent 'staging' tag, and promote it by deploying that tag to production, re-tagging with a "production-" tag. Maybe you also want to print out the commit log between the last production tag and what you're about to deploy, and require interactive confirmation. Add to your
config/deploy/production.rb
:before "deploy:update_code", "git:commit_log", "git:retag" set :confirm_tag, true
Say you cap staging deploy
on April 1 2012 at noon, your deploy will be
tagged staging-2012-04-01-1200
.
Say on April 2 at noon, you run cap production deploy
- you'll be a shown a commit log of changes between the previous
production-
commit and your most recentstaging-
commit,staging-2012-04-01-1200
. (git:commit_log
) - You'll be asked to confirm, (
set :confirm_tag, true
) - And then the deploy will happen, with new tag added
production-2012-04-02-1200
(git:retag
).- Note it's timestamped with date of production deploy. The commit message
for the
production-
tag will say whichstaging-
tag was retagged.
- Note it's timestamped with date of production deploy. The commit message
for the
The git:retag
task has some configurable options (in your recipe or on the
individual command line invocation) too, see cap -e git:retag
.
Look at cap -T git
to see the tasks added by cap_git_tools. Run cap -e taskname
to see expanded documentation info on each one, covering more
specifics of what it does and what cap variables can alter it's behavior.
Some behaviors can be customized by 'capistrano variables'. These can be set in a recipe:
set :variable, "value"
Or set/over-ridden on the individual cap command line invocation:
cap deploy -s variable=value
Doesn't matter if you use cap '-s' or '-S', cap_git_tools tasks always lazily look up these values.
cap git:commit_log
to see the commits between the last tagged release
and what you'd deploy now with cap deploy
. Works in singlestage recipe, or
multistage under 'cap staging git:commit_log' or 'cap production
git:commit_log'.
cap git:show_tags
to show the last 3 deploy tags, with meta information.
Works in single stage recipe or multistage.
-
Tag names are automatically created with a year-month-day-hour-minute timestamp. However, if you try to deploy again before the minute's changed on the clock, the tasks will try to re-tag using an already used name. You'll get an error and the task will abort, but the task could be written to catch this and add a suffix. But it ain't yet.
-
There is some limited experimental functionality to change the format and add new components to the automatically created tag name, using a :tag_template variable. This theoretically allows the
who
andwhat
components used by gitflow. But doing the 'right thing' in multistage (copying the 'what' from the previous tag, but regenerating the rest) is a bit tricky, and hasn't been done yet, which is what keeps this functionality limited and experimental at this point.
Feedback, pull requests, complaints, welcome. Not sure if anyone's gonna use this.