heroploy 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/README.md +7 -6
- data/heroploy.gemspec +1 -0
- data/lib/heroploy/commands/checks.rb +31 -0
- data/lib/heroploy/commands/git.rb +46 -0
- data/lib/heroploy/commands/heroku.rb +19 -0
- data/lib/heroploy/{shell.rb → commands/shell.rb} +0 -0
- data/lib/heroploy/config/checks_config.rb +16 -0
- data/lib/heroploy/config/deploy_config.rb +24 -0
- data/lib/heroploy/config/env_config.rb +21 -0
- data/lib/heroploy/tasks/check_task_lib.rb +56 -0
- data/lib/heroploy/tasks/deploy_task_lib.rb +25 -0
- data/lib/heroploy/tasks/env_task_lib.rb +48 -0
- data/lib/heroploy/tasks/tasks.rake +6 -0
- data/lib/heroploy/version.rb +1 -1
- data/lib/heroploy.rb +3 -1
- data/lib/railtie.rb +1 -1
- data/spec/factories.rb +47 -0
- data/spec/lib/heroploy/commands/checks_spec.rb +98 -0
- data/spec/lib/heroploy/{git_commands_spec.rb → commands/git_spec.rb} +2 -2
- data/spec/lib/heroploy/{heroku_commands_spec.rb → commands/heroku_spec.rb} +2 -2
- data/spec/lib/heroploy/{shell_spec.rb → commands/shell_spec.rb} +0 -0
- data/spec/lib/heroploy/tasks/check_all_spec.rb +50 -0
- data/spec/lib/heroploy/tasks/check_branch_spec.rb +10 -0
- data/spec/lib/heroploy/tasks/check_pushed_spec.rb +10 -0
- data/spec/lib/heroploy/tasks/check_remote_spec.rb +9 -0
- data/spec/lib/heroploy/tasks/check_staged_spec.rb +19 -0
- data/spec/spec_helper.rb +10 -2
- data/spec/support/helpers/deploy_config_helper.rb +0 -0
- data/spec/support/shared_contexts/rake.rb +47 -0
- metadata +51 -13
- data/lib/heroploy/deploy_config.rb +0 -39
- data/lib/heroploy/git_commands.rb +0 -44
- data/lib/heroploy/heroku_commands.rb +0 -17
- data/lib/heroploy/tasks.rb +0 -87
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2596af30153567d6c7fff7dd776b944029034b6
|
4
|
+
data.tar.gz: a1ff270a3b794fb801bc226331c5277059a9b918
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35de3c056e9eff9e0f0cb2a463f20bdce9265ba9247086939e8a0fc1a4f9533d6244aa67cd55c04b342c538f5b65e92bafe1343dca99dfbeac12d1aff9613804
|
7
|
+
data.tar.gz: b369f61be1d4bd0f4caa0d5524125494be8204a2a9b504baf19db95c7a717efe080bc2f47fd3bdf5a3947198109827e3823f1a3c8a534bf07fde0c685b3de390
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Heroploy
|
2
2
|
|
3
3
|
[](https://travis-ci.org/jbrunton/heroploy)
|
4
|
+
[](https://gemnasium.com/jbrunton/heroploy)
|
4
5
|
[](https://codeclimate.com/github/jbrunton/heroploy)
|
5
6
|
|
6
7
|
A few helpful rake tasks to manage deploying Rails apps to development, staging and production Heroku servers.
|
@@ -21,23 +22,23 @@ Or install it yourself as:
|
|
21
22
|
|
22
23
|
## Usage
|
23
24
|
|
24
|
-
Add a .
|
25
|
+
Add a ```heroploy.yml``` file to your application's config directory which describes the Heroku apps you will deploy to, and the checks you would like when deploying to each.
|
25
26
|
|
26
27
|
For example:
|
27
28
|
|
28
29
|
```yaml
|
29
|
-
|
30
|
+
environments:
|
30
31
|
development:
|
31
|
-
heroku: my-app
|
32
|
+
heroku: my-development-app
|
32
33
|
|
33
34
|
staging:
|
34
|
-
heroku: my-app
|
35
|
+
heroku: my-staging-app
|
35
36
|
checks:
|
36
37
|
pushed: true
|
37
38
|
branch: master
|
38
39
|
|
39
40
|
production:
|
40
|
-
heroku: my-app
|
41
|
+
heroku: my-production-app
|
41
42
|
tag: 'RELEASE_%Y%m%dT%H%M%S%z'
|
42
43
|
checks:
|
43
44
|
pushed: true
|
@@ -46,7 +47,7 @@ apps:
|
|
46
47
|
```
|
47
48
|
|
48
49
|
This file:
|
49
|
-
* Describes ```development```, ```staging``` and ```production``` deployment rules for three Heroku apps (named ```my-app
|
50
|
+
* Describes ```development```, ```staging``` and ```production``` deployment rules for three Heroku apps (named ```my-development-app```, ```my-staging-app``` and ```my-production-app``` on Heroku).
|
50
51
|
* Allows any branch to be pushed directly to ```development```.
|
51
52
|
* Only allows ```master``` to be pushed to ```staging```, and requires all changes to have first been pushed to ```origin```.
|
52
53
|
* Only allows deployment to ```production``` if the changes have first been staged on ```staging```.
|
data/heroploy.gemspec
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Commands
|
2
|
+
module Checks
|
3
|
+
def check_remote(remote)
|
4
|
+
unless git_remote_exists?(remote)
|
5
|
+
raise "Could not find remote '#{remote}'"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def check_pushed(branch_name)
|
10
|
+
unless git_remote_has_branch?('origin', branch_name)
|
11
|
+
raise "Branch #{branch_name} doesn't exist in origin"
|
12
|
+
end
|
13
|
+
|
14
|
+
if git_remote_behind?('origin', branch_name) then
|
15
|
+
raise "Branch #{branch_name} is behind origin/#{branch_name}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def check_branch(branch_name, valid_branch, env_name)
|
20
|
+
unless branch_name == valid_branch
|
21
|
+
raise "Cannot deploy branch #{branch_name} to #{env_name}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def check_staged(remote, branch_name, env_name)
|
26
|
+
unless git_staged?(remote, branch_name)
|
27
|
+
raise "Changes not yet staged on #{env_name}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'heroploy/commands/shell'
|
2
|
+
|
3
|
+
module Commands
|
4
|
+
module Git
|
5
|
+
def git_fetch
|
6
|
+
Shell.exec "git fetch"
|
7
|
+
end
|
8
|
+
|
9
|
+
def current_branch
|
10
|
+
branch = Shell.eval "git rev-parse --abbrev-ref HEAD"
|
11
|
+
branch.strip
|
12
|
+
end
|
13
|
+
|
14
|
+
def git_push_to_master(remote, local_branch)
|
15
|
+
if ENV['force'] == 'true' then opts = "--force " end
|
16
|
+
Shell.exec "git push #{opts}#{remote} #{local_branch}:master"
|
17
|
+
end
|
18
|
+
|
19
|
+
def git_remote_exists?(name)
|
20
|
+
remotes = Shell.eval("git remote").strip.split(/\s+/)
|
21
|
+
remotes.include?(name)
|
22
|
+
end
|
23
|
+
|
24
|
+
def git_remote_has_branch?(remote, branch_name)
|
25
|
+
branches = Shell.eval("git branch -r").strip.split(/\s+/)
|
26
|
+
branches.include?("#{remote}/#{branch_name}")
|
27
|
+
end
|
28
|
+
|
29
|
+
def git_remote_behind?(remote, remote_branch_name, local_branch_name = nil)
|
30
|
+
if local_branch_name.nil? then local_branch_name = remote_branch_name end
|
31
|
+
!Shell.eval("git log #{remote}/#{remote_branch_name}..#{local_branch_name}").empty?
|
32
|
+
end
|
33
|
+
|
34
|
+
def git_staged?(remote, local_branch)
|
35
|
+
!git_remote_behind?(remote, 'master', local_branch)
|
36
|
+
end
|
37
|
+
|
38
|
+
def git_tag(tag, message)
|
39
|
+
Shell.exec("git tag -a #{tag} -m \"#{message}\"")
|
40
|
+
end
|
41
|
+
|
42
|
+
def git_push_tag(tag)
|
43
|
+
Shell.exec("git push origin #{tag}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'heroploy/commands/shell'
|
2
|
+
|
3
|
+
module Commands
|
4
|
+
module Heroku
|
5
|
+
def heroku_exec(cmd, app_name)
|
6
|
+
Shell.exec "heroku #{cmd} --app #{app_name}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def heroku_run(cmd, app_name)
|
10
|
+
heroku_exec("run #{cmd}", app_name)
|
11
|
+
end
|
12
|
+
|
13
|
+
def heroku_migrate(app_name)
|
14
|
+
Bundler.with_clean_env do
|
15
|
+
heroku_run("rake db:migrate", app_name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
File without changes
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class ChecksConfig
|
2
|
+
attr_accessor :pushed
|
3
|
+
attr_accessor :branch
|
4
|
+
attr_accessor :staged
|
5
|
+
|
6
|
+
def self.parse(attrs)
|
7
|
+
config = ChecksConfig.new
|
8
|
+
|
9
|
+
attrs ||= {}
|
10
|
+
config.pushed = attrs['pushed']
|
11
|
+
config.branch = attrs['branch']
|
12
|
+
config.staged = attrs['staged'] == true ? 'staging' : attrs['staged']
|
13
|
+
|
14
|
+
config
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'heroploy/config/env_config'
|
2
|
+
|
3
|
+
class DeployConfig
|
4
|
+
attr_accessor :environments
|
5
|
+
|
6
|
+
def [](env_name)
|
7
|
+
environments.select{ |env_config| env_config.name == env_name}.first
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.parse(attrs)
|
11
|
+
config = DeployConfig.new
|
12
|
+
|
13
|
+
config.environments = []
|
14
|
+
attrs['environments'].each do |name, attrs|
|
15
|
+
config.environments << EnvConfig.parse(name, attrs)
|
16
|
+
end
|
17
|
+
|
18
|
+
config
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.load
|
22
|
+
DeployConfig.parse(YAML::load(File.open('config/heroploy.yml')))
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'heroploy/config/checks_config'
|
2
|
+
|
3
|
+
class EnvConfig
|
4
|
+
attr_accessor :name
|
5
|
+
attr_accessor :remote
|
6
|
+
attr_accessor :heroku
|
7
|
+
attr_accessor :tag
|
8
|
+
attr_accessor :checks
|
9
|
+
|
10
|
+
def self.parse(name, attrs)
|
11
|
+
config = EnvConfig.new
|
12
|
+
|
13
|
+
config.name = name
|
14
|
+
config.remote = attrs['remote'] || name
|
15
|
+
config.heroku = attrs['heroku']
|
16
|
+
config.tag = attrs['tag']
|
17
|
+
config.checks = ChecksConfig.parse(attrs['checks'])
|
18
|
+
|
19
|
+
config
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rake/tasklib'
|
2
|
+
|
3
|
+
require 'heroploy/commands/heroku'
|
4
|
+
require 'heroploy/commands/git'
|
5
|
+
require 'heroploy/commands/checks'
|
6
|
+
|
7
|
+
require 'heroploy/config/deploy_config'
|
8
|
+
|
9
|
+
module Heroploy
|
10
|
+
class CheckTaskLib < ::Rake::TaskLib
|
11
|
+
include ::Rake::DSL if defined?(::Rake::DSL)
|
12
|
+
|
13
|
+
include Commands::Git
|
14
|
+
include Commands::Heroku
|
15
|
+
include Commands::Checks
|
16
|
+
|
17
|
+
def initialize(deploy_config, env_config)
|
18
|
+
desc "check remote exists for #{env_config.name}"
|
19
|
+
task :remote do
|
20
|
+
remote = env_config.remote
|
21
|
+
check_remote(remote)
|
22
|
+
end
|
23
|
+
|
24
|
+
all_tasks = [:remote]
|
25
|
+
|
26
|
+
if env_config.checks.pushed then
|
27
|
+
desc "check changes have been pushed to origin"
|
28
|
+
task :pushed do
|
29
|
+
check_pushed(current_branch)
|
30
|
+
end
|
31
|
+
all_tasks << :pushed
|
32
|
+
end
|
33
|
+
|
34
|
+
if env_config.checks.branch then
|
35
|
+
desc "check we can deploy to #{env_config.name} from the current branch"
|
36
|
+
task :branch do
|
37
|
+
valid_branch = env_config.checks.branch
|
38
|
+
check_branch(current_branch, valid_branch, env_config.name)
|
39
|
+
end
|
40
|
+
all_tasks << :branch
|
41
|
+
end
|
42
|
+
|
43
|
+
if env_config.checks.staged then
|
44
|
+
desc "check the changes have already been staged"
|
45
|
+
task :staged do
|
46
|
+
staging_env_config = deploy_config[env_config.checks.staged]
|
47
|
+
check_staged(staging_env_config.remote, current_branch, staging_env_config.name)
|
48
|
+
end
|
49
|
+
all_tasks << :staged
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "do all the checks for #{env_config.name}"
|
53
|
+
task :all => all_tasks
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rake/tasklib'
|
2
|
+
|
3
|
+
require 'heroploy/commands/heroku'
|
4
|
+
require 'heroploy/commands/git'
|
5
|
+
require 'heroploy/commands/checks'
|
6
|
+
|
7
|
+
require 'heroploy/config/deploy_config'
|
8
|
+
require 'heroploy/tasks/env_task_lib'
|
9
|
+
|
10
|
+
module Heroploy
|
11
|
+
class DeployTaskLib < ::Rake::TaskLib
|
12
|
+
include ::Rake::DSL if defined?(::Rake::DSL)
|
13
|
+
|
14
|
+
def initialize(deploy_config)
|
15
|
+
desc 'do a git fetch'
|
16
|
+
task :fetch do
|
17
|
+
git_fetch
|
18
|
+
end
|
19
|
+
|
20
|
+
deploy_config.environments.each do |env_config|
|
21
|
+
EnvTaskLib.new(deploy_config, env_config)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rake/tasklib'
|
2
|
+
|
3
|
+
require 'heroploy/commands/heroku'
|
4
|
+
require 'heroploy/commands/git'
|
5
|
+
require 'heroploy/commands/checks'
|
6
|
+
|
7
|
+
require 'heroploy/config/deploy_config'
|
8
|
+
require 'heroploy/tasks/check_task_lib'
|
9
|
+
|
10
|
+
module Heroploy
|
11
|
+
class EnvTaskLib < ::Rake::TaskLib
|
12
|
+
include ::Rake::DSL if defined?(::Rake::DSL)
|
13
|
+
|
14
|
+
include Commands::Git
|
15
|
+
include Commands::Heroku
|
16
|
+
include Commands::Checks
|
17
|
+
|
18
|
+
def initialize(deploy_config, env_config)
|
19
|
+
namespace env_config.name do
|
20
|
+
namespace :check do
|
21
|
+
CheckTaskLib.new(deploy_config, env_config)
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "push the current branch to master on #{env_config.name}"
|
25
|
+
task :push do
|
26
|
+
git_push_to_master(env_config.remote, current_branch)
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "run database migrations on #{env_config.name}"
|
30
|
+
task :migrate do
|
31
|
+
heroku_migrate(env_config.heroku)
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "tag the deployment to #{env_config.name}"
|
35
|
+
task :tag do
|
36
|
+
if env_config.tag then
|
37
|
+
tag = DateTime.now.strftime(env_config.tag)
|
38
|
+
git_tag(tag, "Deployed #{current_branch} to #{env_config.name}")
|
39
|
+
git_push_tag(tag)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
desc "deploy to #{env_config.name}"
|
44
|
+
task :deploy => ['check:all', :push, :migrate, :tag]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/heroploy/version.rb
CHANGED
data/lib/heroploy.rb
CHANGED
data/lib/railtie.rb
CHANGED
data/spec/factories.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
FactoryGirl.define do
|
2
|
+
factory :checks_config do
|
3
|
+
pushed false
|
4
|
+
staged false
|
5
|
+
branch nil
|
6
|
+
|
7
|
+
trait :development do
|
8
|
+
pushed false
|
9
|
+
staged false
|
10
|
+
branch nil
|
11
|
+
end
|
12
|
+
|
13
|
+
trait :staging do
|
14
|
+
pushed true
|
15
|
+
staged false
|
16
|
+
branch 'master'
|
17
|
+
end
|
18
|
+
|
19
|
+
trait :production do
|
20
|
+
pushed true
|
21
|
+
staged true
|
22
|
+
branch 'master'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
factory :env_config do
|
27
|
+
checks { build(:checks_config) }
|
28
|
+
|
29
|
+
[:development, :staging, :production].each do |t|
|
30
|
+
trait t do
|
31
|
+
name t.to_s
|
32
|
+
remote t.to_s
|
33
|
+
checks { build(:checks_config, t) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
factory :deploy_config do
|
39
|
+
environments {
|
40
|
+
[
|
41
|
+
build(:env_config, :development),
|
42
|
+
build(:env_config, :staging),
|
43
|
+
build(:env_config, :production)
|
44
|
+
]
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Commands::Checks do
|
4
|
+
before(:each) do
|
5
|
+
stub_shell
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:commands) { Object.new.extend(Commands::Checks) }
|
9
|
+
|
10
|
+
describe "#check_remote" do
|
11
|
+
it "invokes git_remote_exists? to check the remote" do
|
12
|
+
expect(commands).to receive(:git_remote_exists?).with("my-remote").and_return(true)
|
13
|
+
commands.check_remote("my-remote")
|
14
|
+
end
|
15
|
+
|
16
|
+
context "if the remote exists" do
|
17
|
+
before { commands.stub(:git_remote_exists?).with("my-remote").and_return(true) }
|
18
|
+
|
19
|
+
it "executes successfully" do
|
20
|
+
commands.check_remote("my-remote")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context "if the remote doesn't exist" do
|
25
|
+
before { commands.stub(:git_remote_exists?).with("my-remote").and_return(false) }
|
26
|
+
|
27
|
+
it "raises an error" do
|
28
|
+
expect{
|
29
|
+
commands.check_remote("my-remote")
|
30
|
+
}.to raise_error("Could not find remote 'my-remote'")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#check_pushed" do
|
36
|
+
context "if the remote branch exists and is up to date" do
|
37
|
+
it "executes successfully" do
|
38
|
+
expect(commands).to receive(:git_remote_has_branch?).with("origin", "my-branch").and_return(true)
|
39
|
+
expect(commands).to receive(:git_remote_behind?).with("origin", "my-branch").and_return(false)
|
40
|
+
commands.check_pushed("my-branch")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "if the remote branch doesn't exist" do
|
45
|
+
it "raises an error" do
|
46
|
+
commands.stub(:git_remote_has_branch?).with("origin", "my-branch").and_return(false)
|
47
|
+
commands.stub(:git_remote_behind?).with("origin", "my-branch").and_return(false)
|
48
|
+
expect{
|
49
|
+
commands.check_pushed("my-branch")
|
50
|
+
}.to raise_error("Branch my-branch doesn't exist in origin")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "if the remote branch is behind" do
|
55
|
+
it "raises an error" do
|
56
|
+
commands.stub(:git_remote_has_branch?).with("origin", "my-branch").and_return(true)
|
57
|
+
commands.stub(:git_remote_behind?).with("origin", "my-branch").and_return(true)
|
58
|
+
expect{
|
59
|
+
commands.check_pushed("my-branch")
|
60
|
+
}.to raise_error("Branch my-branch is behind origin/my-branch")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#check_branch" do
|
66
|
+
context "if the branch is valid" do
|
67
|
+
it "executes successfully" do
|
68
|
+
commands.check_branch("my-branch", "my-branch", "production")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "if the branch is invalid" do
|
73
|
+
it "raises an error" do
|
74
|
+
expect{
|
75
|
+
commands.check_branch("my-branch", "my-valid-branch", "production")
|
76
|
+
}.to raise_error("Cannot deploy branch my-branch to production")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "#check_staged" do
|
82
|
+
context "if the branch is staged" do
|
83
|
+
it "executes successfully" do
|
84
|
+
expect(commands).to receive(:git_staged?).with("my-remote", "my-branch").and_return(true)
|
85
|
+
commands.check_staged("my-remote", "my-branch", "staging")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "if the branch isn't yet staged" do
|
90
|
+
it "raises an error" do
|
91
|
+
expect{
|
92
|
+
commands.stub(:git_staged?).with("my-remote", "my-branch").and_return(false)
|
93
|
+
commands.check_staged("my-remote", "my-branch", "staging")
|
94
|
+
}.to raise_error("Changes not yet staged on staging")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe Commands::Git do
|
4
4
|
before(:each) do
|
5
5
|
stub_shell
|
6
6
|
end
|
7
7
|
|
8
|
-
let(:commands) { Object.new.extend(
|
8
|
+
let(:commands) { Object.new.extend(Commands::Git) }
|
9
9
|
|
10
10
|
context "#git_fetch" do
|
11
11
|
it "executes a git fetch" do
|
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe Commands::Heroku do
|
4
4
|
before(:each) do
|
5
5
|
stub_shell
|
6
6
|
end
|
7
7
|
|
8
|
-
let(:commands) { Object.new.extend(
|
8
|
+
let(:commands) { Object.new.extend(Commands::Heroku) }
|
9
9
|
|
10
10
|
context "#heroku_exec" do
|
11
11
|
it "executes the heroku command for the given app" do
|
File without changes
|
@@ -0,0 +1,50 @@
|
|
1
|
+
describe "check:all" do
|
2
|
+
let(:environment) { build(:env_config, :production) }
|
3
|
+
|
4
|
+
context "in all cases" do
|
5
|
+
include_context "rake"
|
6
|
+
its(:prerequisites) { should include('remote') }
|
7
|
+
end
|
8
|
+
|
9
|
+
context "if checks.pushed is set" do
|
10
|
+
before { environment.checks.pushed = true }
|
11
|
+
include_context "rake"
|
12
|
+
|
13
|
+
its(:prerequisites) { should include('pushed') }
|
14
|
+
end
|
15
|
+
|
16
|
+
context "if checks.pushed is not set" do
|
17
|
+
before { environment.checks.pushed = false }
|
18
|
+
include_context "rake"
|
19
|
+
|
20
|
+
its(:prerequisites) { should_not include('pushed') }
|
21
|
+
end
|
22
|
+
|
23
|
+
context "if checks.staged is set" do
|
24
|
+
before { environment.checks.staged = true }
|
25
|
+
include_context "rake"
|
26
|
+
|
27
|
+
its(:prerequisites) { should include('staged') }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "if checks.staged is not set" do
|
31
|
+
before { environment.checks.staged = false }
|
32
|
+
include_context "rake"
|
33
|
+
|
34
|
+
its(:prerequisites) { should_not include('staged') }
|
35
|
+
end
|
36
|
+
|
37
|
+
context "if checks.branch is set" do
|
38
|
+
before { environment.checks.branch = 'master' }
|
39
|
+
include_context "rake"
|
40
|
+
|
41
|
+
its(:prerequisites) { should include('branch') }
|
42
|
+
end
|
43
|
+
|
44
|
+
context "if checks.staged is not set" do
|
45
|
+
before { environment.checks.branch = nil }
|
46
|
+
include_context "rake"
|
47
|
+
|
48
|
+
its(:prerequisites) { should_not include('branch') }
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
describe "check:branch" do
|
2
|
+
let(:environment) { build(:env_config, :production) }
|
3
|
+
include_context "rake"
|
4
|
+
|
5
|
+
it "invokes :check_branch" do
|
6
|
+
Heroploy::CheckTaskLib.any_instance.stub(:current_branch).and_return("my-branch")
|
7
|
+
expect_any_instance_of(Heroploy::CheckTaskLib).to receive(:check_branch).with("my-branch", "master", "production")
|
8
|
+
task.invoke
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
describe "check:pushed" do
|
2
|
+
let(:environment) { build(:env_config, :production) }
|
3
|
+
include_context "rake"
|
4
|
+
|
5
|
+
it "invokes :check_pushed" do
|
6
|
+
Heroploy::CheckTaskLib.any_instance.stub(:current_branch).and_return("my-branch")
|
7
|
+
expect_any_instance_of(Heroploy::CheckTaskLib).to receive(:check_pushed).with("my-branch")
|
8
|
+
task.invoke
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
describe "check:staged" do
|
2
|
+
let(:production) { build(:env_config, :production) }
|
3
|
+
let(:staging) { build(:env_config, :staging, remote: "my-staging-remote") }
|
4
|
+
let(:environments) { [production, staging] }
|
5
|
+
|
6
|
+
include_context "rake"
|
7
|
+
|
8
|
+
context "if checks.staged is set" do
|
9
|
+
before { production.checks.staged = 'staging' }
|
10
|
+
|
11
|
+
it "invokes :check_pushed" do
|
12
|
+
Heroploy::CheckTaskLib.any_instance.stub(:current_branch).and_return("my-branch")
|
13
|
+
|
14
|
+
expect_any_instance_of(Heroploy::CheckTaskLib).to receive(:check_staged).with("my-staging-remote", "my-branch", "staging")
|
15
|
+
|
16
|
+
task.invoke
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,17 @@
|
|
1
|
-
require '
|
2
|
-
|
1
|
+
require 'factory_girl'
|
2
|
+
|
3
|
+
require 'heroploy'
|
4
|
+
require 'heroploy/tasks/deploy_task_lib'
|
5
|
+
|
3
6
|
require 'support/shell_support'
|
7
|
+
require 'support/shared_contexts/rake'
|
8
|
+
|
9
|
+
FactoryGirl.find_definitions
|
4
10
|
|
5
11
|
RSpec.configure do |config|
|
6
12
|
config.color_enabled = true
|
7
13
|
|
14
|
+
config.include FactoryGirl::Syntax::Methods
|
15
|
+
|
8
16
|
config.include ShellSupport
|
9
17
|
end
|
File without changes
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# references:
|
2
|
+
# http://robots.thoughtbot.com/test-rake-tasks-like-a-boss
|
3
|
+
# http://www.e-tobi.net/blog/2008/10/04/
|
4
|
+
|
5
|
+
require "rake"
|
6
|
+
|
7
|
+
shared_context "rake" do
|
8
|
+
let(:task_name) {
|
9
|
+
if defined?(environment)
|
10
|
+
env_name = environment.name
|
11
|
+
elsif defined?(environments)
|
12
|
+
env_name = environments[0].name
|
13
|
+
else
|
14
|
+
env_name = deploy_config.environments[0].name
|
15
|
+
end
|
16
|
+
|
17
|
+
"#{env_name}:#{self.class.top_level_description}"
|
18
|
+
}
|
19
|
+
|
20
|
+
let(:task) { Rake::Task[task_name] }
|
21
|
+
|
22
|
+
subject { task }
|
23
|
+
|
24
|
+
before(:each) do
|
25
|
+
Rake::Task.clear
|
26
|
+
|
27
|
+
unless defined?(deploy_config)
|
28
|
+
if defined?(environment)
|
29
|
+
deploy_config = build(:deploy_config, environments: [environment])
|
30
|
+
elsif defined?(environments)
|
31
|
+
deploy_config = build(:deploy_config, environments: environments)
|
32
|
+
else
|
33
|
+
deploy_config = build(:deploy_config)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Heroploy::DeployTaskLib.new(deploy_config)
|
38
|
+
|
39
|
+
Rake::Task.tasks.each do |task|
|
40
|
+
if task.name != task_name
|
41
|
+
task.stub(:execute)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
stub_shell
|
46
|
+
end
|
47
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heroploy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Brunton
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01-
|
11
|
+
date: 2014-01-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: factory_girl
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description: Helpful rake tasks to manage deploying to development, staging and production
|
56
70
|
Heroku servers
|
57
71
|
email:
|
@@ -68,17 +82,32 @@ files:
|
|
68
82
|
- Rakefile
|
69
83
|
- heroploy.gemspec
|
70
84
|
- lib/heroploy.rb
|
71
|
-
- lib/heroploy/
|
72
|
-
- lib/heroploy/
|
73
|
-
- lib/heroploy/
|
74
|
-
- lib/heroploy/shell.rb
|
75
|
-
- lib/heroploy/
|
85
|
+
- lib/heroploy/commands/checks.rb
|
86
|
+
- lib/heroploy/commands/git.rb
|
87
|
+
- lib/heroploy/commands/heroku.rb
|
88
|
+
- lib/heroploy/commands/shell.rb
|
89
|
+
- lib/heroploy/config/checks_config.rb
|
90
|
+
- lib/heroploy/config/deploy_config.rb
|
91
|
+
- lib/heroploy/config/env_config.rb
|
92
|
+
- lib/heroploy/tasks/check_task_lib.rb
|
93
|
+
- lib/heroploy/tasks/deploy_task_lib.rb
|
94
|
+
- lib/heroploy/tasks/env_task_lib.rb
|
95
|
+
- lib/heroploy/tasks/tasks.rake
|
76
96
|
- lib/heroploy/version.rb
|
77
97
|
- lib/railtie.rb
|
78
|
-
- spec/
|
79
|
-
- spec/lib/heroploy/
|
80
|
-
- spec/lib/heroploy/
|
98
|
+
- spec/factories.rb
|
99
|
+
- spec/lib/heroploy/commands/checks_spec.rb
|
100
|
+
- spec/lib/heroploy/commands/git_spec.rb
|
101
|
+
- spec/lib/heroploy/commands/heroku_spec.rb
|
102
|
+
- spec/lib/heroploy/commands/shell_spec.rb
|
103
|
+
- spec/lib/heroploy/tasks/check_all_spec.rb
|
104
|
+
- spec/lib/heroploy/tasks/check_branch_spec.rb
|
105
|
+
- spec/lib/heroploy/tasks/check_pushed_spec.rb
|
106
|
+
- spec/lib/heroploy/tasks/check_remote_spec.rb
|
107
|
+
- spec/lib/heroploy/tasks/check_staged_spec.rb
|
81
108
|
- spec/spec_helper.rb
|
109
|
+
- spec/support/helpers/deploy_config_helper.rb
|
110
|
+
- spec/support/shared_contexts/rake.rb
|
82
111
|
- spec/support/shell_support.rb
|
83
112
|
homepage: ''
|
84
113
|
licenses:
|
@@ -105,8 +134,17 @@ signing_key:
|
|
105
134
|
specification_version: 4
|
106
135
|
summary: Helpful rake tasks for deploying to Heroku
|
107
136
|
test_files:
|
108
|
-
- spec/
|
109
|
-
- spec/lib/heroploy/
|
110
|
-
- spec/lib/heroploy/
|
137
|
+
- spec/factories.rb
|
138
|
+
- spec/lib/heroploy/commands/checks_spec.rb
|
139
|
+
- spec/lib/heroploy/commands/git_spec.rb
|
140
|
+
- spec/lib/heroploy/commands/heroku_spec.rb
|
141
|
+
- spec/lib/heroploy/commands/shell_spec.rb
|
142
|
+
- spec/lib/heroploy/tasks/check_all_spec.rb
|
143
|
+
- spec/lib/heroploy/tasks/check_branch_spec.rb
|
144
|
+
- spec/lib/heroploy/tasks/check_pushed_spec.rb
|
145
|
+
- spec/lib/heroploy/tasks/check_remote_spec.rb
|
146
|
+
- spec/lib/heroploy/tasks/check_staged_spec.rb
|
111
147
|
- spec/spec_helper.rb
|
148
|
+
- spec/support/helpers/deploy_config_helper.rb
|
149
|
+
- spec/support/shared_contexts/rake.rb
|
112
150
|
- spec/support/shell_support.rb
|
@@ -1,39 +0,0 @@
|
|
1
|
-
class ChecksConfig
|
2
|
-
attr_reader :pushed
|
3
|
-
attr_reader :branch
|
4
|
-
attr_reader :staged
|
5
|
-
|
6
|
-
def initialize(attrs)
|
7
|
-
attrs ||= {}
|
8
|
-
@pushed = attrs['pushed']
|
9
|
-
@branch = attrs['branch']
|
10
|
-
@staged = attrs['staged']
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class AppConfig
|
15
|
-
attr_reader :name
|
16
|
-
attr_reader :remote
|
17
|
-
attr_reader :heroku
|
18
|
-
attr_reader :tag
|
19
|
-
attr_reader :checks
|
20
|
-
|
21
|
-
def initialize(name, attrs)
|
22
|
-
@name = name
|
23
|
-
@remote = attrs['remote'] || name
|
24
|
-
@heroku = attrs['heroku']
|
25
|
-
@tag = attrs['tag']
|
26
|
-
@checks = ChecksConfig.new(attrs['checks'])
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
class DeployConfig
|
31
|
-
attr_reader :apps
|
32
|
-
|
33
|
-
def initialize(file_name)
|
34
|
-
@apps = YAML::load(File.open(file_name))['apps']
|
35
|
-
@apps.each do |app_name, attrs|
|
36
|
-
@apps[app_name] = AppConfig.new(app_name, attrs)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
@@ -1,44 +0,0 @@
|
|
1
|
-
require 'heroploy/shell'
|
2
|
-
|
3
|
-
module GitCommands
|
4
|
-
def git_fetch
|
5
|
-
Shell.exec "git fetch"
|
6
|
-
end
|
7
|
-
|
8
|
-
def current_branch
|
9
|
-
branch = Shell.eval "git rev-parse --abbrev-ref HEAD"
|
10
|
-
branch.strip
|
11
|
-
end
|
12
|
-
|
13
|
-
def git_push_to_master(remote, local_branch)
|
14
|
-
if ENV['force'] == 'true' then opts = "--force " end
|
15
|
-
Shell.exec "git push #{opts}#{remote} #{local_branch}:master"
|
16
|
-
end
|
17
|
-
|
18
|
-
def git_remote_exists?(name)
|
19
|
-
remotes = Shell.eval("git remote").strip.split(/\s+/)
|
20
|
-
remotes.include?(name)
|
21
|
-
end
|
22
|
-
|
23
|
-
def git_remote_has_branch?(remote, branch_name)
|
24
|
-
branches = Shell.eval("git branch -r").strip.split(/\s+/)
|
25
|
-
branches.include?("#{remote}/#{branch_name}")
|
26
|
-
end
|
27
|
-
|
28
|
-
def git_remote_behind?(remote, remote_branch_name, local_branch_name = nil)
|
29
|
-
if local_branch_name.nil? then local_branch_name = remote_branch_name end
|
30
|
-
!Shell.eval("git log #{remote}/#{remote_branch_name}..#{local_branch_name}").empty?
|
31
|
-
end
|
32
|
-
|
33
|
-
def git_staged?(remote, local_branch)
|
34
|
-
!git_remote_behind?(remote, 'master', local_branch)
|
35
|
-
end
|
36
|
-
|
37
|
-
def git_tag(tag, message)
|
38
|
-
Shell.exec("git tag -a #{tag} -m \"#{message}\"")
|
39
|
-
end
|
40
|
-
|
41
|
-
def git_push_tag(tag)
|
42
|
-
Shell.exec("git push origin #{tag}")
|
43
|
-
end
|
44
|
-
end
|
@@ -1,17 +0,0 @@
|
|
1
|
-
require 'heroploy/shell'
|
2
|
-
|
3
|
-
module HerokuCommands
|
4
|
-
def heroku_exec(cmd, app_name)
|
5
|
-
Shell.exec "heroku #{cmd} --app #{app_name}"
|
6
|
-
end
|
7
|
-
|
8
|
-
def heroku_run(cmd, app_name)
|
9
|
-
heroku_exec("run #{cmd}", app_name)
|
10
|
-
end
|
11
|
-
|
12
|
-
def heroku_migrate(app_name)
|
13
|
-
Bundler.with_clean_env do
|
14
|
-
heroku_run("rake db:migrate", app_name)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/heroploy/tasks.rb
DELETED
@@ -1,87 +0,0 @@
|
|
1
|
-
require 'heroploy/git_commands'
|
2
|
-
require 'heroploy/heroku_commands'
|
3
|
-
require 'heroploy/deploy_config'
|
4
|
-
|
5
|
-
namespace :heroploy do
|
6
|
-
include GitCommands
|
7
|
-
include HerokuCommands
|
8
|
-
|
9
|
-
desc 'do a git fetch'
|
10
|
-
task :fetch do
|
11
|
-
git_fetch
|
12
|
-
end
|
13
|
-
|
14
|
-
deploy_config = DeployConfig.new('.heroploy.yml')
|
15
|
-
|
16
|
-
deploy_config.apps.each do |app_name, config|
|
17
|
-
namespace app_name do
|
18
|
-
namespace :check do
|
19
|
-
desc "check remote exists for #{app_name}"
|
20
|
-
task :remote do
|
21
|
-
remote = config.remote || app_name
|
22
|
-
unless git_remote_exists?(remote)
|
23
|
-
raise "Could not find remote '#{remote}'"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
desc "check changes have been pushed to origin"
|
28
|
-
task :pushed do
|
29
|
-
if config.checks.pushed then
|
30
|
-
branch_name = current_branch
|
31
|
-
unless git_remote_has_branch?('origin', branch_name)
|
32
|
-
raise "Branch #{branch_name} doesn't exist in origin"
|
33
|
-
end
|
34
|
-
|
35
|
-
if git_remote_behind?('origin', branch_name) then
|
36
|
-
raise "Branch #{branch_name} is behind origin/#{branch_name}"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
desc "check we can deploy to #{app_name} from the current branch"
|
42
|
-
task :branch do
|
43
|
-
if config.checks.branch then
|
44
|
-
unless current_branch == config.checks.branch
|
45
|
-
raise "Cannot deploy branch #{current_branch} to #{app_name}"
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
desc "check the changes have already been staged"
|
51
|
-
task :staged do
|
52
|
-
if config.checks.staged then
|
53
|
-
staging_app_name = config.checks.staged == true ? 'staging' : config.checks.staged
|
54
|
-
unless git_staged?(deploy_config.apps[staging_app_name].remote, current_branch)
|
55
|
-
raise "Changes not yet staged on #{app_name}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
desc "do all the checks for #{app_name}"
|
61
|
-
task :all => [:remote, :pushed, :branch, :staged]
|
62
|
-
end
|
63
|
-
|
64
|
-
desc "push the current branch to master on #{app_name}"
|
65
|
-
task :push do
|
66
|
-
git_push_to_master(config.remote, current_branch)
|
67
|
-
end
|
68
|
-
|
69
|
-
desc "run database migrations on #{app_name}"
|
70
|
-
task :migrate do
|
71
|
-
heroku_migrate(config.heroku)
|
72
|
-
end
|
73
|
-
|
74
|
-
desc "tag the deployment to #{app_name}"
|
75
|
-
task :tag do
|
76
|
-
if config.tag then
|
77
|
-
tag = DateTime.now.strftime(config.tag)
|
78
|
-
git_tag(tag, "Deployed #{current_branch} to #{app_name}")
|
79
|
-
git_push_tag(tag)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
desc "deploy to #{app_name}"
|
84
|
-
task :deploy => ['check:all', :push, :migrate, :tag]
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|