zilkey-auto_tagger 0.0.6 → 0.0.7
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 +52 -12
- data/lib/auto_tagger/capistrano_helper.rb +5 -5
- data/lib/auto_tagger/commander.rb +1 -2
- data/lib/auto_tagger/repository.rb +5 -5
- data/lib/auto_tagger/stage_manager.rb +9 -9
- data/lib/auto_tagger/tag.rb +10 -10
- data/recipes/release_tagger.rb +6 -7
- data/spec/auto_tagger/auto_tagger_spec.rb +10 -7
- data/spec/auto_tagger/capistrano_helper_spec.rb +17 -17
- data/spec/auto_tagger/commander_spec.rb +1 -1
- data/spec/auto_tagger/stage_manager_spec.rb +2 -2
- data/spec/auto_tagger/tag_spec.rb +5 -5
- metadata +1 -1
data/README.md
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
# IMPORTANT NOTE
|
2
|
+
|
3
|
+
This gem is still in it's infancy, and lots of things might change. Since this creates and pushes tags to your git repository, please use with caution.
|
4
|
+
|
1
5
|
# AutoTagger
|
2
6
|
|
3
7
|
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.
|
@@ -45,21 +49,21 @@ Example `config/deploy.rb` file:
|
|
45
49
|
|
46
50
|
require 'release_tagger'
|
47
51
|
|
48
|
-
# The :
|
49
|
-
set :
|
52
|
+
# The :autotagger_stages variable is required
|
53
|
+
set :autotagger_stages, [:ci, :staging, :production]
|
50
54
|
|
51
55
|
# The :working_directory variable is optional, and defaults to Dir.pwd
|
52
56
|
# :working_directory can be an absolute or relative path
|
53
57
|
set :working_directory, "../../"
|
54
58
|
|
55
59
|
task :production do
|
56
|
-
# In each of your environments that need auto-branch setting, you need to set :
|
57
|
-
set :
|
60
|
+
# In each of your environments that need auto-branch setting, you need to set :stage
|
61
|
+
set :stage, :production
|
58
62
|
end
|
59
63
|
|
60
64
|
task :staging do
|
61
|
-
# If you do not set
|
62
|
-
# set :
|
65
|
+
# If you do not set stage, it will not auto-set your branch
|
66
|
+
# set :stage, :staging
|
63
67
|
end
|
64
68
|
|
65
69
|
# You need to add the before/ater callbacks yourself
|
@@ -68,7 +72,18 @@ Example `config/deploy.rb` file:
|
|
68
72
|
after "deploy", "release_tagger:write_tag_to_shared"
|
69
73
|
after "deploy", "release_tagger:print_latest_tags"
|
70
74
|
|
71
|
-
###
|
75
|
+
### Cpistano-ext multistage support
|
76
|
+
|
77
|
+
If you use capistano-ext multistage, you can use auto_tagger.
|
78
|
+
|
79
|
+
set :autotagger_stages, [:ci, :staging, :production]
|
80
|
+
set :stages, [:staging, :production]
|
81
|
+
set :default_stage, :staging
|
82
|
+
require 'capistrano/ext/multistage'
|
83
|
+
|
84
|
+
When you deploy, autotagger will auto-detect your current stage.
|
85
|
+
|
86
|
+
### release_tagger:set_branch
|
72
87
|
|
73
88
|
This task sets the git branch to the latest tag from the previous stage. Assume you have the following tags in your git repository:
|
74
89
|
|
@@ -78,7 +93,7 @@ This task sets the git branch to the latest tag from the previous stage. Assume
|
|
78
93
|
|
79
94
|
And the following stages in your capistrano file:
|
80
95
|
|
81
|
-
set :
|
96
|
+
set :autotagger_stages, [:ci, :staging, :production]
|
82
97
|
|
83
98
|
The deployments would look like this:
|
84
99
|
|
@@ -96,19 +111,19 @@ If you add `before "deploy:update_code", "release_tagger:set_branch"`, you can j
|
|
96
111
|
|
97
112
|
and the branch will be set for you automatically.
|
98
113
|
|
99
|
-
###
|
114
|
+
### release_tagger:create_tag
|
100
115
|
|
101
116
|
This cap task creates a new tag, based on the latest tag from the previous environment.
|
102
117
|
|
103
118
|
If there is no tag from the previous stage, it creates a new tag from the latest commit in your _working directory_.
|
104
119
|
|
105
|
-
###
|
120
|
+
### release_tagger:print_latest_tags
|
106
121
|
|
107
122
|
This task reads the git version from the text file in shared:
|
108
123
|
|
109
124
|
cap staging release_tagger:read_tag_from_shared
|
110
125
|
|
111
|
-
###
|
126
|
+
### release_tagger:print_latest_tags
|
112
127
|
|
113
128
|
This task takes the latest tag from each environment and prints it to the screen. You can add it to your deploy.rb like so:
|
114
129
|
|
@@ -125,9 +140,34 @@ This will produce output like:
|
|
125
140
|
** staging staging/20090331050908 8031807feb5f4f99dd83257cdc07081fa6080cba some commit message
|
126
141
|
** production production/20090331050917 8031807feb5f4f99dd83257cdc07081fa6080cba some commit message
|
127
142
|
|
143
|
+
## Running tests:
|
144
|
+
|
145
|
+
You must be able to ssh into your box via localhost (remote login). To make this easier, add your own key to your own account:
|
146
|
+
|
147
|
+
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
|
148
|
+
|
149
|
+
To ensure that this has worked, try this:
|
150
|
+
|
151
|
+
ssh localhost
|
152
|
+
|
153
|
+
If it asks you for a password, you've done something wrong.
|
154
|
+
|
155
|
+
To run the specs, execute:
|
156
|
+
|
157
|
+
spec spec/
|
158
|
+
|
159
|
+
To run the cucumber features, execute:
|
160
|
+
|
161
|
+
cucumber features/
|
162
|
+
|
128
163
|
## Acknowledgments
|
129
164
|
|
130
|
-
Special thanks to
|
165
|
+
Special thanks to
|
166
|
+
|
167
|
+
* Brian Takita for the original recipes
|
168
|
+
* Mike Dalessio for his git fu
|
169
|
+
* Chad Wooley for his feature ideas
|
170
|
+
* Tim Holahan for his QA
|
131
171
|
|
132
172
|
## Links
|
133
173
|
|
@@ -1,16 +1,16 @@
|
|
1
1
|
class CapistranoHelper
|
2
2
|
|
3
|
-
attr_reader :variables, :
|
3
|
+
attr_reader :variables, :stage, :working_directory
|
4
4
|
|
5
5
|
def initialize(variables)
|
6
|
-
@stage_manager = StageManager.new(variables[:
|
6
|
+
@stage_manager = StageManager.new(variables[:autotagger_stages])
|
7
7
|
@variables = variables
|
8
|
-
@
|
8
|
+
@stage = variables[:stage]
|
9
9
|
@working_directory = variables[:working_directory] || Dir.pwd
|
10
10
|
end
|
11
11
|
|
12
12
|
def previous_stage
|
13
|
-
@stage_manager.previous_stage(
|
13
|
+
@stage_manager.previous_stage(stage)
|
14
14
|
end
|
15
15
|
|
16
16
|
def branch
|
@@ -36,4 +36,4 @@ class CapistranoHelper
|
|
36
36
|
entries
|
37
37
|
end
|
38
38
|
|
39
|
-
end
|
39
|
+
end
|
@@ -13,7 +13,7 @@ class Repository
|
|
13
13
|
elsif ! File.exists?(path)
|
14
14
|
raise NoSuchPathError
|
15
15
|
elsif ! File.exists?(File.join(path, ".git"))
|
16
|
-
raise InvalidGitRepositoryError
|
16
|
+
raise InvalidGitRepositoryError
|
17
17
|
else
|
18
18
|
@path = path
|
19
19
|
end
|
@@ -22,15 +22,15 @@ class Repository
|
|
22
22
|
def ==(other)
|
23
23
|
other.is_a?(Repository) && other.path == path
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def tags
|
27
27
|
@tags ||= Tag.new(self)
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
def commit_for(tag)
|
31
31
|
Commander.execute(path, "git --no-pager log #{tag} --pretty=oneline -1")
|
32
32
|
end
|
33
|
-
|
33
|
+
|
34
34
|
def run(cmd)
|
35
35
|
Commander.execute(path, cmd)
|
36
36
|
end
|
@@ -38,5 +38,5 @@ class Repository
|
|
38
38
|
def run!(cmd)
|
39
39
|
Commander.execute!(path, cmd) || raise(GitCommandFailedError)
|
40
40
|
end
|
41
|
-
|
41
|
+
|
42
42
|
end
|
@@ -5,19 +5,19 @@ class StageManager
|
|
5
5
|
"You must set the :stages variable to an array, like set :stages, [:ci, :demo]"
|
6
6
|
end
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
attr_reader :stages
|
10
|
-
|
10
|
+
|
11
11
|
def initialize(stages)
|
12
12
|
raise NoStagesSpecifiedError unless stages && stages.is_a?(Array)
|
13
13
|
@stages = stages
|
14
14
|
end
|
15
|
-
|
16
|
-
def previous_stage(
|
17
|
-
if
|
18
|
-
index = stages.index(
|
19
|
-
stages[index] if index > -1
|
15
|
+
|
16
|
+
def previous_stage(stage)
|
17
|
+
if stage
|
18
|
+
index = stages.index(stage) - 1
|
19
|
+
stages[index] if index > -1
|
20
20
|
end
|
21
21
|
end
|
22
|
-
|
23
|
-
end
|
22
|
+
|
23
|
+
end
|
data/lib/auto_tagger/tag.rb
CHANGED
@@ -1,21 +1,21 @@
|
|
1
1
|
# git --no-pager log --pretty=oneline -1
|
2
2
|
# git tag -a -m 'Successful continuous integration build on #{timestamp}' #{tag_name}"
|
3
3
|
class Tag
|
4
|
-
|
4
|
+
|
5
5
|
attr_reader :repository
|
6
|
-
|
6
|
+
|
7
7
|
def initialize(repository)
|
8
8
|
@repository = repository
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
def find_all
|
12
12
|
repository.run("git tag").split("\n")
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
def fetch
|
16
16
|
repository.run! "git fetch origin --tags"
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def latest_from(stage)
|
20
20
|
find_all.select{|tag| tag =~ /^#{stage}/}.sort.last
|
21
21
|
end
|
@@ -23,7 +23,7 @@ class Tag
|
|
23
23
|
def push
|
24
24
|
repository.run! "git push origin --tags"
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
def create(stage, commit = nil)
|
28
28
|
tag_name = name_for(stage)
|
29
29
|
cmd = "git tag #{tag_name}"
|
@@ -31,11 +31,11 @@ class Tag
|
|
31
31
|
repository.run! cmd
|
32
32
|
tag_name
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
private
|
36
|
-
|
36
|
+
|
37
37
|
def name_for(stage)
|
38
38
|
"%s/%s" % [stage, Time.now.utc.strftime('%Y%m%d%H%M%S')]
|
39
39
|
end
|
40
|
-
|
41
|
-
end
|
40
|
+
|
41
|
+
end
|
data/recipes/release_tagger.rb
CHANGED
@@ -3,7 +3,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "..", "lib", "auto_ta
|
|
3
3
|
Capistrano::Configuration.instance(:must_exist).load do
|
4
4
|
namespace :release_tagger do
|
5
5
|
desc %Q{
|
6
|
-
Sets the branch to the latest tag from the previous stage.
|
6
|
+
Sets the branch to the latest tag from the previous stage.
|
7
7
|
Use -Shead=true to set the branch to master, -Stag=<tag> to specify the tag explicitly.
|
8
8
|
}
|
9
9
|
task :set_branch do
|
@@ -40,16 +40,15 @@ Capistrano::Configuration.instance(:must_exist).load do
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
desc %Q{Creates a tag using the
|
43
|
+
desc %Q{Creates a tag using the stage variable}
|
44
44
|
task :create_tag, :roles => :app do
|
45
|
-
if variables[:
|
46
|
-
previous_tag = AutoTagger.new(StageManager.new(
|
47
|
-
tag_name = AutoTagger.new(variables[:
|
45
|
+
if variables[:stage]
|
46
|
+
previous_tag = AutoTagger.new(StageManager.new(autotagger_stages).previous_stage(stage), Dir.pwd).latest_tag
|
47
|
+
tag_name = AutoTagger.new(variables[:stage], variables[:working_directory]).create_tag(previous_tag)
|
48
48
|
logger.info "AUTO TAGGER created tag #{tag_name} from #{previous_tag.inspect}"
|
49
49
|
else
|
50
|
-
logger.info "AUTO TAGGER WARNING: skipping auto-creation of tag. Please specify :
|
50
|
+
logger.info "AUTO TAGGER WARNING: skipping auto-creation of tag. Please specify :stage to enable auto-creation of tags (like set :stage, :ci)."
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
54
54
|
end
|
55
|
-
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
2
|
|
3
3
|
describe AutoTagger do
|
4
|
+
before(:each) do
|
5
|
+
stub(Dir).pwd { File.join(File.dirname(__FILE__), '..', '..') }
|
6
|
+
end
|
4
7
|
|
5
8
|
describe ".new" do
|
6
9
|
it "blows up if you don't pass an stage" do
|
@@ -8,7 +11,7 @@ describe AutoTagger do
|
|
8
11
|
AutoTagger.new(nil)
|
9
12
|
end.should raise_error(AutoTagger::EnvironmentCannotBeBlankError)
|
10
13
|
end
|
11
|
-
|
14
|
+
|
12
15
|
it "sets the stage when it's passed" do
|
13
16
|
AutoTagger.new("ci").stage.should == "ci"
|
14
17
|
end
|
@@ -40,7 +43,7 @@ describe AutoTagger do
|
|
40
43
|
mock(Commander).execute!("/foo", "git fetch origin --tags") {true}
|
41
44
|
mock(Commander).execute!("/foo", "git tag ci/#{timestamp}") {true}
|
42
45
|
mock(Commander).execute!("/foo", "git push origin --tags") {true}
|
43
|
-
|
46
|
+
|
44
47
|
AutoTagger.new("ci", "/foo").create_tag
|
45
48
|
end
|
46
49
|
|
@@ -53,17 +56,17 @@ describe AutoTagger do
|
|
53
56
|
mock(Commander).execute!("/foo", "git fetch origin --tags") {true}
|
54
57
|
mock(Commander).execute!("/foo", "git tag ci/#{timestamp} guid") {true}
|
55
58
|
mock(Commander).execute!("/foo", "git push origin --tags") {true}
|
56
|
-
|
59
|
+
|
57
60
|
AutoTagger.new("ci", "/foo").create_tag("guid")
|
58
61
|
end
|
59
|
-
|
62
|
+
|
60
63
|
it "returns the tag that was created" do
|
61
64
|
time = Time.local(2001,1,1)
|
62
65
|
mock(Time).now.once {time}
|
63
66
|
timestamp = time.utc.strftime('%Y%m%d%H%M%S')
|
64
67
|
mock(File).exists?(anything).twice { true }
|
65
68
|
mock(Commander).execute!(anything, anything).times(any_times) {true}
|
66
|
-
|
69
|
+
|
67
70
|
AutoTagger.new("ci", "/foo").create_tag.should == "ci/#{timestamp}"
|
68
71
|
end
|
69
72
|
end
|
@@ -74,9 +77,9 @@ describe AutoTagger do
|
|
74
77
|
|
75
78
|
mock(Commander).execute!("/foo", "git fetch origin --tags") {true}
|
76
79
|
mock(Commander).execute("/foo", "git tag") { "ci_01" }
|
77
|
-
|
80
|
+
|
78
81
|
AutoTagger.new("ci", "/foo").latest_tag
|
79
82
|
end
|
80
83
|
end
|
81
|
-
|
84
|
+
|
82
85
|
end
|
@@ -12,25 +12,25 @@ describe CapistranoHelper do
|
|
12
12
|
|
13
13
|
describe "#variables" do
|
14
14
|
it "returns all variables" do
|
15
|
-
CapistranoHelper.new({:
|
15
|
+
CapistranoHelper.new({:autotagger_stages => [:bar]}).variables.should == {:autotagger_stages => [:bar]}
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
19
|
describe "#working_directory" do
|
20
20
|
it "returns the hashes' working directory value" do
|
21
|
-
CapistranoHelper.new({:
|
21
|
+
CapistranoHelper.new({:autotagger_stages => [:bar], :working_directory => "/foo"}).working_directory.should == "/foo"
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
it "defaults to Dir.pwd if it's not set, or it's nil" do
|
25
25
|
mock(Dir).pwd { "/bar" }
|
26
|
-
CapistranoHelper.new({:
|
26
|
+
CapistranoHelper.new({:autotagger_stages => [:bar]}).working_directory.should == "/bar"
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
-
describe "#
|
30
|
+
describe "#stage" do
|
31
31
|
it "returns the hashes' current stage value" do
|
32
|
-
CapistranoHelper.new({:
|
33
|
-
CapistranoHelper.new({:
|
32
|
+
CapistranoHelper.new({:autotagger_stages => [:bar], :stage => :bar}).stage.should == :bar
|
33
|
+
CapistranoHelper.new({:autotagger_stages => [:bar]}).stage.should be_nil
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
@@ -45,7 +45,7 @@ describe CapistranoHelper do
|
|
45
45
|
|
46
46
|
variables = {
|
47
47
|
:working_directory => "/foo",
|
48
|
-
:
|
48
|
+
:autotagger_stages => [:ci, :staging, :production]
|
49
49
|
}
|
50
50
|
histories = CapistranoHelper.new(variables).release_tag_entries
|
51
51
|
histories.length.should == 3
|
@@ -59,7 +59,7 @@ describe CapistranoHelper do
|
|
59
59
|
describe "with :head and :branch specified" do
|
60
60
|
it "returns master" do
|
61
61
|
variables = {
|
62
|
-
:
|
62
|
+
:autotagger_stages => [:bar],
|
63
63
|
:head => nil,
|
64
64
|
:branch => "foo"
|
65
65
|
}
|
@@ -70,7 +70,7 @@ describe CapistranoHelper do
|
|
70
70
|
describe "with :head specified, but no branch specified" do
|
71
71
|
it "returns master" do
|
72
72
|
variables = {
|
73
|
-
:
|
73
|
+
:autotagger_stages => [:bar],
|
74
74
|
:head => nil
|
75
75
|
}
|
76
76
|
CapistranoHelper.new(variables).branch.should == nil
|
@@ -80,7 +80,7 @@ describe CapistranoHelper do
|
|
80
80
|
describe "with :branch specified" do
|
81
81
|
it "returns the value of branch" do
|
82
82
|
variables = {
|
83
|
-
:
|
83
|
+
:autotagger_stages => [:bar],
|
84
84
|
:branch => "foo"
|
85
85
|
}
|
86
86
|
CapistranoHelper.new(variables).branch.should == "foo"
|
@@ -90,8 +90,8 @@ describe CapistranoHelper do
|
|
90
90
|
describe "with a previous stage with a tag" do
|
91
91
|
it "returns the latest tag for the previous stage" do
|
92
92
|
variables = {
|
93
|
-
:
|
94
|
-
:
|
93
|
+
:autotagger_stages => [:foo, :bar],
|
94
|
+
:stage => :bar,
|
95
95
|
:branch => "master",
|
96
96
|
:working_directory => "/foo"
|
97
97
|
}
|
@@ -105,8 +105,8 @@ describe CapistranoHelper do
|
|
105
105
|
describe "with no branch and a previous stage with no tag" do
|
106
106
|
it "returns nil" do
|
107
107
|
variables = {
|
108
|
-
:
|
109
|
-
:
|
108
|
+
:autotagger_stages => [:foo, :bar],
|
109
|
+
:stage => :bar,
|
110
110
|
:working_directory => "/foo"
|
111
111
|
}
|
112
112
|
tagger = Object.new
|
@@ -119,8 +119,8 @@ describe CapistranoHelper do
|
|
119
119
|
describe "with no branch and previous stage" do
|
120
120
|
it "returns nil" do
|
121
121
|
variables = {
|
122
|
-
:
|
123
|
-
:
|
122
|
+
:autotagger_stages => [:bar],
|
123
|
+
:stage => :bar
|
124
124
|
}
|
125
125
|
CapistranoHelper.new(variables).previous_stage.should be_nil
|
126
126
|
CapistranoHelper.new(variables).branch.should == nil
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
2
|
|
3
3
|
describe StageManager do
|
4
|
-
|
4
|
+
|
5
5
|
describe ".new" do
|
6
6
|
[nil, ""].each do |value|
|
7
7
|
it "blows up if there are stages == #{value.inspect}" do
|
@@ -26,5 +26,5 @@ describe StageManager do
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
|
29
|
+
|
30
30
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
2
|
|
3
3
|
describe Tag do
|
4
|
-
|
4
|
+
|
5
5
|
before(:each) do
|
6
6
|
@repository = Object.new
|
7
7
|
end
|
@@ -11,7 +11,7 @@ describe Tag do
|
|
11
11
|
Tag.new(@repository).repository.should == @repository
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
describe "#find_all" do
|
16
16
|
it "returns an array of tags" do
|
17
17
|
mock(@repository).run("git tag") { "ci_01\nci_02" }
|
@@ -23,7 +23,7 @@ describe Tag do
|
|
23
23
|
Tag.new(@repository).find_all.should be_empty
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
describe "#latest_from" do
|
28
28
|
before do
|
29
29
|
@tag = Tag.new(@repository)
|
@@ -61,6 +61,6 @@ describe Tag do
|
|
61
61
|
mock(@repository).run!("git tag #{tag_name}")
|
62
62
|
Tag.new(@repository).create("ci").should == tag_name
|
63
63
|
end
|
64
|
-
end
|
65
|
-
|
64
|
+
end
|
65
|
+
|
66
66
|
end
|