kumade 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,6 +2,7 @@ rvm:
2
2
  - 1.9.2
3
3
  - 1.8.7
4
4
  - ree-1.8.7
5
+ - 1.8.7-p249
5
6
  # branches:
6
7
  # only:
7
8
  # - master
data/README.md CHANGED
@@ -3,6 +3,11 @@ Kumade is a set of basic Rake tasks for deploying to Heroku. It aims to
3
3
  provide most of what you want. Unlike other Heroku deploy gems, it is
4
4
  well-tested.
5
5
 
6
+ ## Development
7
+ Development is happening very fast, and the internals are in constant flux. The
8
+ public API is constant (e.g. `kumade production` will work), but you may have to
9
+ rebase against master a couple times before your pull request can be merged.
10
+
6
11
  ## What does Kumade do?
7
12
  Before deploying, Kumade ensures the git repo is clean and that all tests pass.
8
13
  After that, it packages assets using
@@ -28,6 +33,14 @@ For example, if you have a remote named "bamboo":
28
33
 
29
34
  $ bundle exec kumade bamboo
30
35
 
36
+ or
37
+
38
+ # in your Rakefile:
39
+ require 'kumade'
40
+
41
+ # kumade auto-generates a deploy:ENV task for every Heroku environment
42
+ $ rake deploy:bamboo
43
+
31
44
  which will autodetect the name of the Heroku app that the bamboo remote points
32
45
  to and deploy to it.
33
46
 
@@ -40,93 +53,13 @@ The default is to deploy to staging:
40
53
 
41
54
  $ bundle exec kumade # equivalent to "bundle exec kumade staging"
42
55
 
56
+
43
57
  ## Does it support the Cedar stack?
44
58
 
45
59
  Yes. To indicate that a particular app is using Cedar, run with the -c flag:
46
60
 
47
61
  bundle exec kumade bamboo -c
48
62
 
49
- ## Sample Output
50
-
51
- ### Normal mode
52
-
53
- $ kumade heroku-staging
54
- ==> Deploying to: heroku-staging
55
- ==> heroku-staging is a Heroku remote
56
- ==> Git repo is clean
57
- /Users/gabe/.rvm/rubies/ree-1.8.7-2011.03/bin/ruby -S bundle exec rspec [blah blah]
58
- ....
59
- rake output removed
60
- ...
61
- ==> Rake passed
62
- ==> Packaged assets with Jammit
63
- ==> + git add /Users/gabe/thoughtbot/sushi/public/assets && git commit -m 'Assets'
64
- [master bc8932b] Assets
65
- 4 files changed, 0 insertions(+), 0 deletions(-)
66
- ==> - true
67
- ==> Added and committed all assets
68
- ==> + git push origin master
69
- Counting objects: 15, done.
70
- Delta compression using up to 2 threads.
71
- Compressing objects: 100% (8/8), done.
72
- Writing objects: 100% (8/8), 639 bytes, done.
73
- Total 8 (delta 7), reused 0 (delta 0)
74
- To git@github.com:sushi/sushi.git
75
- a465afd..bc8932b master -> master
76
- ==> - true
77
- ==> Pushed master -> origin
78
- ==> + git push -f heroku-staging master
79
- Counting objects: 15, done.
80
- Delta compression using up to 2 threads.
81
- Compressing objects: 100% (8/8), done.
82
- Writing objects: 100% (8/8), 639 bytes, done.
83
- Total 8 (delta 7), reused 0 (delta 0)
84
-
85
- -----> Heroku receiving push
86
- -----> Rails app detected
87
- -----> Detected Rails is not set to serve static_assets
88
- Installing rails3_serve_static_assets... done
89
- -----> Configure Rails 3 to disable x-sendfile
90
- Installing rails3_disable_x_sendfile... done
91
- -----> Configure Rails to log to stdout
92
- Installing rails_log_stdout... done
93
- -----> Gemfile detected, running Bundler version 1.0.7
94
- All dependencies are satisfied
95
- -----> Compiled slug size is 65.5MB
96
- -----> Launching... done, v172
97
- http://staging-sushi.heroku.com deployed to Heroku
98
-
99
- To git@heroku.com:staging-sushi.git
100
- a465afd..bc8932b master -> master
101
- ==> - true
102
- ==> Force pushed master -> heroku-staging
103
- ==> + bundle exec heroku rake db:migrate --app staging-sushi
104
- ... Postgres output removed ...
105
- ==> - false
106
- ==> Migrated staging-sushi
107
- ==> Deployed to: heroku-staging
108
-
109
- ### Pretend mode
110
-
111
- $ kumade heroku-staging -p
112
- ==> In Pretend Mode
113
- ==> Deploying to: heroku-staging
114
- ==> heroku-staging is a Heroku remote
115
- ==> Git repo is clean
116
- ==> Rake passed
117
- ==> Packaged assets with Jammit
118
- ==> Pushed master -> origin
119
- ==> Force pushed master -> heroku-staging
120
- ==> Migrated staging-sushi
121
- ==> Deployed to: heroku-staging
122
-
123
- ### Pretend Mode with a non-Heroku remote
124
-
125
- $ kumade origin -p
126
- ==> In Pretend Mode
127
- ==> Deploying to: origin
128
- ==> ! Cannot deploy: "origin" remote does not point to Heroku
129
-
130
63
  ## Compatibility
131
64
 
132
65
  Tested against:
@@ -137,7 +70,7 @@ Tested against:
137
70
 
138
71
  ## Misc Features
139
72
 
140
- Want to run a task before bundling your assets on deploy? In your rails app's rake tasks, drop in:
73
+ Want to run a task before bundling your assets on deploy? In your Rails app's rake tasks, drop in:
141
74
 
142
75
  ``` ruby
143
76
  namespace :kumade do
@@ -26,8 +26,8 @@ Feature: Kumade executable
26
26
  ==> Pushed master -> origin
27
27
  run git branch deploy
28
28
  run git push -f pretend-staging deploy:master
29
- ==> Force pushed master -> pretend-staging
30
- ==> Migrated pretend-staging-app
29
+ ==> Pushed deploy:master -> pretend-staging
30
+ ==> Migrated pretend-staging
31
31
  run git checkout master && git branch -D deploy
32
32
  ==> Deployed to: pretend-staging
33
33
  """
@@ -57,8 +57,8 @@ Feature: Kumade executable
57
57
  ==> Pushed new_branch -> origin
58
58
  run git branch deploy
59
59
  run git push -f pretend-staging deploy:master
60
- ==> Force pushed new_branch -> pretend-staging
61
- ==> Migrated pretend-staging-app
60
+ ==> Pushed deploy:master -> pretend-staging
61
+ ==> Migrated pretend-staging
62
62
  run git checkout new_branch && git branch -D deploy
63
63
  ==> Deployed to: pretend-staging
64
64
  """
@@ -7,8 +7,8 @@ Gem::Specification.new do |s|
7
7
  s.version = Kumade::VERSION
8
8
  s.authors = ["Gabe Berke-Williams", "thoughtbot"]
9
9
  s.email = ["gabe@thoughtbot.com", "support@thoughtbot.com"]
10
- s.homepage = ""
11
- s.summary = %q{Simple rake tasks for deploying to Heroku}
10
+ s.homepage = "http://thoughtbot.com/community/"
11
+ s.summary = %q{A well-tested script for easy deploying to Heroku}
12
12
  s.description = s.summary
13
13
 
14
14
  s.files = `git ls-files`.split("\n")
@@ -18,8 +18,9 @@ Gem::Specification.new do |s|
18
18
 
19
19
  s.add_dependency('heroku', '~> 2.0')
20
20
  s.add_dependency('thor', '~> 0.14')
21
+ s.add_dependency('rake', '>= 0.8.7')
21
22
 
22
- s.add_development_dependency('rake', '~> 0.8.7')
23
+ s.add_development_dependency('rake', '>= 0.8.7')
23
24
  s.add_development_dependency('rspec', '~> 2.6.0')
24
25
  s.add_development_dependency('cucumber', '~> 1.0.2')
25
26
  s.add_development_dependency('aruba', '~> 0.4.3')
@@ -1,29 +1,13 @@
1
1
  require 'rake'
2
2
  require 'thor'
3
+ require 'stringio'
3
4
 
5
+ require 'kumade/base'
6
+ require 'kumade/git'
4
7
  require 'kumade/deployer'
5
8
  require 'kumade/runner'
6
9
  require 'kumade/railtie'
10
+ require 'kumade/deployment_error'
7
11
 
8
12
  module Kumade
9
- def self.app_for(environment)
10
- heroku_git_url = `git config --get remote.#{environment}.url`.strip
11
- if heroku_git_url =~ /^git@heroku\.com:(.+)\.git$/
12
- $1
13
- else
14
- nil
15
- end
16
- end
17
- def self.environments
18
- url_remotes = `git remote`.strip.split("\n").map{|remote| [remote, `git config --get remote.#{remote}.url`.strip] }.select{|remote| remote.last =~ /^git@heroku\.com:(.+)\.git$/}.map{|remote| remote.first}
19
- end
20
-
21
- def self.on_cedar!(app)
22
- @cedar_apps ||= []
23
- @cedar_apps << app
24
- end
25
-
26
- def self.cedar?(app)
27
- @cedar_apps.include?(app)
28
- end
29
13
  end
@@ -0,0 +1,30 @@
1
+ module Kumade
2
+ class Base < Thor::Shell::Color
3
+ def initialize
4
+ super()
5
+ end
6
+
7
+ def run_or_error(commands, error_message)
8
+ all_commands = [commands].flatten.join(' && ')
9
+ if @pretending
10
+ say_status(:run, all_commands)
11
+ else
12
+ error(error_message) unless run(all_commands)
13
+ end
14
+ end
15
+
16
+ def run(command, config = {})
17
+ say_status :run, command
18
+ config[:capture] ? `#{command}` : system("#{command}")
19
+ end
20
+
21
+ def error(message)
22
+ say("==> ! #{message}", :red)
23
+ raise Kumade::DeploymentError.new(message)
24
+ end
25
+
26
+ def success(message)
27
+ say("==> #{message}", :green)
28
+ end
29
+ end
30
+ end
@@ -1,14 +1,14 @@
1
1
  module Kumade
2
- class Deployer < Thor::Shell::Color
2
+ class Deployer < Base
3
3
  DEPLOY_BRANCH = "deploy"
4
- attr_reader :environment, :pretending
4
+ attr_reader :environment, :pretending, :git
5
5
 
6
- def initialize(environment = 'staging', pretending = false, cedar = false)
6
+ def initialize(environment = 'staging', pretending = false)
7
7
  super()
8
8
  @environment = environment
9
9
  @pretending = pretending
10
- @branch = current_branch
11
- @cedar = cedar
10
+ @git = Git.new(pretending, environment)
11
+ @branch = @git.current_branch
12
12
  end
13
13
 
14
14
  def deploy
@@ -26,50 +26,44 @@ module Kumade
26
26
  end
27
27
 
28
28
  def sync_github
29
- run_or_error("git push origin #{@branch}",
30
- "Failed to push #{@branch} -> origin")
31
- success("Pushed #{@branch} -> origin")
29
+ git.push(@branch)
32
30
  end
33
31
 
34
32
  def sync_heroku
35
- unless branch_exist?(DEPLOY_BRANCH)
36
- run_or_error("git branch deploy", "Failed to create #{DEPLOY_BRANCH}")
37
- end
38
- run_or_error("git push -f #{environment} #{DEPLOY_BRANCH}:master",
39
- "Failed to force push #{DEPLOY_BRANCH} -> #{environment}/master")
40
- success("Force pushed #{@branch} -> #{environment}")
33
+ git.create(DEPLOY_BRANCH)
34
+ git.push("#{DEPLOY_BRANCH}:master", environment, true)
41
35
  end
42
36
 
43
37
  def heroku_migrate
44
- app = Kumade.app_for(environment)
45
-
46
- heroku("rake db:migrate", app) unless pretending
47
- success("Migrated #{app}")
38
+ heroku("rake db:migrate") unless pretending
39
+ success("Migrated #{environment}")
48
40
  end
49
41
 
50
42
  def post_deploy
51
- run_or_error(["git checkout #{@branch}", "git branch -D #{DEPLOY_BRANCH}"],
52
- "Failed to clean up #{DEPLOY_BRANCH} branch")
43
+ git.delete(DEPLOY_BRANCH, @branch)
53
44
  end
54
45
 
55
- def heroku(command, app)
56
- heroku_command = if @cedar
46
+ def heroku(command)
47
+ heroku_command = if cedar?
57
48
  "bundle exec heroku run"
58
49
  else
59
50
  "bundle exec heroku"
60
51
  end
61
- run_or_error("#{heroku_command} #{command} --app #{app}",
52
+ run_or_error("#{heroku_command} #{command} --remote #{environment}",
62
53
  "Failed to run #{command} on Heroku")
63
54
  end
64
55
 
65
- def ensure_clean_git
66
- if ! pretending && git_dirty?
67
- error("Cannot deploy: repo is not clean.")
68
- else
69
- success("Git repo is clean")
56
+ def cedar?
57
+ return @cedar unless @cedar.nil?
58
+ @cedar = heroku("stack").split("\n").grep(/\*/).any? do |line|
59
+ line.include?("cedar")
70
60
  end
71
61
  end
72
62
 
63
+ def ensure_clean_git
64
+ git.ensure_clean_git
65
+ end
66
+
73
67
  def package_assets
74
68
  invoke_custom_task if custom_task?
75
69
  package_with_jammit if jammit_installed?
@@ -100,7 +94,7 @@ module Kumade
100
94
  else
101
95
  begin
102
96
  run "bundle exec rake more:generate"
103
- if git_dirty?
97
+ if git.dirty?
104
98
  success(success_message)
105
99
  git_add_and_commit_all_assets_in(more_assets_path)
106
100
  end
@@ -116,10 +110,7 @@ module Kumade
116
110
  end
117
111
 
118
112
  def git_add_and_commit_all_assets_in(dir)
119
- run_or_error ["git checkout -b #{DEPLOY_BRANCH}", "git add -f #{dir}", "git commit -m 'Compiled assets'"],
120
- "Cannot deploy: couldn't commit assets"
121
-
122
- success "Added and committed all assets"
113
+ git.add_and_commit_all_in(dir, DEPLOY_BRANCH, 'Compiled assets', "Added and committed all assets", "couldn't commit assets")
123
114
  end
124
115
 
125
116
  def jammit_assets_path
@@ -157,44 +148,9 @@ module Kumade
157
148
  Rake::Task.task_defined?("kumade:before_asset_compilation")
158
149
  end
159
150
 
160
- def git_dirty?
161
- `git diff --exit-code`
162
- !$?.success?
163
- end
164
-
165
- def run_or_error(commands, error_message)
166
- all_commands = [commands].flatten.join(' && ')
167
- if pretending
168
- say_status(:run, all_commands)
169
- else
170
- error(error_message) unless run(all_commands)
171
- end
172
- end
173
-
174
- def run(command, config = {})
175
- say_status :run, command
176
- config[:capture] ? `#{command}` : system("#{command}")
177
- end
178
-
179
- def branch_exist?(branch)
180
- branches = `git branch`
181
- regex = Regexp.new('[\\n\\s\\*]+' + Regexp.escape(branch.to_s) + '\\n')
182
- result = ((branches =~ regex) ? true : false)
183
- return result
184
- end
185
-
186
- def error(message)
187
- say("==> ! #{message}", :red)
188
- exit 1
189
- end
190
-
191
- def success(message)
192
- say("==> #{message}", :green)
193
- end
194
-
195
151
  def ensure_heroku_remote_exists
196
- if remote_exists?(environment)
197
- if app_name = Kumade.app_for(environment)
152
+ if git.remote_exists?(environment)
153
+ if git.heroku_remote?
198
154
  success("#{environment} is a Heroku remote")
199
155
  else
200
156
  error(%{Cannot deploy: "#{environment}" remote does not point to Heroku})
@@ -203,17 +159,5 @@ module Kumade
203
159
  error(%{Cannot deploy: "#{environment}" remote does not exist})
204
160
  end
205
161
  end
206
-
207
- def remote_exists?(remote_name)
208
- if pretending
209
- true
210
- else
211
- `git remote` =~ /^#{remote_name}$/
212
- end
213
- end
214
-
215
- def current_branch
216
- `git symbolic-ref HEAD`.sub("refs/heads/", "").strip
217
- end
218
162
  end
219
163
  end
@@ -0,0 +1,4 @@
1
+ module Kumade
2
+ class DeploymentError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,73 @@
1
+ module Kumade
2
+ class Git < Base
3
+ attr_reader :environment
4
+ def initialize(pretending, environment)
5
+ super()
6
+ @pretending = pretending
7
+ @environment = environment
8
+ end
9
+
10
+ def heroku_remote?
11
+ `git config --get remote.#{environment}.url`.strip =~ /^git@heroku\..+:(.+)\.git$/
12
+ end
13
+
14
+ def self.environments
15
+ url_remotes = `git remote`.strip.split("\n").map{|remote| [remote, `git config --get remote.#{remote}.url`.strip] }.select{|remote| remote.last =~ /^git@heroku\.com:(.+)\.git$/}.map{|remote| remote.first}
16
+ end
17
+
18
+ def push(branch, remote = 'origin', force = false)
19
+ command = ["git push"]
20
+ command << "-f" if force
21
+ command << remote
22
+ command << branch
23
+ command = command.join(" ")
24
+ run_or_error([command], "Failed to push #{branch} -> #{remote}")
25
+ success("Pushed #{branch} -> #{remote}")
26
+ end
27
+
28
+ def create(branch)
29
+ unless branch_exist?(branch)
30
+ run_or_error("git branch #{branch}", "Failed to create #{branch}")
31
+ end
32
+ end
33
+
34
+ def delete(branch_to_delete, branch_to_checkout)
35
+ run_or_error(["git checkout #{branch_to_checkout}", "git branch -D #{branch_to_delete}"],
36
+ "Failed to clean up #{branch_to_delete} branch")
37
+ end
38
+
39
+ def add_and_commit_all_in(dir, branch, commit_message, success_output, error_output)
40
+ run_or_error ["git checkout -b #{branch}", "git add -f #{dir}", "git commit -m '#{commit_message}'"],
41
+ "Cannot deploy: #{error_output}"
42
+ success success_output
43
+ end
44
+
45
+ def current_branch
46
+ `git symbolic-ref HEAD`.sub("refs/heads/", "").strip
47
+ end
48
+
49
+ def remote_exists?(remote_name)
50
+ if @pretending
51
+ true
52
+ else
53
+ `git remote` =~ /^#{remote_name}$/
54
+ end
55
+ end
56
+
57
+ def dirty?
58
+ ! system("git diff --exit-code")
59
+ end
60
+
61
+ def ensure_clean_git
62
+ if ! @pretending && dirty?
63
+ error("Cannot deploy: repo is not clean.")
64
+ else
65
+ success("Git repo is clean")
66
+ end
67
+ end
68
+
69
+ def branch_exist?(branch)
70
+ system("git show-ref #{branch}")
71
+ end
72
+ end
73
+ end