cap_git_tools 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in cap_git_tools.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # cap_git_tools
2
+
3
+ Re-usable, composable [Capistrano](https://github.com/capistrano/capistrano) tasks for git tagging and other work with the
4
+ git repository you use for your Cap deploys.
5
+
6
+ * Ensure your local git is committed and pushed, so you are deploying what you
7
+ think you are.
8
+ * Automatically git tag the deploy
9
+ * Enforce a [multistage](https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension) workflow where only a tagged staging release can be deployed
10
+ to production (ala [gitflow](https://github.com/apinstein/git-deployment))
11
+
12
+ Functionality is split into discrete yet composable tasks, with sensible defaults
13
+ but configurable, so you can build a cap recipe that fits *your*
14
+ requirements and workflow. Ordinary single stage or multi-stage; with or without
15
+ interactive confirmation prompts; using git however you're already or would like
16
+ to be using it.
17
+
18
+ (_Inspired by Alan Pinstein and Josh Nichols' neat
19
+ [gitflow](https://github.com/apinstein/git-deployment), but refactored for more
20
+ flexiblity with less hardcoded workflow. Some functionality changed in the
21
+ process.)_
22
+
23
+ ## Installation
24
+
25
+ gem install cap_git_tools
26
+
27
+ Or if in the context of something using bundler such as Rails, add to Gemfile
28
+ eg:
29
+
30
+ gem 'cap_git_tools', :group => :development
31
+
32
+ Add to top of a relevant Capistrano file (such as config/deploy.rb ordinarily):
33
+
34
+ require 'cap_git_tools/tasks'
35
+
36
+ This makes cap_git_tool's tasks available to you, but doesn't automatically wire
37
+ them up to be used by your `cap deploy`. See below.
38
+
39
+ You probably want to make sure you have an `ssh-agent` set up, or you'll have to
40
+ enter your private key password to access git several times. You will need to be
41
+ running your cap recipes from a directory with a git checkout (usual behavior
42
+ for cap usage, but not actually required otherwise by cap).
43
+
44
+ ## Ensure git is committed and pushed when deploying
45
+
46
+ Have you ever deployed the 'wrong' thing, because you forgot to commit and/or
47
+ push your changes to git? I have.
48
+
49
+ Have cap make sure you're committed and pushed before deploying by adding to
50
+ your recipe in deploy.rb:
51
+
52
+ before "deploy:update_code", "git:guard_committed", "git:guard_upstream"
53
+
54
+ Or use just one or the other
55
+
56
+ * `guard_committed` makes sure you have no uncommitted changes. _IF_ you have a :branch set
57
+ in your cap recipe, it will also make sure your curent checkout matches that branch.
58
+ * `guard_upstream` makes sure the current working copy branch committed tip (or local branch matching Cap
59
+ :branch, if set) matches the upstream remote version.
60
+
61
+ ## Automatically tag on deploy
62
+
63
+ Every time you deploy, want to have Capistrano automatically tag exactly what
64
+ gets deployed, with a tag like "deploy-2012-04-11-1517"?
65
+
66
+ Add this to your Cap recipe, usefully combining with the tasks to make sure
67
+ your git copy is 'clean' as discussed above:
68
+
69
+ before "deploy:update_code", "git:guard_committed", "git:guard_pushed", "git:tag"
70
+
71
+ That's a date and timestamp, deploy-yyyy-mm-dd-hhmm.
72
+
73
+ If you are using multistage, instead of "deploy-" as a prefix, it'll use
74
+ the current stage name like "production" or "staging" (but see below for
75
+ fancier multi-stage workflow).
76
+
77
+ Ordinarily what's in your current git checkout will be tagged; but if
78
+ you have set cap's `:branch`, it'll tag and deploy the HEAD of that branch
79
+ even if that's not your current checkout.
80
+
81
+ You can customize the prefix and other aspects of tagging, both in your recipe
82
+ and with command line over-rides, see `cap -E git:tag` for more info.
83
+
84
+ ## Multistage workflow
85
+
86
+ Are you using Capistrano's [multistage
87
+ extension](https://github.com/capistrano/capistrano/wiki/2.x-Multistage-Extension)?
88
+ In one commonly desired multistage workflow (similar to what
89
+ [gitflow](https://github.com/apinstein/git-deployment) enforces):
90
+
91
+ * Under staging, you want automatic tagging with staging-yyyy-mm-dd-hhmm, just
92
+ as above under 'Automatically tag on deploy'. Add to your `config/staging.rb`:
93
+
94
+ before "deploy:update_code", "git:guard_committed", "git:guard_upstream", "git:tag"
95
+
96
+ * Under production, you want to take the most recent 'staging' tag, and promote
97
+ it by deploying that tag to production, re-tagging with a "production-" tag.
98
+ Maybe you also want to print out the commit log between the last production
99
+ tag and what you're about to deploy, and require interactive confirmation.
100
+ Add to your `config/deploy.rb`:
101
+
102
+ before "deploy:update_code", "git:commit_log", "git:retag"
103
+ set :confirm_tag, true
104
+
105
+ Say you `cap staging deploy` on April 1 2012 at noon, your deploy will be
106
+ tagged `staging-2012-04-01-1200`.
107
+
108
+ Say on April 2 at noon, you run `cap production deploy`
109
+
110
+ * you'll be a shown a commit log of changes between the previous `production-`
111
+ commit and your most recent `staging-` commit, `staging-2012-04-01-1200`.
112
+ (`git:commit_log`)
113
+ * You'll be asked to confirm, (`set :confirm_tag, true`)
114
+ * And then the deploy will happen, with new tag added `production-2012-04-02-1200`
115
+ (`git:retag`).
116
+ * Note it's timestamped with date of production deploy. The commit message
117
+ for the `production-` tag will say which `staging-` tag was retagged.
118
+
119
+ The `git:retag` task has some configurable options (in your recipe or on the
120
+ individual command line invocation) too, see `cap -e git:retag`.
121
+
122
+ ## Make your own recipe
123
+
124
+ Look at `cap -T git` to see the tasks added by cap_git_tools. Run `cap -e
125
+ taskname` to see expanded documentation info on each one, covering more
126
+ specifics of what it does and what cap variables can alter it's behavior.
127
+
128
+ Some behaviors can be customized by 'capistrano variables'. These can be set in
129
+ a recipe:
130
+
131
+ set :variable, "value"
132
+
133
+ Or set/over-ridden on the individual cap command line invocation:
134
+
135
+ cap deploy -s variable=value
136
+
137
+ Doesn't matter if you use cap '-s' or '-S', cap_git_tools tasks always lazily
138
+ look up these values.
139
+
140
+ ## Other tools
141
+
142
+ `cap git:commit_log` to see the commits between the *last* tagged release
143
+ and what you'd deploy now with `cap deploy`. Works in singlestage recipe, or
144
+ multistage under 'cap staging git:commit_log' or 'cap production
145
+ git:commit_log'.
146
+
147
+ `cap git:show_tags` to show the last 3 deploy tags, with meta information.
148
+ Works in single stage recipe or multistage.
149
+
150
+ ## To Be Done
151
+
152
+ * Tag names are automatically created with a year-month-day-hour-minute timestamp.
153
+ However, if you try to deploy again before the minute's changed on the clock,
154
+ the tasks will try to re-tag using an already used name. You'll get an error and
155
+ the task will abort, but the task could be written to catch this and add a
156
+ suffix. But it ain't yet.
157
+
158
+ * There is some limited experimental functionality to change the format and add
159
+ new components to the automatically created tag name, using a
160
+ [:tag_template](https://github.com/jrochkind/cap_git_tools/blob/master/lib/cap_git_tools/task_helpers.rb#L162)
161
+ variable. This theoretically allows the `who` and `what` components used by
162
+ gitflow. But doing the 'right thing' in multistage (copying the 'what' from the
163
+ previous tag, but regenerating the rest) is a bit tricky, and hasn't been done
164
+ yet, which is what keeps this functionality limited and experimental at this
165
+ point.
166
+
167
+ ## Let me know
168
+
169
+ Feedback, pull requests, complaints, welcome. Not sure if anyone's gonna use
170
+ this.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "cap_git_tools/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "cap_git_tools"
7
+ s.version = CapGitTools::VERSION
8
+ s.authors = ["Jonathan Rochkind"]
9
+ s.email = ["jonathan@dnil.net"]
10
+ s.homepage = ""
11
+ s.summary = %q{re-usable, composable Capistrano tasks for git tagging and other work with a git repo}
12
+
13
+ s.rubyforge_project = "cap_git_tools"
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = ["lib"]
19
+
20
+
21
+ s.add_dependency "capistrano", "~> 2.0"
22
+ end
@@ -0,0 +1,5 @@
1
+ require "cap_git_tools/version"
2
+
3
+ module CapGitTools
4
+ # We don't really do anything by default on gem load.
5
+ end
@@ -0,0 +1,194 @@
1
+ # methods used by tasks defined in tasks.rb
2
+ #
3
+ # generally this module is 'include'd into a cap
4
+ # :namespace, seems to do what we want.
5
+ require 'cap_git_tools'
6
+
7
+ module CapGitTools::TaskHelpers
8
+ ####
9
+ # Some functions used by the tasks
10
+ #
11
+
12
+ # say with an indent in spaces
13
+ def say_formatted(msg, options = {})
14
+ options.merge!(:indent => 4)
15
+ Capistrano::CLI.ui.say(' ' * options[:indent] + msg )
16
+ end
17
+
18
+ # execute a 'git fetch', but mark in a private variable that
19
+ # we have, so we only do it once per cap execution.
20
+ def ensure_git_fetch
21
+ unless @__git_fetched
22
+ local_sh "git fetch #{upstream_remote}"
23
+ @__git_fetched = true
24
+ end
25
+ end
26
+
27
+ # execute locally as a shell command, echo'ing to output, as
28
+ # well as capturing error and aborting.
29
+ def local_sh(cmd)
30
+ say_formatted("executing locally: #{cmd}")
31
+ `#{cmd}`
32
+ abort("failed: #{cmd}") unless $? == 0
33
+ end
34
+
35
+ # How to refer to the upstream git repo configured in cap :repository?
36
+ # Will _usually_ return 'origin', will sometimes return another remote,
37
+ # will occasionally return a raw git url when it's not configured in
38
+ # remotes for some reason.
39
+ #
40
+ # This used to be hard-coded to 'origin'. Then it was configurable.
41
+ # Then I realized it _has_ to be whatever is set in cap :repository.
42
+ # We'll look up the remote alias for that, if available, and cache
43
+ # the lookup. Usually it'll be 'origin', yeah.
44
+ def upstream_remote
45
+ @__upstream_remote = begin
46
+ git_url = fetch(:repository)
47
+
48
+ remote_info =
49
+ `git remote -v`.
50
+ split("\n").
51
+ collect {|line| line.split(/[\t ]/) }.
52
+ find {|list| list[1] == git_url }
53
+
54
+ remote_info ? remote_info[0] : git_url
55
+ end
56
+ end
57
+
58
+
59
+ # what branch we're going to tag and deploy -- if cap 'branch' is set,
60
+ # use that one, otherwise use current branch in checkout
61
+ def working_branch
62
+ @__git_working_branch ||= begin
63
+ if exists?("branch")
64
+ fetch(:branch)
65
+ else
66
+ b = `git symbolic-ref -q HEAD`.sub(%r{^refs/heads/}, '').chomp
67
+ b.empty? ? "HEAD" : b
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ # current SHA fingerprint of local branch mentioned in :branch
74
+ def local_sha
75
+ `git log --pretty=format:%H #{working_branch} -1`.chomp
76
+ end
77
+
78
+ def tag_prefix
79
+ fetch(:tag_prefix, fetch(:stage, "deploy"))
80
+ end
81
+
82
+ def from_prefix
83
+ fetch("from_prefix", "staging")
84
+ end
85
+
86
+
87
+ # mostly used by git:retag, calculate the tag we'll be retagging FROM.
88
+ #
89
+ # can set cap :from_tag. Or else find last tag matching from_prefix,
90
+ # which by default is "staging-*"
91
+ def from_tag
92
+ t = nil
93
+ if exists?("from_tag")
94
+ t = fetch("from_tag")
95
+ else
96
+ t = fetch_last_tag( self.from_prefix )
97
+
98
+ if t.nil? || t.empty?
99
+ abort("failed: can't find existing tag matching #{self.from_prefix}-*")
100
+ end
101
+ end
102
+ return t
103
+ end
104
+
105
+ # find the last (chronological) tag with given prefix.
106
+ # prefix can include shell-style wildcards like '*'. Defaults to
107
+ # last tag with current default tag_prefix.
108
+ #
109
+ # Note: Will only work on git 'full' annotated tags (those signed or
110
+ # with -m message or -a) because git only stores dates for annotated tags.
111
+ # others will end up sorted lexicographically BEFORE any annotated tags.
112
+ def fetch_last_tag(pattern_prefix = self.tag_prefix)
113
+ # make sure we've fetched to get latest from upstream.
114
+ ensure_git_fetch
115
+
116
+ # crazy git command, yeah. Sort by tagged date descending, one line only,
117
+ # output refname:short, look for tags matching our pattern.
118
+ last_tag = `git for-each-ref --count=1 --sort='-taggerdate' --format='%(refname:short)' 'refs/tags/#{pattern_prefix}-*' 2>/dev/null`.chomp
119
+ return nil if last_tag == ''
120
+ return last_tag
121
+ end
122
+
123
+ # show commit lot from commit-ish to commit-ish,
124
+ # using appropriate UI tool.
125
+ #
126
+ # If you have cap :github_browser_compare set and the remote is github,
127
+ # use `open` to open in browser.
128
+ #
129
+ # else if you have ENV['git_log_command'] set, pass to `git` (don't know
130
+ # what this is for, inherited from gitflow)
131
+ #
132
+ # else just use an ordinary command line git log
133
+ def show_commit_log(from_tag, to_tag)
134
+ if fetch("github_browser_compare", false ) && `git config remote.#{upstream_remote}.url` =~ /git@github.com:(.*)\/(.*).git/
135
+ # be awesome for github, use `open` in browser
136
+ command = "open https://github.com/#{$1}/#{$2}/compare/#{from_tag}...#{to_tag}"
137
+ elsif ENV['git_log_command'] && ENV['git_log_command'].strip != ''
138
+ # use custom compare command if set
139
+ command = "git #{ENV['git_log_command']} #{from_tag}..#{to_tag}"
140
+ else
141
+ # standard git log command
142
+ command = "git log #{from_tag}..#{to_tag}"
143
+ end
144
+
145
+ say_formatted "Displaying commits from #{from_tag} to #{to_tag}\n\n"
146
+ say_formatted command + "\n\n"
147
+ system command
148
+ puts "" # newline
149
+ end
150
+
151
+
152
+ def calculate_new_tag
153
+ # if capistrano :tag is already set, just use it
154
+ if exists?("tag")
155
+ return fetch("tag")
156
+ end
157
+
158
+ # otherwise calculate, based on template
159
+
160
+ tag_suffix = fetch("tag_template", "%{datetime}")
161
+
162
+ tag_suffix.gsub!(/\%\{([^}]+)\}/) do
163
+ case $1
164
+ when 'date'
165
+ Time.now.localtime.strftime('%Y-%m-%d')
166
+ when 'datetime'
167
+ Time.now.localtime.strftime('%Y-%m-%d-%H%M')
168
+ when 'what'
169
+ (@__git_what = Capistrano::CLI.ui.ask("What does this release introduce? (this will be normalized and used in the tag for this release) ").gsub(/[ '"]+/, "_"))
170
+ when 'who'
171
+ `whoami`.chomp
172
+ end
173
+ end
174
+
175
+ return "#{tag_prefix}-#{tag_suffix}"
176
+ end
177
+
178
+ # will prompt to confirm new tag, if :confirm_tag is true, otherwise
179
+ # no-op.
180
+ def guard_confirm_tag(new_tag)
181
+ if exists?("confirm_tag") && [true, "true"].include?( confirm_tag )
182
+ prompt = "Do you really want to deploy "
183
+ prompt += new_tag
184
+ prompt += " to #{stage}" if exists? :stage
185
+ prompt += "?"
186
+
187
+ unless Capistrano::CLI.ui.agree(prompt)
188
+ abort("exiting, user cancelled.")
189
+ end
190
+ end
191
+ end
192
+
193
+
194
+ end
@@ -0,0 +1,301 @@
1
+ # Written using crazy meta-code cribbed from capistrano_ext so you
2
+ # can (and must) 'require' this file rather than 'load' it.
3
+ require 'capistrano'
4
+
5
+ unless Capistrano::Configuration.respond_to?(:instance)
6
+ abort "cap_git_tools requires Capistrano 2"
7
+ end
8
+
9
+ require 'cap_git_tools/task_helpers'
10
+
11
+
12
+
13
+ Capistrano::Configuration.instance.load do
14
+
15
+
16
+ namespace :git do
17
+ # include our helper methods, I believe just into this namespace
18
+ # if we do it this way.
19
+ extend CapGitTools::TaskHelpers
20
+
21
+ desc <<-DESC
22
+ Ensure git working copy has no uncommitted changes, or abort.
23
+
24
+ If cap :branch is set, will also ensure git working copy is on same
25
+ branch as :cap branch.
26
+
27
+ The idea is to make sure you're deploying what you're looking at.
28
+ See also git:guard_upstream, you often want to use both to ensure
29
+ this.
30
+
31
+ before "git:tag", "git:check_committed", "git:check_upstream"
32
+ or
33
+ before "deploy", , "git:check_committed", "git:check_upstream"
34
+
35
+ setting cap :skip_guard_committed to true will skip even if task is
36
+ invoked. (eg, `cap deploy -s skip_guard_upstream=true`)
37
+ DESC
38
+ task :guard_committed do
39
+ if [true, "true"].include? fetch("skip_guard_committed", false)
40
+ say_formatted("Skipping git:guard_committed")
41
+ else
42
+ if exists?("branch")
43
+ working_branch = `git symbolic-ref -q HEAD`.sub(%r{^refs/heads/}, '').chomp
44
+ unless fetch("branch") == working_branch
45
+ abort %Q{failed: guard_committed: wrong branch
46
+
47
+ You have configured to deploy from branch #{fetch("branch")}
48
+ but your git working copy is on branch #{working_branch}
49
+
50
+ git checkout #{fetch("branch")}
51
+
52
+ and try again. Or, to skip this check, execute cap again with:
53
+
54
+ -s skip_guard_committed=true
55
+ }
56
+ end
57
+ end
58
+
59
+ # cribbed from bundle release rake task
60
+ `git diff HEAD --exit-code`
61
+ return_code = $?.to_i
62
+ if return_code == 0
63
+ say_formatted("guard_clean: passed")
64
+ else
65
+ abort %Q{failed: guard_committed: uncomitted changes
66
+
67
+ There are files that need to be committed first.
68
+
69
+ Or, to skip this check, execute cap again with:
70
+ -s skip_guard_committed=true
71
+ }
72
+ end
73
+ end
74
+ end
75
+
76
+ desc <<-DESC
77
+ Ensure sure local git has been pushed to upstream, or abort
78
+
79
+ * 'upstream' is whatever you have configured as cap :repository
80
+ * Looks in :branch (default 'master') to see what branch should be checked,
81
+ Assumes local :branch tracks upstream_remote/branch
82
+
83
+ The idea is to ensure what you're deploying is what you're looking at.
84
+ See also git:guard_committed, you usually want to use both to ensure this.
85
+
86
+ before "git:tag", "git:check_committed", "git:check_upstream"
87
+ or if not using git:tag, eg
88
+ before "deploy", , "git:check_committed", "git:check_upstream"
89
+
90
+ setting cap :skip_guard_upstream to truewill skip even if task is invoked.
91
+ (eg, `cap deploy -s skip_guard_upstream=true`)
92
+ DESC
93
+ task :guard_upstream do
94
+ if [true, "true"].include? fetch("skip_guard_upstream", false)
95
+ say_formatted("Skipping git:guard_upstream")
96
+ else
97
+
98
+ ensure_git_fetch
99
+
100
+ remote_sha = `git log --pretty=format:%H #{upstream_remote}/#{working_branch} -1`.chomp
101
+
102
+ unless local_sha == remote_sha
103
+ abort %Q{failed:
104
+ Your local #{working_branch} branch is not up to date with #{upstream_remote}/#{working_branch}.
105
+ This will likely result in deploying something other than you expect.
106
+
107
+ Please make sure you have pulled and pushed all code before deploying:
108
+
109
+ git pull #{upstream_remote} #{working_branch}
110
+ # run tests, etc
111
+ git push #{upstream_remote} #{working_branch}
112
+
113
+ Or, to skip this check run cap again with `-s skip_guard_upstream=true`
114
+ }
115
+ end
116
+
117
+ say_formatted("guard_upstream: passed")
118
+ end
119
+ end
120
+
121
+ desc <<-DESC
122
+ Tags the current checkout and pushes tag to remote.
123
+
124
+ Normally will tag and deploy whatever is in your current git working
125
+ copy -- you may want to use with the guard tasks to make sure
126
+ you're deploying what you think and sync'ing it to your upstream
127
+ repository:
128
+
129
+ before "deploy", "git:guard_committed", "git:guard_upstream", "git:tag"
130
+
131
+ However, if you have set cap :branch, git:retag will tag the HEAD
132
+ of THAT branch, rather than whatever is the current working copy
133
+ branch.
134
+
135
+ Either way, git:tag:
136
+ * pushes the new tag to upstream remote git
137
+ * sets the cap :branch variable to the newly created tag, to be
138
+ sure cap deploys that tag.
139
+
140
+ What will the created tag look like?
141
+
142
+ Without multi-stage, by default something like `deploy-yyyy-mm-dd-hhmm`.
143
+
144
+ * The deploy- prefix will be the current stage name if multi-stage.
145
+ * The prefix can be manually set in config file or command line
146
+ with cap :tag_prefix variable instead.
147
+ * Somewhat experimental, you can also set :tag_template to change the
148
+ part after the prefix.
149
+ * or you can specify a complete tag on the cap command line `-s tag=deploy-whatever`.
150
+
151
+ `set :confirm_tag, true` in the config file to force an interactive
152
+ prompt and confirmation before continuing.
153
+ DESC
154
+ task :tag do
155
+
156
+ # make sure we have any other deployment tags that have been pushed by
157
+ # others so our auto-increment code doesn't create conflicting tags
158
+ ensure_git_fetch
159
+
160
+ tag = calculate_new_tag
161
+
162
+ commit_msg = @__git_what || "cap git:tag: #{tag}"
163
+
164
+ self.guard_confirm_tag(tag)
165
+
166
+ # tag 'working_branch', means :branch if set, otherwise
167
+ # current working directory checkout.
168
+ local_sh "git tag -a -m '#{commit_msg}' #{tag} #{self.working_branch}"
169
+
170
+ # Push new tag back to origin
171
+ local_sh "git push -q #{upstream_remote} #{tag}"
172
+
173
+ # set :branch to tag, so cap will continue on to deploy the tag we just created!
174
+ set(:branch, tag)
175
+ end
176
+
177
+ desc <<-DESC
178
+ Takes an already existing tag, and retags it and deploys that tag.
179
+
180
+ Will push the new tag to upstream repo, and set the new tag as cap
181
+ :branch so cap willd deploy it.
182
+
183
+ Usually used in git multistage for moving from staging to production,
184
+ for instance in your production.rb:
185
+
186
+ before "deploy", "git:retag"
187
+
188
+ Or use with the guard tasks:
189
+ before "deploy", "git:guard_committed", "git:guard_upstream", "git:retag"
190
+
191
+ `set :confirm_tag, true` in the config file to force an interactive
192
+ prompt and confirmation before continuing.
193
+
194
+ What tag will be used as source tag?
195
+
196
+ * Normally the most recent tag beginning "staging-"
197
+ * Or set cap :tag_prefix in config file or command line
198
+ to use a different prefix.
199
+ * Or set :tag_from in config file or on command line
200
+ to specify a specific tag. `cap deploy -s tag_from=staging_something`
201
+
202
+ What will the newly created tag look like? Same rules as for
203
+ git:tag.
204
+
205
+ * By default in a production stage it's going to look
206
+ like `production-yyyy-mm-dd-hhmm`, but there are several
207
+ of cap variables you can set in a config file to change this,
208
+ including :tag_prefix and :tag_template
209
+ DESC
210
+ task :retag do
211
+ from_tag = self.from_tag
212
+
213
+ to_tag = calculate_new_tag
214
+
215
+ self.guard_confirm_tag(from_tag)
216
+
217
+ say_formatted("git:retag taking #{from_tag} and retagging as #{to_tag}")
218
+
219
+ local_sh "git tag -a -m 'tagging #{from_tag} for deployment as #{to_tag}' #{to_tag} #{from_tag}"
220
+
221
+ # Push new tag back to origin
222
+ local_sh "git push -q #{upstream_remote} #{to_tag}"
223
+
224
+ set(:branch, to_tag)
225
+ end
226
+
227
+ desc <<-DESC
228
+ Show 5 most recent tags set by git:tag or git:retag
229
+
230
+ Can be used to see what you've deployed recently.
231
+
232
+ cap git:show_tags
233
+ or for multi-stage:
234
+ cap staging git:show_tags
235
+ cap production git:show_tags
236
+
237
+ Looks for tags matching the prefix that the tag or retag task
238
+ would use to tag. Ie, 'deploy-', or 'stagename-' in multi-stage,
239
+ or according to :tag_prefix setting.
240
+
241
+ Will also output date of tag, commit message, and account doing the commit.
242
+ DESC
243
+ task :show_tags do
244
+ # in newer versions of git you could prob do this with a git-log instead with
245
+ # certain arguments, but my local git is too old to support --tags arg properly.
246
+ system "git for-each-ref --count=4 --sort='-taggerdate' --format='\n* %(refname:short)\n Tagger: %(taggeremail)\n Date: %(taggerdate)\n\n %(subject)' 'refs/tags/#{tag_prefix}-*' "
247
+ end
248
+
249
+
250
+ desc <<-DESC
251
+ Show log between most tagged deploy and what would be deployed now.
252
+
253
+ Requires you to be using git:tag or git:retag to make any sense,
254
+ so we can find the 'last deployed' tag to compare.
255
+
256
+ You can run this manually:
257
+ cap git:commit_log
258
+ Or for multi-stage, perhaps:
259
+ cap staging git:commit_log
260
+ cap production git:commit_log
261
+ Or you can use cap callbacks to ensure this is shown before
262
+ a deploy, for multi-stage for instance add to your production.rb:
263
+ before "git:retag", "git:commit_log"
264
+ # and force an interactive confirmation after they've seen it
265
+ set :confirm_retag, true
266
+
267
+ Ordinarily shows commit log between current git working copy
268
+ (forced to current head of :branch if cap :branch is set), and
269
+ last deployed tag, by default tag beginning "deploy-", or
270
+ tag beginning :stage if you are cap multi-stage, or beginning
271
+ with :tag_prefix if that is set.
272
+
273
+ Hard-coded to do something special if you are using multistage
274
+ cap, and are in stage 'production' -- in that case it will
275
+ show you the commits between the most recent production-* tag,
276
+ and the most recent staging-* tag. (Cap :tag_prefix and :from_prefix
277
+ can change those tag prefixes).
278
+ DESC
279
+ task :commit_log do
280
+ from, to = nil, nil
281
+
282
+ if exists?("stage") && stage.to_s == "production"
283
+ # production stage in multi-stage
284
+ from = fetch_last_tag # last deploy-* tag, or last :tag_prefix tag
285
+ to = from_tag # last staging-* tag, or last :from_prefix tag
286
+ else
287
+ # 'staging' stage in multi-stage, or else any old
288
+ # non-multistage.
289
+ from = fetch_last_tag # last deploy-* tag, or last :tag_prefix tag
290
+ to = local_sha.slice(0,8) # current git working copy, or local branch head.
291
+ end
292
+
293
+ show_commit_log(from, to)
294
+ end
295
+
296
+
297
+
298
+ end
299
+
300
+
301
+ end
@@ -0,0 +1,3 @@
1
+ module CapGitTools
2
+ VERSION = "0.8.0"
3
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cap_git_tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jonathan Rochkind
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-06-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: capistrano
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.0'
30
+ description:
31
+ email:
32
+ - jonathan@dnil.net
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - README.md
40
+ - Rakefile
41
+ - cap_git_tools.gemspec
42
+ - lib/cap_git_tools.rb
43
+ - lib/cap_git_tools/task_helpers.rb
44
+ - lib/cap_git_tools/tasks.rb
45
+ - lib/cap_git_tools/version.rb
46
+ homepage: ''
47
+ licenses: []
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project: cap_git_tools
66
+ rubygems_version: 1.8.24
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: re-usable, composable Capistrano tasks for git tagging and other work with
70
+ a git repo
71
+ test_files: []