zilkey-auto_tagger 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AutoTagger
2
2
 
3
- AutoTagger allows you to create a date-stamped tag for each stage of your deployment, and deploy from the last tag from the previous environment.
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
- Example deploy file:
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:update_code", "release_tagger:create_tag"
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
- 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 :variables, :stages, :current_stage, :working_directory
3
+ attr_reader :variables, :current_stage, :working_directory
10
4
 
11
5
  def initialize(variables)
12
- raise NoStagesSpecifiedError unless variables[:stages]
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
- if current_stage
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
@@ -27,6 +27,10 @@ class Repository
27
27
  @tags ||= Tag.new(self)
28
28
  end
29
29
 
30
+ def commit_for(tag)
31
+ Commander.execute(path, "git --no-pager log #{tag} --pretty=oneline -1")
32
+ end
33
+
30
34
  def run(cmd)
31
35
  Commander.execute(path, cmd)
32
36
  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
@@ -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
- repository.run! "git tag #{tag_name}"
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
@@ -3,6 +3,7 @@
3
3
  'repository',
4
4
  'tag',
5
5
  'auto_tagger',
6
+ 'stage_manager',
6
7
  'capistrano_helper'
7
8
  ].each do |file|
8
9
  require File.expand_path(File.join(File.dirname(__FILE__), "auto_tagger", file))
@@ -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 WARNING: skipping auto-assignment of branch. Branch will remain the default.}"
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
- tag_name = AutoTagger.new(variables[:current_stage], variables[:working_directory]).create_tag
22
- logger.info "created and pushed tag #{tag_name}"
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(CapistranoHelper::NoStagesSpecifiedError)
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 "#previous_stage" do
44
- it "returns the previous stage if there is more than one stage, and there is a current stage" do
45
- CapistranoHelper.new({:stages => [:foo, :bar], :current_stage => :bar}).previous_stage.should == :foo
46
- end
47
-
48
- it "returns nil if there is no previous stage" do
49
- CapistranoHelper.new({:stages => [:bar], :current_stage => :bar}).previous_stage.should be_nil
50
- end
51
-
52
- it "returns nil if there is no current stage" do
53
- CapistranoHelper.new({:stages => [:bar]}).previous_stage.should be_nil
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
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