factorylabs-auto_tagger 0.1.1
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/.document +6 -0
- data/.gitignore +6 -0
- data/CHANGELOG +30 -0
- data/MIT-LICENSE +20 -0
- data/README.md +176 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/auto_tagger.gemspec +83 -0
- data/bin/autotag +38 -0
- data/features/autotag.feature +39 -0
- data/features/deployment.feature +29 -0
- data/features/step_definitions/autotag_steps.rb +41 -0
- data/features/step_definitions/deployment_steps.rb +53 -0
- data/features/support/env.rb +8 -0
- data/features/support/step_helpers.rb +136 -0
- data/features/templates/cap_ext_deploy.erb +32 -0
- data/features/templates/deploy.erb +34 -0
- data/features/templates/stage.erb +1 -0
- data/geminstaller.yml +7 -0
- data/lib/auto_tagger.rb +10 -0
- data/lib/auto_tagger/auto_tagger.rb +26 -0
- data/lib/auto_tagger/capistrano_helper.rb +39 -0
- data/lib/auto_tagger/commander.rb +15 -0
- data/lib/auto_tagger/recipes.rb +54 -0
- data/lib/auto_tagger/repository.rb +42 -0
- data/lib/auto_tagger/stage_manager.rb +23 -0
- data/lib/auto_tagger/tag.rb +41 -0
- data/spec/auto_tagger/auto_tagger_spec.rb +85 -0
- data/spec/auto_tagger/capistrano_helper_spec.rb +146 -0
- data/spec/auto_tagger/commander_spec.rb +17 -0
- data/spec/auto_tagger/repository_spec.rb +72 -0
- data/spec/auto_tagger/stage_manager_spec.rb +34 -0
- data/spec/auto_tagger/tag_spec.rb +66 -0
- data/spec/spec_helper.rb +7 -0
- metadata +116 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
Feature: Deployment
|
2
|
+
In order to get auto-tagging goodness
|
3
|
+
As a ruby and git ninja
|
4
|
+
I want cap to do all the work for me
|
5
|
+
|
6
|
+
Scenario: user deploys with single file deploy.rb with 3 stages
|
7
|
+
Given a three-stage app using single deploy file
|
8
|
+
And a ci tag
|
9
|
+
When I deploy to staging
|
10
|
+
Then a tag should be added to git
|
11
|
+
|
12
|
+
Scenario: user deploys with single file deploy.rb with no stages
|
13
|
+
Given a one-stage app using single deploy file with the following environments:
|
14
|
+
|production |
|
15
|
+
When I deploy
|
16
|
+
Then a "production" tag should be added to git
|
17
|
+
|
18
|
+
Scenario: user deploys with single file deploy.rb stages with dashes in the name
|
19
|
+
Given a one-stage app using single deploy file with the following environments:
|
20
|
+
|staging |
|
21
|
+
|secret-production |
|
22
|
+
When I deploy to secret-production
|
23
|
+
Then a "secret-production" tag should be added to git
|
24
|
+
|
25
|
+
Scenario: user deploys with cap-ext multistage
|
26
|
+
Given a three-stage app using cap-multistage
|
27
|
+
And a ci tag
|
28
|
+
When I deploy to staging
|
29
|
+
Then a tag should be added to git
|
@@ -0,0 +1,41 @@
|
|
1
|
+
Given /^a repo$/ do
|
2
|
+
puts
|
3
|
+
helpers = StepHelpers.new
|
4
|
+
helpers.create_git_repo
|
5
|
+
helpers.create_app
|
6
|
+
@tags = helpers.tags
|
7
|
+
puts
|
8
|
+
end
|
9
|
+
|
10
|
+
When /^I run autotag with no arguments$/ do
|
11
|
+
puts
|
12
|
+
helpers = StepHelpers.new
|
13
|
+
@output = helpers.run_autotag
|
14
|
+
@tags = helpers.tags
|
15
|
+
puts
|
16
|
+
end
|
17
|
+
|
18
|
+
When /^I run autotag with "([^\"]*)"$/ do |args|
|
19
|
+
puts
|
20
|
+
helpers = StepHelpers.new
|
21
|
+
@output = helpers.run_autotag(args)
|
22
|
+
@tags = helpers.tags
|
23
|
+
puts
|
24
|
+
end
|
25
|
+
|
26
|
+
Then /^I should see "([^\"]*)"$/ do |text|
|
27
|
+
@output.should =~ /#{Regexp.escape(text)}/
|
28
|
+
end
|
29
|
+
|
30
|
+
Then /^no tags should be created$/ do
|
31
|
+
@tags.strip.should == ""
|
32
|
+
end
|
33
|
+
|
34
|
+
Then /^a "([^\"]*)" tag should be added to git$/ do |stage|
|
35
|
+
helpers = StepHelpers.new
|
36
|
+
helpers.tags.starts_with?(stage).should be_true
|
37
|
+
end
|
38
|
+
|
39
|
+
Then /^a "([^\"]*)" tag should be created$/ do |prefix|
|
40
|
+
@tags.strip.split("\n").any?{|tag| tag.starts_with?("demo")}.should be_true
|
41
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
Given /^a three-stage app using single deploy file$/ do
|
2
|
+
puts
|
3
|
+
helpers = StepHelpers.new
|
4
|
+
helpers.create_app_with_single_deploy_file([:ci, :staging, :production])
|
5
|
+
puts
|
6
|
+
@tags = helpers.tags
|
7
|
+
end
|
8
|
+
|
9
|
+
Given /^a one\-stage app using single deploy file with the following environments:$/ do |table|
|
10
|
+
puts
|
11
|
+
helpers = StepHelpers.new
|
12
|
+
helpers.create_app_with_single_deploy_file table.raw.map(&:first)
|
13
|
+
puts
|
14
|
+
@tags = helpers.tags
|
15
|
+
end
|
16
|
+
|
17
|
+
Given /^a three-stage app using cap-multistage$/ do
|
18
|
+
puts
|
19
|
+
helpers = StepHelpers.new
|
20
|
+
helpers.create_app_with_cap_ext_multistage
|
21
|
+
puts
|
22
|
+
@tags = helpers.tags
|
23
|
+
end
|
24
|
+
|
25
|
+
Given /^a ci tag$/ do
|
26
|
+
puts
|
27
|
+
helpers = StepHelpers.new
|
28
|
+
helpers.autotag("ci")
|
29
|
+
@tags = helpers.tags
|
30
|
+
puts
|
31
|
+
end
|
32
|
+
|
33
|
+
When /^I deploy to (.*)$/ do |environment|
|
34
|
+
puts
|
35
|
+
helpers = StepHelpers.new
|
36
|
+
helpers.deploy(environment)
|
37
|
+
puts
|
38
|
+
end
|
39
|
+
|
40
|
+
When /^I deploy$/ do
|
41
|
+
puts
|
42
|
+
helpers = StepHelpers.new
|
43
|
+
helpers.deploy
|
44
|
+
puts
|
45
|
+
end
|
46
|
+
|
47
|
+
Then /^a tag should be added to git$/ do
|
48
|
+
puts
|
49
|
+
helpers = StepHelpers.new
|
50
|
+
new_tags = helpers.tags
|
51
|
+
@tags.length.should < new_tags.length
|
52
|
+
puts
|
53
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
class StepHelpers
|
2
|
+
|
3
|
+
attr_reader :test_files_dir, :app_dir, :repo_dir
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@test_files_dir = File.join(Dir.pwd, "test_files")
|
7
|
+
@app_dir = File.join(test_files_dir, "app")
|
8
|
+
@repo_dir = File.join(test_files_dir, "repo")
|
9
|
+
end
|
10
|
+
|
11
|
+
def reset
|
12
|
+
FileUtils.rm_r(test_files_dir) if File.exists?(test_files_dir)
|
13
|
+
FileUtils.mkdir_p(test_files_dir)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_git_repo
|
17
|
+
FileUtils.mkdir_p repo_dir
|
18
|
+
`cd #{repo_dir} && git --bare init`
|
19
|
+
end
|
20
|
+
|
21
|
+
def create_app_with_single_deploy_file(environments)
|
22
|
+
create_git_repo
|
23
|
+
create_app
|
24
|
+
capify_app
|
25
|
+
create_single_deploy_file(environments)
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_autotag(args = nil)
|
29
|
+
cmd = "cd #{app_dir} && ../../bin/autotag"
|
30
|
+
cmd += " #{args}" if args
|
31
|
+
`#{cmd}`
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_app_with_cap_ext_multistage
|
35
|
+
create_git_repo
|
36
|
+
create_app
|
37
|
+
capify_app
|
38
|
+
create_cap_ext_multistage_deploy_files
|
39
|
+
end
|
40
|
+
|
41
|
+
def deploy(stage = nil)
|
42
|
+
deploy_command = "cap "
|
43
|
+
deploy_command += "#{stage} " if stage
|
44
|
+
deploy_command += "deploy"
|
45
|
+
|
46
|
+
run_commands [
|
47
|
+
"cd #{app_dir}",
|
48
|
+
"cap deploy:setup",
|
49
|
+
deploy_command
|
50
|
+
]
|
51
|
+
end
|
52
|
+
|
53
|
+
def autotag(stage)
|
54
|
+
system "cd #{app_dir} && git tag #{stage}/#{Time.now.utc.strftime('%Y%m%d%H%M%S')} && git push origin --tags"
|
55
|
+
end
|
56
|
+
|
57
|
+
def tags
|
58
|
+
system "cd #{app_dir} && git fetch origin --tags"
|
59
|
+
tags = `cd #{app_dir} && git tag`
|
60
|
+
puts tags
|
61
|
+
tags
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_app
|
65
|
+
FileUtils.mkdir_p app_dir
|
66
|
+
run_commands [
|
67
|
+
"cd #{app_dir}",
|
68
|
+
"touch README",
|
69
|
+
"git init",
|
70
|
+
"git add .",
|
71
|
+
%Q{git commit -m "first commit"},
|
72
|
+
"git remote add origin file://#{repo_dir}",
|
73
|
+
"git push origin master"
|
74
|
+
]
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def capify_app
|
80
|
+
run_commands [
|
81
|
+
"cd #{app_dir}",
|
82
|
+
"mkdir config",
|
83
|
+
"capify ."
|
84
|
+
]
|
85
|
+
end
|
86
|
+
|
87
|
+
def create_single_deploy_file(environments)
|
88
|
+
repository = repo_dir
|
89
|
+
deploy_to = File.join(test_files_dir, "deployed")
|
90
|
+
git_location = `which git`.strip
|
91
|
+
user = Etc.getlogin
|
92
|
+
|
93
|
+
path = File.expand_path(File.join(__FILE__, "..", "..", "templates", "deploy.erb"))
|
94
|
+
#puts "TRACE %s:%s" % [__FILE__, __LINE__]
|
95
|
+
#puts path
|
96
|
+
#puts File.exists?(path)
|
97
|
+
|
98
|
+
template = ERB.new File.read(path)
|
99
|
+
output = template.result(binding)
|
100
|
+
File.open(File.join(app_dir, "config", "deploy.rb"), 'w') {|f| f.write(output) }
|
101
|
+
end
|
102
|
+
|
103
|
+
def create_cap_ext_multistage_deploy_files
|
104
|
+
repository = repo_dir
|
105
|
+
deploy_to = File.join(test_files_dir, "deployed")
|
106
|
+
git_location = `which git`.strip
|
107
|
+
user = Etc.getlogin
|
108
|
+
environments = [:ci, :staging, :production]
|
109
|
+
|
110
|
+
path = File.expand_path(File.join(__FILE__, "..", "..", "templates", "cap_ext_deploy.erb"))
|
111
|
+
|
112
|
+
template = ERB.new File.read(path)
|
113
|
+
output = template.result(binding)
|
114
|
+
File.open(File.join(app_dir, "config", "deploy.rb"), 'w') {|f| f.write(output) }
|
115
|
+
|
116
|
+
%w(staging production).each do |stage|
|
117
|
+
create_cap_ext_multistage_deploy_stage_file(stage)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def create_cap_ext_multistage_deploy_stage_file(stage)
|
122
|
+
deploy_subdir = File.join(app_dir, "config", "deploy")
|
123
|
+
FileUtils.mkdir_p deploy_subdir
|
124
|
+
|
125
|
+
template_path = File.expand_path(File.join(__FILE__, "..", "..", "templates", "stage.erb"))
|
126
|
+
template = ERB.new File.read(template_path)
|
127
|
+
output = template.result(binding)
|
128
|
+
|
129
|
+
File.open(File.join(deploy_subdir, "#{stage}.rb"), 'w') {|f| f.write(output) }
|
130
|
+
end
|
131
|
+
|
132
|
+
def run_commands(commands)
|
133
|
+
puts `#{commands.join(" && ")}`
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "recipes", "release_tagger"))
|
2
|
+
|
3
|
+
set :autotagger_stages, [:ci, :staging, :production]
|
4
|
+
set :stages, [:staging, :production]
|
5
|
+
set :default_stage, :staging
|
6
|
+
|
7
|
+
require 'capistrano/ext/multistage'
|
8
|
+
|
9
|
+
set :application, "deployed"
|
10
|
+
set :user, "<%= user %>"
|
11
|
+
set :repository, "file://<%= repository %>"
|
12
|
+
set :scm, :git
|
13
|
+
set :scm_command, "<%= git_location %>"
|
14
|
+
|
15
|
+
set :deploy_to, "<%= deploy_to %>"
|
16
|
+
|
17
|
+
set :run_method, :run
|
18
|
+
set :ssh_options, { :forward_agent => true }
|
19
|
+
default_run_options[:pty] = true
|
20
|
+
|
21
|
+
role :app, "localhost" # => this must be mapped
|
22
|
+
|
23
|
+
namespace :deploy do
|
24
|
+
task :restart do
|
25
|
+
puts "restarting..."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
before "deploy:update_code", "release_tagger:set_branch"
|
30
|
+
after "deploy", "release_tagger:create_tag"
|
31
|
+
after "deploy", "release_tagger:write_tag_to_shared"
|
32
|
+
after "deploy", "release_tagger:print_latest_tags"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "recipes", "release_tagger"))
|
2
|
+
|
3
|
+
set :autotagger_stages, ["<%= environments.join('", "') %>"]
|
4
|
+
|
5
|
+
set :application, "deployed"
|
6
|
+
set :user, "<%= user %>"
|
7
|
+
set :repository, "file://<%= repository %>"
|
8
|
+
set :deploy_to, "<%= deploy_to %>"
|
9
|
+
set :scm, :git
|
10
|
+
set :scm_command, "<%= git_location %>"
|
11
|
+
|
12
|
+
set :run_method, :run
|
13
|
+
set :ssh_options, { :forward_agent => true }
|
14
|
+
default_run_options[:pty] = true
|
15
|
+
|
16
|
+
role :app, "localhost" # => this must be mapped
|
17
|
+
|
18
|
+
|
19
|
+
namespace :deploy do
|
20
|
+
task :restart do
|
21
|
+
puts "restarting..."
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
<% environments.each do |environment| %>
|
26
|
+
task "<%= environment %>" do
|
27
|
+
set :stage, "<%= environment %>"
|
28
|
+
end
|
29
|
+
<% end %>
|
30
|
+
|
31
|
+
before "deploy:update_code", "release_tagger:set_branch"
|
32
|
+
after "deploy", "release_tagger:create_tag"
|
33
|
+
after "deploy", "release_tagger:write_tag_to_shared"
|
34
|
+
after "deploy", "release_tagger:print_latest_tags"
|
@@ -0,0 +1 @@
|
|
1
|
+
# nothing to see here...
|
data/geminstaller.yml
ADDED
data/lib/auto_tagger.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
class AutoTagger
|
2
|
+
|
3
|
+
class EnvironmentCannotBeBlankError < StandardError; end
|
4
|
+
|
5
|
+
attr_reader :stage, :repository, :working_directory
|
6
|
+
|
7
|
+
def initialize(stage, path = nil)
|
8
|
+
raise EnvironmentCannotBeBlankError if stage.to_s.strip == ""
|
9
|
+
@working_directory = File.expand_path(path ||= Dir.pwd)
|
10
|
+
@repository = Repository.new(@working_directory)
|
11
|
+
@stage = stage
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_tag(commit = nil)
|
15
|
+
repository.tags.fetch
|
16
|
+
new_tag = repository.tags.create(stage, commit)
|
17
|
+
repository.tags.push
|
18
|
+
new_tag
|
19
|
+
end
|
20
|
+
|
21
|
+
def latest_tag
|
22
|
+
repository.tags.fetch
|
23
|
+
repository.tags.latest_from(stage)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class CapistranoHelper
|
2
|
+
|
3
|
+
attr_reader :variables, :stage, :working_directory
|
4
|
+
|
5
|
+
def initialize(variables)
|
6
|
+
@stage_manager = StageManager.new(variables[:autotagger_stages])
|
7
|
+
@variables = variables
|
8
|
+
@stage = variables[:stage]
|
9
|
+
@working_directory = variables[:working_directory] || Dir.pwd
|
10
|
+
end
|
11
|
+
|
12
|
+
def previous_stage
|
13
|
+
@stage_manager.previous_stage(stage)
|
14
|
+
end
|
15
|
+
|
16
|
+
def branch
|
17
|
+
if variables.has_key?(:head)
|
18
|
+
variables[:branch]
|
19
|
+
elsif variables.has_key?(:tag)
|
20
|
+
variables[:tag]
|
21
|
+
elsif previous_stage && (latest = AutoTagger.new(previous_stage, working_directory).latest_tag)
|
22
|
+
latest
|
23
|
+
else
|
24
|
+
variables[:branch]
|
25
|
+
end
|
26
|
+
end
|
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
|
+
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class Commander
|
2
|
+
class << self
|
3
|
+
def execute(path, cmd)
|
4
|
+
`#{command_in_context(path, cmd)}`
|
5
|
+
end
|
6
|
+
|
7
|
+
def execute!(path, cmd)
|
8
|
+
system command_in_context(path, cmd)
|
9
|
+
end
|
10
|
+
|
11
|
+
def command_in_context(path, cmd)
|
12
|
+
"cd #{path} && #{cmd}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|