kumade 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.9.2
3
+ - 1.8.7
4
+ - ree-1.8.7
5
+ # branches:
6
+ # only:
7
+ # - master
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Kumade
1
+ # Kumade [![Build Status](https://secure.travis-ci.org/gabebw/kumade.png)](http://travis-ci.org/gabebw/kumade)
2
2
  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.
@@ -7,11 +7,11 @@ well-tested.
7
7
  Before deploying, Kumade ensures the git repo is clean and that all tests pass.
8
8
  After that, it packages assets using
9
9
  [Jammit](http://documentcloud.github.com/jammit/) and/or
10
- [More](https://github.com/cloudhead/more), commits them, and pushes to
11
- origin. Then it force pushes to the staging or production remote as
12
- appropriate and runs `rake db:migrate` on the Heroku app.
10
+ [More](https://github.com/cloudhead/more), commits them, and pushes to origin.
11
+ Then it force pushes to the correct remote and runs `rake db:migrate` on the
12
+ Heroku app.
13
13
 
14
- If any step fails, it immediately raises an error and stops the deploy
14
+ If any step fails, it immediately prints an error and stops the deploy
15
15
  process.
16
16
 
17
17
  ## Install
@@ -22,50 +22,109 @@ gem 'kumade'
22
22
  ```
23
23
 
24
24
  ## Usage
25
- In your Rakefile:
25
+ kumade will deploy to any remote in the repo.
26
+ For example, if you have a remote named "bamboo":
27
+
28
+ $ kumade deploy bamboo
29
+
30
+ which will autodetect the name of the Heroku app that the bamboo remote points
31
+ to, and deploy to it.
32
+
33
+ To run in pretend mode, which just prints what would be done:
34
+
35
+ $ kumade deploy bamboo -p
36
+
37
+ The default task is to deploy to staging:
38
+
39
+ $ kumade # equivalent to "kumade deploy staging"
40
+
41
+ ## Sample Output
42
+
43
+ ### Normal mode
44
+
45
+ $ kumade deploy heroku-staging
46
+ ==> Deploying to: heroku-staging
47
+ ==> heroku-staging is a Heroku remote
48
+ ==> Git repo is clean
49
+ /Users/gabe/.rvm/rubies/ree-1.8.7-2011.03/bin/ruby -S bundle exec rspec [blah blah]
50
+ ....
51
+ rake output removed
52
+ ...
53
+ ==> Rake passed
54
+ ==> Packaged assets with Jammit
55
+ ==> + git add /Users/gabe/thoughtbot/sushi/public/assets && git commit -m 'Assets'
56
+ [master bc8932b] Assets
57
+ 4 files changed, 0 insertions(+), 0 deletions(-)
58
+ ==> - true
59
+ ==> Added and committed all assets
60
+ ==> + git push origin master
61
+ Counting objects: 15, done.
62
+ Delta compression using up to 2 threads.
63
+ Compressing objects: 100% (8/8), done.
64
+ Writing objects: 100% (8/8), 639 bytes, done.
65
+ Total 8 (delta 7), reused 0 (delta 0)
66
+ To git@github.com:sushi/sushi.git
67
+ a465afd..bc8932b master -> master
68
+ ==> - true
69
+ ==> Pushed master -> origin
70
+ ==> + git push -f heroku-staging master
71
+ Counting objects: 15, done.
72
+ Delta compression using up to 2 threads.
73
+ Compressing objects: 100% (8/8), done.
74
+ Writing objects: 100% (8/8), 639 bytes, done.
75
+ Total 8 (delta 7), reused 0 (delta 0)
76
+
77
+ -----> Heroku receiving push
78
+ -----> Rails app detected
79
+ -----> Detected Rails is not set to serve static_assets
80
+ Installing rails3_serve_static_assets... done
81
+ -----> Configure Rails 3 to disable x-sendfile
82
+ Installing rails3_disable_x_sendfile... done
83
+ -----> Configure Rails to log to stdout
84
+ Installing rails_log_stdout... done
85
+ -----> Gemfile detected, running Bundler version 1.0.7
86
+ All dependencies are satisfied
87
+ -----> Compiled slug size is 65.5MB
88
+ -----> Launching... done, v172
89
+ http://staging-sushi.heroku.com deployed to Heroku
90
+
91
+ To git@heroku.com:staging-sushi.git
92
+ a465afd..bc8932b master -> master
93
+ ==> - true
94
+ ==> Force pushed master -> heroku-staging
95
+ ==> + bundle exec heroku rake db:migrate --app staging-sushi
96
+ ... Postgres output removed ...
97
+ ==> - false
98
+ ==> Migrated staging-sushi
99
+ ==> Deployed to: heroku-staging
100
+
101
+ ### Pretend mode
102
+
103
+ $ kumade deploy heroku-staging -p
104
+ ==> In Pretend Mode
105
+ ==> Deploying to: heroku-staging
106
+ ==> heroku-staging is a Heroku remote
107
+ ==> Git repo is clean
108
+ ==> Rake passed
109
+ ==> Packaged assets with Jammit
110
+ ==> Pushed master -> origin
111
+ ==> Force pushed master -> heroku-staging
112
+ ==> Migrated staging-sushi
113
+ ==> Deployed to: heroku-staging
114
+
115
+ ### Pretend Mode with a non-Heroku remote
116
+
117
+ $ kumade deploy origin -p
118
+ ==> In Pretend Mode
119
+ ==> Deploying to: origin
120
+ ==> ! Cannot deploy: "origin" remote does not point to Heroku
26
121
 
27
- ```ruby
28
- Kumade.load_tasks
29
- ## Set the name of the staging remote (autodetected by default)
30
- # Kumade.staging_remote = 'staging'
31
- ## Set the name of the production remote (autodetected by default)
32
- # Kumade.production_remote = 'production'
33
-
34
- # Set the name of the staging app on Heroku (required)
35
- Kumade.staging_app = 'my-staging-app'
36
- # Set the name of the production app on Heroku (required)
37
- Kumade.production_app = 'my-production-app'
38
- ```
39
-
40
- Now running `rake -T` shows the new tasks:
41
-
42
- ```bash
43
- rake deploy # Alias for kumade:deploy
44
- rake deploy:production # Alias for kumade:deploy:production
45
- rake deploy:staging # Alias for kumade:deploy:staging
46
-
47
- rake kumade:deploy # Alias for kumade:deploy:staging
48
- rake kumade:deploy:production # Deploy to Heroku production
49
- rake kumade:deploy:staging # Deploy to Heroku staging
50
- ```
51
-
52
- If you only want the namespaced tasks (the ones with "kumade:" in front), do
53
- this in your Rakefile:
54
-
55
- ```ruby
56
- Kumade.load_namespaced_tasks
57
- ```
58
-
59
- Now `rake -T` will only show this:
60
-
61
- ```bash
62
- rake kumade:deploy # Alias for kumade:deploy:staging
63
- rake kumade:deploy:production # Deploy to Heroku production
64
- rake kumade:deploy:staging # Deploy to Heroku staging
65
- ```
66
122
 
67
123
  ## Compatibility
68
- Tested on MRI 1.8.7 and 1.9.2.
124
+ Tested against:
125
+ * MRI 1.8.7
126
+ * MRI 1.9.2
127
+ * REE 1.8.7
69
128
 
70
129
  ## What's with the name?
71
130
  Kumade ([pronunciation here](http://translate.google.com/#ja|en|熊手)) means
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'kumade'
4
+
5
+ Kumade::ThorTask.start
@@ -0,0 +1,35 @@
1
+ @creates-remote
2
+ Feature: Kumade executable
3
+ As a user
4
+ I want to be able to use the kumade executable
5
+ So I can have a better experience than Rake provides
6
+
7
+ Background:
8
+ Given a directory named "executable"
9
+ And I cd to "executable"
10
+ When I create a Heroku remote for "pretend-staging-app" named "pretend-staging"
11
+ And I create a Heroku remote for "app-two" named "staging"
12
+
13
+ Scenario: Pretend mode with a Heroku remote
14
+ When I run `kumade deploy pretend-staging -p`
15
+ Then the output should contain "In Pretend Mode"
16
+ And the output should contain:
17
+ """
18
+ ==> Git repo is clean
19
+ ==> Rake passed
20
+ ==> Packaged assets with Jammit
21
+ ==> Pushed master -> origin
22
+ ==> Force pushed master -> pretend-staging
23
+ ==> Migrated pretend-staging-app
24
+ ==> Deployed to: pretend-staging
25
+ """
26
+ But the output should not contain "==> Packaged assets with More"
27
+
28
+ Scenario: Default environment is staging
29
+ When I run `kumade -p`
30
+ Then the output should contain "==> Deployed to: staging"
31
+
32
+ Scenario: Can deploy to arbitrary environment
33
+ When I run `kumade deploy bamboo`
34
+ Then the output should contain "==> Deploying to: bamboo"
35
+ Then the output should match /Cannot deploy: /
@@ -0,0 +1,3 @@
1
+ When /^I add "([^"]*)" from this project as a dependency$/ do |gem_name|
2
+ append_to_file('Gemfile', %{\ngem "#{gem_name}", :path => "#{PROJECT_ROOT}"})
3
+ end
@@ -0,0 +1,9 @@
1
+ When /^I create a Heroku remote for "([^"]*)" named "([^"]*)"$/ do |app_name, remote_name|
2
+ `git remote add #{remote_name} git@heroku.com:#{app_name}.git`
3
+
4
+ end
5
+
6
+ After("@creates-remote") do
7
+ heroku_remotes = `git remote -v show | grep heroku | grep fetch | cut -f1`.strip.split
8
+ heroku_remotes.each { |remote| `git remote rm #{remote} 2> /dev/null` }
9
+ end
@@ -1,5 +1,2 @@
1
1
  require 'aruba/cucumber'
2
2
  require 'kumade'
3
- require 'cucumber/rspec/doubles'
4
-
5
- PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..')).freeze
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.require_paths = ["lib"]
19
19
 
20
20
  s.add_dependency('heroku')
21
+ s.add_dependency('thor', '~> 0.14')
21
22
 
22
23
  s.add_development_dependency('rake', '~> 0.8.7')
23
24
  s.add_development_dependency('rspec', '~> 2.6.0')
@@ -1,45 +1,14 @@
1
1
  require 'kumade/deployer'
2
-
3
- class Kumade
4
- def self.load_tasks
5
- deployer.load_tasks
6
- end
7
-
8
- def self.load_namespaced_tasks
9
- deployer.load_namespaced_tasks
10
- end
11
-
12
- def self.deployer
13
- @deployer ||= Deployer.new
14
- end
15
-
16
- class << self
17
- attr_writer :staging_remote, :production_remote
18
- attr_accessor :staging_app, :production_app
19
-
20
- def reset!
21
- @staging_remote = nil
22
- @production_remote = nil
23
-
24
- @staging_app = nil
25
- @production_app = nil
26
- end
27
-
28
- def staging_remote
29
- @staging_remote ||= local_remote_for_app(Kumade.staging_app)
30
- end
31
-
32
- def production_remote
33
- @production_remote ||= local_remote_for_app(Kumade.production_app)
34
- end
35
-
36
- def local_remote_for_app(app_name)
37
- heroku_remote_url = heroku_remote_url_for_app(app_name)
38
- `git remote -v | grep push | grep '#{heroku_remote_url}' | cut -f1`.strip
39
- end
40
-
41
- def heroku_remote_url_for_app(app_name)
42
- "git@heroku.com:#{app_name}.git"
2
+ require 'kumade/thor_task'
3
+
4
+ module Kumade
5
+ def self.app_for(environment)
6
+ heroku_git_url = `git remote show -n #{environment} | grep 'Push URL' | cut -d' ' -f6`.strip
7
+ match = heroku_git_url.match(/^git@heroku\.com:(.+)\.git$/)
8
+ if match
9
+ match[1]
10
+ else
11
+ ""
43
12
  end
44
13
  end
45
14
  end
@@ -1,12 +1,13 @@
1
- class Kumade
2
- class Deployer
3
- def load_tasks
4
- load_namespaced_tasks
5
- load 'kumade/tasks/deploy.rake'
6
- end
1
+ require 'thor/shell'
2
+ require 'rake'
3
+
4
+ module Kumade
5
+ class Deployer < Thor::Shell::Color
6
+ attr_reader :pretending
7
7
 
8
- def load_namespaced_tasks
9
- load 'kumade/tasks/namespaced_deploy.rake'
8
+ def initialize(pretending = false)
9
+ super()
10
+ @pretending = !!pretending
10
11
  end
11
12
 
12
13
  def pre_deploy
@@ -16,51 +17,54 @@ class Kumade
16
17
  git_push('origin')
17
18
  end
18
19
 
19
- def deploy_to_staging
20
- ensure_staging_app_is_present
21
- pre_deploy
22
- git_force_push(Kumade.staging_remote)
23
- heroku_migrate(:staging)
24
- end
25
-
26
- def deploy_to_production
27
- ensure_production_app_is_present
20
+ def deploy_to(environment)
21
+ string_environment = environment.to_s
22
+ ensure_heroku_remote_exists_for(string_environment)
28
23
  pre_deploy
29
- git_force_push(Kumade.production_remote)
30
- heroku_migrate(:production)
24
+ git_force_push(string_environment)
25
+ heroku_migrate(string_environment)
31
26
  end
32
27
 
33
28
  def git_push(remote)
34
- run_or_raise("git push #{remote} master",
35
- "Failed to push master -> #{remote}")
36
- announce "Pushed master -> #{remote}"
29
+ unless pretending
30
+ run_or_error("git push #{remote} master",
31
+ "Failed to push master -> #{remote}")
32
+ end
33
+ success("Pushed master -> #{remote}")
37
34
  end
38
35
 
39
36
  def git_force_push(remote)
40
- run_or_raise("git push -f #{remote} master",
41
- "Failed to force push master -> #{remote}")
42
- announce "Force pushed master -> #{remote}"
37
+ unless pretending
38
+ run_or_error("git push -f #{remote} master",
39
+ "Failed to force push master -> #{remote}")
40
+ end
41
+ success("Force pushed master -> #{remote}")
43
42
  end
44
43
 
45
44
  def heroku_migrate(environment)
46
- app = if environment == :staging
47
- Kumade.staging_app
48
- elsif environment == :production
49
- Kumade.production_app
50
- end
45
+ app = Kumade.app_for(environment)
51
46
 
52
- run("bundle exec heroku rake db:migrate --app #{app}")
47
+ unless pretending
48
+ run("bundle exec heroku rake db:migrate --app #{app}")
49
+ end
50
+ success("Migrated #{app}")
53
51
  end
54
52
 
55
53
  def ensure_clean_git
56
- if git_dirty?
57
- raise "Cannot deploy: repo is not clean."
54
+ if git_dirty? && ! pretending
55
+ error("Cannot deploy: repo is not clean.")
56
+ else
57
+ success("Git repo is clean")
58
58
  end
59
59
  end
60
60
 
61
61
  def ensure_rake_passes
62
62
  if default_task_exists?
63
- raise "Cannot deploy: tests did not pass" unless rake_succeeded?
63
+ if pretending || rake_succeeded?
64
+ success("Rake passed")
65
+ else
66
+ error("Cannot deploy: tests did not pass")
67
+ end
64
68
  end
65
69
  end
66
70
 
@@ -70,35 +74,45 @@ class Kumade
70
74
  end
71
75
 
72
76
  def package_with_jammit
73
- Jammit.package!
74
- announce("Successfully packaged with Jammit")
75
- if git_dirty?
76
- git_add_and_commit_all_assets_in(absolute_assets_path)
77
+ begin
78
+ success_message = "Packaged assets with Jammit"
79
+
80
+ if pretending
81
+ success(success_message)
82
+ else
83
+ Jammit.package!
84
+
85
+ if git_dirty?
86
+ success(success_message)
87
+ git_add_and_commit_all_assets_in(jammit_assets_path)
88
+ end
89
+ end
90
+ rescue => jammit_error
91
+ error("Error: #{jammit_error.class}: #{jammit_error.message}")
77
92
  end
78
93
  end
79
94
 
80
95
  def package_with_more
81
- Rake::Task['more:generate'].invoke
82
- if git_dirty?
83
- announce("Successfully packaged with More")
84
-
85
- git_add_and_commit_all_assets_in(more_assets_path)
96
+ begin
97
+ system "bundle exec rake more:generate"
98
+ if git_dirty?
99
+ success("Packaged assets with More")
100
+
101
+ git_add_and_commit_all_assets_in(more_assets_path)
102
+ end
103
+ rescue => more_error
104
+ error("Error: #{more_error.class}: #{more_error.message}")
86
105
  end
87
106
  end
88
107
 
89
108
  def git_add_and_commit_all_assets_in(dir)
90
- announce "Committing assets"
91
- run_or_raise("git add #{dir} && git commit -m 'Assets'",
109
+ run_or_error("git add #{dir} && git commit -m 'Assets'",
92
110
  "Cannot deploy: couldn't commit assets")
93
- end
94
111
 
95
- def git_add_and_commit_all_more_assets
96
- announce "Committing assets"
97
- run_or_raise("git add #{more_assets_path} && git commit -m 'Assets'",
98
- "Cannot deploy: couldn't commit assets")
112
+ success("Added and committed all assets")
99
113
  end
100
114
 
101
- def absolute_assets_path
115
+ def jammit_assets_path
102
116
  File.join(Jammit::PUBLIC_ROOT, Jammit.package_path)
103
117
  end
104
118
 
@@ -127,12 +141,12 @@ class Kumade
127
141
  end
128
142
 
129
143
  def default_task_exists?
130
- Rake::Task.task_defined?('default')
144
+ `rake -s -P | grep 'rake default'`.strip.size > 0
131
145
  end
132
146
 
133
147
  def rake_succeeded?
134
148
  begin
135
- Rake::Task[:default].invoke
149
+ system "bundle exec rake"
136
150
  rescue
137
151
  false
138
152
  end
@@ -149,27 +163,58 @@ class Kumade
149
163
  $?.success?
150
164
  end
151
165
 
152
- def run_or_raise(command, error_message)
153
- raise(error_message) unless run(command)
166
+ def run_or_error(command, error_message)
167
+ if ! pretending
168
+ unless run(command)
169
+ error(error_message)
170
+ end
171
+ end
154
172
  end
155
173
 
156
174
  def announce(message)
157
- puts message
175
+ say "==> #{message}"
176
+ end
177
+
178
+ def error(message)
179
+ say("==> ! #{message}", :red)
180
+ exit 1
181
+ end
182
+
183
+ def success(message)
184
+ say("==> #{message}", :green)
158
185
  end
159
186
 
160
187
  def string_present?(maybe_string)
161
188
  maybe_string.is_a?(String) && maybe_string.size > 0
162
189
  end
163
190
 
164
- def ensure_staging_app_is_present
165
- unless string_present?(Kumade.staging_app)
166
- raise "Cannot deploy: Kumade.staging_app is not present"
191
+ def ensure_heroku_remote_exists_for(environment)
192
+ if remote_exists?(environment)
193
+ if Kumade.app_for(environment)
194
+ app_name = Kumade.app_for(environment)
195
+ if string_present?(app_name)
196
+ success("#{environment} is a Heroku remote")
197
+ else
198
+ error(%{Cannot deploy: "#{environment}" remote does not point to Heroku})
199
+ end
200
+ end
201
+ else
202
+ error(%{Cannot deploy: "#{environment}" remote does not exist})
203
+ end
204
+ end
205
+
206
+ def remote_exists?(remote_name)
207
+ if pretending
208
+ true
209
+ else
210
+ `git remote`.split("\n").include?(remote_name)
167
211
  end
168
212
  end
169
213
 
170
- def ensure_production_app_is_present
171
- unless string_present?(Kumade.production_app)
172
- raise "Cannot deploy: Kumade.production_app is not present"
214
+ def initialize_rake
215
+ if Rake.application.tasks.empty?
216
+ Rake.application.options.rakelib = ['rakelib']
217
+ Rake.application.load_rakefile
173
218
  end
174
219
  end
175
220
  end