heroploy 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -6
  3. data/heroploy.gemspec +1 -0
  4. data/lib/heroploy/commands/checks.rb +31 -0
  5. data/lib/heroploy/commands/git.rb +46 -0
  6. data/lib/heroploy/commands/heroku.rb +19 -0
  7. data/lib/heroploy/{shell.rb → commands/shell.rb} +0 -0
  8. data/lib/heroploy/config/checks_config.rb +16 -0
  9. data/lib/heroploy/config/deploy_config.rb +24 -0
  10. data/lib/heroploy/config/env_config.rb +21 -0
  11. data/lib/heroploy/tasks/check_task_lib.rb +56 -0
  12. data/lib/heroploy/tasks/deploy_task_lib.rb +25 -0
  13. data/lib/heroploy/tasks/env_task_lib.rb +48 -0
  14. data/lib/heroploy/tasks/tasks.rake +6 -0
  15. data/lib/heroploy/version.rb +1 -1
  16. data/lib/heroploy.rb +3 -1
  17. data/lib/railtie.rb +1 -1
  18. data/spec/factories.rb +47 -0
  19. data/spec/lib/heroploy/commands/checks_spec.rb +98 -0
  20. data/spec/lib/heroploy/{git_commands_spec.rb → commands/git_spec.rb} +2 -2
  21. data/spec/lib/heroploy/{heroku_commands_spec.rb → commands/heroku_spec.rb} +2 -2
  22. data/spec/lib/heroploy/{shell_spec.rb → commands/shell_spec.rb} +0 -0
  23. data/spec/lib/heroploy/tasks/check_all_spec.rb +50 -0
  24. data/spec/lib/heroploy/tasks/check_branch_spec.rb +10 -0
  25. data/spec/lib/heroploy/tasks/check_pushed_spec.rb +10 -0
  26. data/spec/lib/heroploy/tasks/check_remote_spec.rb +9 -0
  27. data/spec/lib/heroploy/tasks/check_staged_spec.rb +19 -0
  28. data/spec/spec_helper.rb +10 -2
  29. data/spec/support/helpers/deploy_config_helper.rb +0 -0
  30. data/spec/support/shared_contexts/rake.rb +47 -0
  31. metadata +51 -13
  32. data/lib/heroploy/deploy_config.rb +0 -39
  33. data/lib/heroploy/git_commands.rb +0 -44
  34. data/lib/heroploy/heroku_commands.rb +0 -17
  35. data/lib/heroploy/tasks.rb +0 -87
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1abe27a12facd2dc9f5efb27cfd5c959186ef25a
4
- data.tar.gz: 1e2995fb4cd6a6af47366cda82c7362adcd032c9
3
+ metadata.gz: c2596af30153567d6c7fff7dd776b944029034b6
4
+ data.tar.gz: a1ff270a3b794fb801bc226331c5277059a9b918
5
5
  SHA512:
6
- metadata.gz: 37e04d64e2abdb2730baeeb28233b25b4471e8b9c1e6000fc3b92e9bb70ede30eaa3f678871baaf8c1e7ac002c23127e21cf84a4efd0c9696ba0ceeb81657d81
7
- data.tar.gz: 2e8a442f4431e1b1fbf0b2667033905ac12e927066a309abbe480efe0875cfdc2697a92fb6ac118c10cd254f8dffeb16adb2a8116d43bc7aec69b6ec1fd2a893
6
+ metadata.gz: 35de3c056e9eff9e0f0cb2a463f20bdce9265ba9247086939e8a0fc1a4f9533d6244aa67cd55c04b342c538f5b65e92bafe1343dca99dfbeac12d1aff9613804
7
+ data.tar.gz: b369f61be1d4bd0f4caa0d5524125494be8204a2a9b504baf19db95c7a717efe080bc2f47fd3bdf5a3947198109827e3823f1a3c8a534bf07fde0c685b3de390
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Heroploy
2
2
 
3
3
  [![Build Status](https://travis-ci.org/jbrunton/heroploy.png?branch=master)](https://travis-ci.org/jbrunton/heroploy)
4
+ [![Dependency Status](https://gemnasium.com/jbrunton/heroploy.png)](https://gemnasium.com/jbrunton/heroploy)
4
5
  [![Code Climate](https://codeclimate.com/github/jbrunton/heroploy.png)](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 .heroploy file in your application's root directory which describes the Heroku apps you will deploy to, and the checks you would like when deploying to each.
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
- apps:
30
+ environments:
30
31
  development:
31
- heroku: my-app-development
32
+ heroku: my-development-app
32
33
 
33
34
  staging:
34
- heroku: my-app-staging
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-production
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-development```, ```my-app-staging``` and ```my-app-production``` on Heroku).
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
@@ -21,4 +21,5 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency "bundler", "~> 1.3"
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "factory_girl"
24
25
  end
@@ -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
@@ -0,0 +1,6 @@
1
+ require 'heroploy/tasks/deploy_task_lib'
2
+
3
+ namespace :heroploy do
4
+ deploy_config = DeployConfig.load
5
+ Heroploy::DeployTaskLib.new(deploy_config)
6
+ end
@@ -1,3 +1,3 @@
1
1
  module Heroploy
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/lib/heroploy.rb CHANGED
@@ -2,5 +2,7 @@ require "heroploy/version"
2
2
  require File.join(File.dirname(__FILE__), 'railtie.rb') if defined?(Rails) && Rails::VERSION::MAJOR >= 3
3
3
 
4
4
  module Heroploy
5
- # Your code goes here...
5
+ def self.root
6
+ Pathname.new(File.expand_path '../..', __FILE__)
7
+ end
6
8
  end
data/lib/railtie.rb CHANGED
@@ -3,7 +3,7 @@ require 'rails'
3
3
  module Heroploy
4
4
  class Railtie < Rails::Railtie
5
5
  rake_tasks do
6
- load 'heroploy/tasks.rb'
6
+ load 'heroploy/tasks/tasks.rake'
7
7
  end
8
8
  end
9
9
  end
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 GitCommands do
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(GitCommands) }
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 HerokuCommands do
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(HerokuCommands) }
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
@@ -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,9 @@
1
+ describe "check:remote" do
2
+ let(:environment) { build(:env_config, :production) }
3
+ include_context "rake"
4
+
5
+ it "invokes :check_remote" do
6
+ expect_any_instance_of(Heroploy::CheckTaskLib).to receive(:check_remote).with("production")
7
+ task.invoke
8
+ end
9
+ 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 'heroploy/heroku_commands'
2
- require 'heroploy/git_commands'
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.1
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-19 00:00:00.000000000 Z
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/deploy_config.rb
72
- - lib/heroploy/git_commands.rb
73
- - lib/heroploy/heroku_commands.rb
74
- - lib/heroploy/shell.rb
75
- - lib/heroploy/tasks.rb
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/lib/heroploy/git_commands_spec.rb
79
- - spec/lib/heroploy/heroku_commands_spec.rb
80
- - spec/lib/heroploy/shell_spec.rb
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/lib/heroploy/git_commands_spec.rb
109
- - spec/lib/heroploy/heroku_commands_spec.rb
110
- - spec/lib/heroploy/shell_spec.rb
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
@@ -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