zilkey-auto_tagger 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +36 -26
- data/lib/auto_tagger/auto_tagger.rb +2 -2
- data/lib/auto_tagger/capistrano_helper.rb +14 -13
- data/lib/auto_tagger/repository.rb +4 -0
- data/lib/auto_tagger/stage_manager.rb +23 -0
- data/lib/auto_tagger/tag.rb +6 -3
- data/lib/auto_tagger.rb +1 -0
- data/recipes/release_tagger.rb +30 -4
- data/spec/auto_tagger/auto_tagger_spec.rb +13 -0
- data/spec/auto_tagger/capistrano_helper_spec.rb +19 -18
- data/spec/auto_tagger/stage_manager_spec.rb +30 -0
- metadata +3 -1
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# AutoTagger
|
2
2
|
|
3
|
-
AutoTagger
|
3
|
+
AutoTagger is a gem that helps you automatically create a date-stamped tag for each stage of your deployment, and deploy from the last tag from the previous environment.
|
4
4
|
|
5
5
|
Let's say you have the following workflow:
|
6
6
|
|
@@ -10,9 +10,38 @@ Let's say you have the following workflow:
|
|
10
10
|
|
11
11
|
You can use the `autotag` command to tag releases on your CI box, then use the capistrano tasks to auto-tag each release.
|
12
12
|
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
gem sources -a http://gems.github.com
|
16
|
+
sudo gem install zilkey-auto_tagger
|
17
|
+
|
18
|
+
## Contribute
|
19
|
+
|
20
|
+
* [Tracker Project](http://www.pivotaltracker.com/projects/11988)
|
21
|
+
* [GitHub Repository](http://github.com/zilkey/auto_tagger/tree/master)
|
22
|
+
|
23
|
+
## The autotag executable
|
24
|
+
|
25
|
+
Installing the gem creates an executable file named autotag, which takes two arguments: the stage, and optionally the path to the git repo:
|
26
|
+
|
27
|
+
$ autotag demo # => creates a tag like demo/200804041234 in the current directory
|
28
|
+
$ autotag demo . # => same as above
|
29
|
+
$ autotag demo /Users/me/foo # => cd's to /Users/me/foo before creating the tag
|
30
|
+
|
31
|
+
Running autotag does the following:
|
32
|
+
|
33
|
+
$ git fetch origin --tags
|
34
|
+
$ git tag <stage>/<timestamp>
|
35
|
+
$ git push origin --tags
|
36
|
+
|
13
37
|
## Capistrano Integration
|
14
38
|
|
15
|
-
|
39
|
+
AutoTagger comes with 2 capistrano tasks:
|
40
|
+
|
41
|
+
* `release_tagger:set_branch` tries to set the branch to the last tag from the previous environment.
|
42
|
+
* `release_tagger:create_tag` runs autotag for the current stage
|
43
|
+
|
44
|
+
Example `config/deploy.rb` file:
|
16
45
|
|
17
46
|
require 'release_tagger'
|
18
47
|
|
@@ -35,7 +64,7 @@ Example deploy file:
|
|
35
64
|
|
36
65
|
# You need to add the before/ater callbacks yourself
|
37
66
|
before "deploy:update_code", "release_tagger:set_branch"
|
38
|
-
after "deploy
|
67
|
+
after "deploy", "release_tagger:create_tag"
|
39
68
|
|
40
69
|
Assume you have the following tags in your git repository:
|
41
70
|
|
@@ -45,32 +74,13 @@ Assume you have the following tags in your git repository:
|
|
45
74
|
|
46
75
|
The deployments would look like this:
|
47
76
|
|
48
|
-
cap staging deploy # => ci/01
|
49
|
-
cap production deploy # => staging/01
|
77
|
+
cap staging deploy # => sets branch to ci/01
|
78
|
+
cap production deploy # => sets branch to staging/01
|
50
79
|
|
51
80
|
You can override with with the -Shead and -Stag options
|
52
81
|
|
53
|
-
cap staging deploy -Shead=true # => master
|
54
|
-
cap staging deploy -Stag=staging/01 # => staging/01
|
55
|
-
|
56
|
-
## The autotag executable
|
57
|
-
|
58
|
-
autotag -h
|
59
|
-
autotag demo
|
60
|
-
autotag demo .
|
61
|
-
autotag demo /Users/me/foo
|
62
|
-
|
63
|
-
## Known Issues
|
64
|
-
|
65
|
-
* DOES NOT work with capistrano ext multi-stage
|
66
|
-
* It will accept invalid tag names (if you specify a tag name with a space, it will blow up when you try to create the tag)
|
67
|
-
|
68
|
-
## Things that might be useful
|
69
|
-
|
70
|
-
* Make it possible to define a different remote other than "origin"
|
71
|
-
* Make it possible to define a different default branch other than "master"
|
72
|
-
* Make it work with either cap-ext multistage or single-file deploy.rb files
|
73
|
-
* Make it possible to provide your own tag naming convention (like the PaperClip string interpolation), instead of relying on <prefix>/<timestamp>
|
82
|
+
cap staging deploy -Shead=true # => sets branch to master
|
83
|
+
cap staging deploy -Stag=staging/01 # => sets branch to staging/01
|
74
84
|
|
75
85
|
## Links
|
76
86
|
|
@@ -11,9 +11,9 @@ class AutoTagger
|
|
11
11
|
@stage = stage
|
12
12
|
end
|
13
13
|
|
14
|
-
def create_tag
|
14
|
+
def create_tag(commit = nil)
|
15
15
|
repository.tags.fetch
|
16
|
-
new_tag = repository.tags.create(stage)
|
16
|
+
new_tag = repository.tags.create(stage, commit)
|
17
17
|
repository.tags.push
|
18
18
|
new_tag
|
19
19
|
end
|
@@ -1,26 +1,16 @@
|
|
1
1
|
class CapistranoHelper
|
2
2
|
|
3
|
-
|
4
|
-
def message
|
5
|
-
"You must set the :stages variable to an array, like set :stages, [:ci, :demo]"
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
attr_reader :variables, :stages, :current_stage, :working_directory
|
3
|
+
attr_reader :variables, :current_stage, :working_directory
|
10
4
|
|
11
5
|
def initialize(variables)
|
12
|
-
|
6
|
+
@stage_manager = StageManager.new(variables[:stages])
|
13
7
|
@variables = variables
|
14
|
-
@stages = variables[:stages]
|
15
8
|
@current_stage = variables[:current_stage]
|
16
9
|
@working_directory = variables[:working_directory] || Dir.pwd
|
17
10
|
end
|
18
11
|
|
19
12
|
def previous_stage
|
20
|
-
|
21
|
-
index = stages.index(current_stage) - 1
|
22
|
-
stages[index] if index > -1
|
23
|
-
end
|
13
|
+
@stage_manager.previous_stage(current_stage)
|
24
14
|
end
|
25
15
|
|
26
16
|
def branch
|
@@ -35,4 +25,15 @@ class CapistranoHelper
|
|
35
25
|
end
|
36
26
|
end
|
37
27
|
|
28
|
+
def release_tag_entries
|
29
|
+
entries = []
|
30
|
+
@stage_manager.stages.each do |stage|
|
31
|
+
tagger = AutoTagger.new(stage, working_directory)
|
32
|
+
tag = tagger.latest_tag
|
33
|
+
commit = tagger.repository.commit_for(tag)
|
34
|
+
entries << "#{stage.to_s.ljust(10, " ")} #{tag.to_s.ljust(30, " ")} #{commit.to_s}"
|
35
|
+
end
|
36
|
+
entries
|
37
|
+
end
|
38
|
+
|
38
39
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class StageManager
|
2
|
+
|
3
|
+
class NoStagesSpecifiedError < StandardError
|
4
|
+
def message
|
5
|
+
"You must set the :stages variable to an array, like set :stages, [:ci, :demo]"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :stages
|
10
|
+
|
11
|
+
def initialize(stages)
|
12
|
+
raise NoStagesSpecifiedError unless stages && stages.is_a?(Array)
|
13
|
+
@stages = stages
|
14
|
+
end
|
15
|
+
|
16
|
+
def previous_stage(current_stage)
|
17
|
+
if current_stage
|
18
|
+
index = stages.index(current_stage) - 1
|
19
|
+
stages[index] if index > -1
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
data/lib/auto_tagger/tag.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# git --no-pager log --pretty=oneline -1
|
2
|
+
# git tag -a -m 'Successful continuous integration build on #{timestamp}' #{tag_name}"
|
1
3
|
class Tag
|
2
4
|
|
3
5
|
attr_reader :repository
|
@@ -22,10 +24,11 @@ class Tag
|
|
22
24
|
repository.run! "git push origin --tags"
|
23
25
|
end
|
24
26
|
|
25
|
-
def create(stage)
|
26
|
-
# git tag -a -m 'Successful continuous integration build on #{timestamp}' #{tag_name}"
|
27
|
+
def create(stage, commit = nil)
|
27
28
|
tag_name = name_for(stage)
|
28
|
-
|
29
|
+
cmd = "git tag #{tag_name}"
|
30
|
+
cmd += " #{commit}" if commit
|
31
|
+
repository.run! cmd
|
29
32
|
tag_name
|
30
33
|
end
|
31
34
|
|
data/lib/auto_tagger.rb
CHANGED
data/recipes/release_tagger.rb
CHANGED
@@ -11,15 +11,41 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
11
11
|
set :branch, branch_name
|
12
12
|
logger.info "setting branch to #{branch_name}"
|
13
13
|
else
|
14
|
-
logger.info "AUTO TAGGER
|
14
|
+
logger.info "AUTO TAGGER: skipping auto-assignment of branch. Branch will remain the default.}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc %Q{Prints the most current tags from all stages}
|
19
|
+
task :print_latest_tags, :roles => :app do
|
20
|
+
logger.info "AUTO TAGGER: release tag history is:"
|
21
|
+
entries = CapistranoHelper.new(variables).release_tag_entries
|
22
|
+
entries.each do |entry|
|
23
|
+
logger.info entry
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
desc %Q{Reads the text file with the latest tag from the shared directory}
|
28
|
+
task :read_tag_from_shared, :roles => :app do
|
29
|
+
logger.info "AUTO TAGGER: latest tag deployed to this environment was:"
|
30
|
+
run "cat #{shared_path}/released_git_tag.txt"
|
31
|
+
end
|
32
|
+
|
33
|
+
desc %Q{Writes the tag name to a file in the shared directory}
|
34
|
+
task :write_tag_to_shared, :roles => :app do
|
35
|
+
if exists?(:branch)
|
36
|
+
logger.info "AUTO TAGGER: writing tag to shared text file on remote server"
|
37
|
+
run "echo '#{branch}' > #{shared_path}/released_git_tag.txt"
|
38
|
+
else
|
39
|
+
logger.info "AUTO TAGGER: no branch available. Text file was not written to server"
|
15
40
|
end
|
16
41
|
end
|
17
42
|
|
18
43
|
desc %Q{Creates a tag using the current_stage variable}
|
19
|
-
task :create_tag do
|
44
|
+
task :create_tag, :roles => :app do
|
20
45
|
if variables[:current_stage]
|
21
|
-
|
22
|
-
|
46
|
+
previous_tag = AutoTagger.new(StageManager.new(stages).previous_stage(current_stage), Dir.pwd).latest_tag
|
47
|
+
tag_name = AutoTagger.new(variables[:current_stage], variables[:working_directory]).create_tag(previous_tag)
|
48
|
+
logger.info "AUTO TAGGER created tag #{tag_name} from #{previous_tag.inspect}"
|
23
49
|
else
|
24
50
|
logger.info "AUTO TAGGER WARNING: skipping auto-creation of tag. Please specify :current_stage to enable auto-creation of tags (like set :current_stage, :ci)."
|
25
51
|
end
|
@@ -43,6 +43,19 @@ describe AutoTagger do
|
|
43
43
|
|
44
44
|
AutoTagger.new("ci", "/foo").create_tag
|
45
45
|
end
|
46
|
+
|
47
|
+
it "allows you to base it off an existing tag or commit" do
|
48
|
+
time = Time.local(2001,1,1)
|
49
|
+
mock(Time).now.once {time}
|
50
|
+
timestamp = time.utc.strftime('%Y%m%d%H%M%S')
|
51
|
+
mock(File).exists?(anything).twice { true }
|
52
|
+
|
53
|
+
mock(Commander).execute!("/foo", "git fetch origin --tags") {true}
|
54
|
+
mock(Commander).execute!("/foo", "git tag ci/#{timestamp} guid") {true}
|
55
|
+
mock(Commander).execute!("/foo", "git push origin --tags") {true}
|
56
|
+
|
57
|
+
AutoTagger.new("ci", "/foo").create_tag("guid")
|
58
|
+
end
|
46
59
|
|
47
60
|
it "returns the tag that was created" do
|
48
61
|
time = Time.local(2001,1,1)
|
@@ -6,7 +6,7 @@ describe CapistranoHelper do
|
|
6
6
|
it "blows up if there are no stages" do
|
7
7
|
proc do
|
8
8
|
CapistranoHelper.new({})
|
9
|
-
end.should raise_error(
|
9
|
+
end.should raise_error(StageManager::NoStagesSpecifiedError)
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
@@ -16,12 +16,6 @@ describe CapistranoHelper do
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
describe "#stages" do
|
20
|
-
it "returns the hashes' stages value" do
|
21
|
-
CapistranoHelper.new({:stages => [:bar]}).stages.should == [:bar]
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
19
|
describe "#working_directory" do
|
26
20
|
it "returns the hashes' working directory value" do
|
27
21
|
CapistranoHelper.new({:stages => [:bar], :working_directory => "/foo"}).working_directory.should == "/foo"
|
@@ -40,17 +34,24 @@ describe CapistranoHelper do
|
|
40
34
|
end
|
41
35
|
end
|
42
36
|
|
43
|
-
describe "#
|
44
|
-
it "returns
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
37
|
+
describe "#release_tag_entries" do
|
38
|
+
it "returns a column-justifed version of all the commits" do
|
39
|
+
mock(Commander).execute("/foo", "git tag").times(any_times) { "ci_01\nstaging_01\nproduction_01" }
|
40
|
+
mock(Commander).execute("/foo", "git --no-pager log ci_01 --pretty=oneline -1") { "guid1" }
|
41
|
+
mock(Commander).execute("/foo", "git --no-pager log staging_01 --pretty=oneline -1") { "guid2" }
|
42
|
+
mock(Commander).execute("/foo", "git --no-pager log production_01 --pretty=oneline -1") { "guid3" }
|
43
|
+
mock(Commander).execute!("/foo", "git fetch origin --tags").times(any_times) { true }
|
44
|
+
mock(File).exists?(anything).times(any_times) {true}
|
45
|
+
|
46
|
+
variables = {
|
47
|
+
:working_directory => "/foo",
|
48
|
+
:stages => [:ci, :staging, :production]
|
49
|
+
}
|
50
|
+
histories = CapistranoHelper.new(variables).release_tag_entries
|
51
|
+
histories.length.should == 3
|
52
|
+
histories[0].should include("ci_01", "guid1")
|
53
|
+
histories[1].should include("staging_01", "guid2")
|
54
|
+
histories[2].should include("production_01", "guid3")
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe StageManager do
|
4
|
+
|
5
|
+
describe ".new" do
|
6
|
+
[nil, ""].each do |value|
|
7
|
+
it "blows up if there are stages == #{value.inspect}" do
|
8
|
+
proc do
|
9
|
+
StageManager.new(value)
|
10
|
+
end.should raise_error(StageManager::NoStagesSpecifiedError)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#previous_stage" do
|
16
|
+
it "returns the previous stage if there is more than one stage, and there is a current stage" do
|
17
|
+
StageManager.new([:foo, :bar]).previous_stage(:bar).should == :foo
|
18
|
+
end
|
19
|
+
|
20
|
+
it "returns nil if there is no previous stage" do
|
21
|
+
StageManager.new([:bar]).previous_stage(:bar).should be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns nil if there is no current stage" do
|
25
|
+
StageManager.new([:bar]).previous_stage(nil).should be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zilkey-auto_tagger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeff Dean
|
@@ -36,6 +36,7 @@ files:
|
|
36
36
|
- lib/auto_tagger/capistrano_helper.rb
|
37
37
|
- lib/auto_tagger/commander.rb
|
38
38
|
- lib/auto_tagger/repository.rb
|
39
|
+
- lib/auto_tagger/stage_manager.rb
|
39
40
|
- lib/auto_tagger/tag.rb
|
40
41
|
- lib/auto_tagger.rb
|
41
42
|
- recipes/release_tagger.rb
|
@@ -45,6 +46,7 @@ files:
|
|
45
46
|
- spec/auto_tagger/capistrano_helper_spec.rb
|
46
47
|
- spec/auto_tagger/commander_spec.rb
|
47
48
|
- spec/auto_tagger/repository_spec.rb
|
49
|
+
- spec/auto_tagger/stage_manager_spec.rb
|
48
50
|
- spec/auto_tagger/tag_spec.rb
|
49
51
|
- spec/spec_helper.rb
|
50
52
|
- MIT-LICENSE
|