kumade 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +7 -0
- data/README.md +105 -46
- data/bin/kumade +5 -0
- data/features/kumade_executable.feature +35 -0
- data/features/step_definitions/dependency_steps.rb +3 -0
- data/features/step_definitions/git_steps.rb +9 -0
- data/features/support/env.rb +0 -3
- data/kumade.gemspec +1 -0
- data/lib/kumade.rb +10 -41
- data/lib/kumade/deployer.rb +107 -62
- data/lib/kumade/thor_task.rb +18 -0
- data/lib/kumade/version.rb +2 -2
- data/spec/kumade/deployer_spec.rb +586 -0
- data/spec/kumade/thor_task_spec.rb +14 -0
- data/spec/kumade_spec.rb +16 -64
- data/spec/spec_helper.rb +13 -0
- metadata +84 -123
- data/features/deploying.feature +0 -23
- data/features/loading_tasks.feature +0 -36
- data/features/namespaced_deploy.feature +0 -23
- data/features/step_definitions/stub_steps.rb +0 -10
- data/features/step_definitions/task_steps.rb +0 -27
- data/features/support/insert_into_rakefile.rb +0 -15
- data/features/support/require_kumade.rb +0 -18
- data/lib/kumade/tasks/deploy.rake +0 -12
- data/lib/kumade/tasks/namespaced_deploy.rake +0 -18
- data/spec/deployer_spec.rb +0 -564
data/.travis.yml
ADDED
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
|
-
|
12
|
-
|
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
|
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
|
-
|
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
|
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
|
data/bin/kumade
ADDED
@@ -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,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
|
data/features/support/env.rb
CHANGED
data/kumade.gemspec
CHANGED
data/lib/kumade.rb
CHANGED
@@ -1,45 +1,14 @@
|
|
1
1
|
require 'kumade/deployer'
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
data/lib/kumade/deployer.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
9
|
-
|
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
|
20
|
-
|
21
|
-
|
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(
|
30
|
-
heroku_migrate(
|
24
|
+
git_force_push(string_environment)
|
25
|
+
heroku_migrate(string_environment)
|
31
26
|
end
|
32
27
|
|
33
28
|
def git_push(remote)
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
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 =
|
47
|
-
Kumade.staging_app
|
48
|
-
elsif environment == :production
|
49
|
-
Kumade.production_app
|
50
|
-
end
|
45
|
+
app = Kumade.app_for(environment)
|
51
46
|
|
52
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
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
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
144
|
+
`rake -s -P | grep 'rake default'`.strip.size > 0
|
131
145
|
end
|
132
146
|
|
133
147
|
def rake_succeeded?
|
134
148
|
begin
|
135
|
-
|
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
|
153
|
-
|
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
|
-
|
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
|
165
|
-
|
166
|
-
|
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
|
171
|
-
|
172
|
-
|
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
|