kumade 0.0.2 → 0.0.3
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/.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 [](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
|