heroku_san 1.3.0 → 2.0.rc.1

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/.gitignore CHANGED
@@ -3,6 +3,6 @@ doc
3
3
  pkg
4
4
  Thumbs.db
5
5
  tmp
6
-
6
+ .idea/**
7
7
  Gemfile.lock
8
8
  *.swp
data/.rvmrc CHANGED
@@ -1 +1 @@
1
- rvm @heroku_san --create
1
+ rvm gemset use heroku_san --create
data/README.rdoc CHANGED
@@ -121,7 +121,8 @@ Issue Tracker:: http://github.com/fastestforward/heroku_san/issues
121
121
  * Jon Wood (jon@blankpad.net)
122
122
  * Mat Schaffer (mat@schaffer.me)
123
123
  * Jonathan Hironaga (jonathan.hironaga@halogennetwork.com)
124
+ * Ken Mayer (ken@bitwrangler.com)
124
125
 
125
126
  == License
126
127
 
127
- License:: Copyright (c) 2009 Elijah Miller <mailto:elijah.miller@gmail.com>, released under the MIT license.
128
+ License:: Copyright (c) 2009-2012 Elijah Miller <mailto:elijah.miller@gmail.com>, released under the MIT license.
data/Rakefile CHANGED
@@ -1,5 +1,13 @@
1
1
  require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+
2
4
  Bundler::GemHelper.install_tasks
3
5
 
4
- desc 'Default: build gem.'
5
- task :default => :build
6
+ desc 'Default: run unit tests.'
7
+ task :default => :spec
8
+
9
+ desc "Run all specs"
10
+ RSpec::Core::RakeTask.new do |t|
11
+ t.pattern = 'spec/**/*_spec.rb'
12
+ t.rspec_opts = ["-c", "-f progress"]
13
+ end
@@ -0,0 +1 @@
1
+ Autotest.add_discovery { "rspec2" }
data/cucumber.yml ADDED
@@ -0,0 +1,2 @@
1
+ default: --tags ~@slow_process
2
+ remote: --tags @slow_process
@@ -0,0 +1,62 @@
1
+ # Support for auto_tagger (0.2.6) (http://github.com/zilkey/auto_tagger)
2
+
3
+ # Add the following to your ci build script
4
+ # this will create a tag if the build is successful
5
+ # rake ci-test-target autotag:create[ci]
6
+
7
+ # Add the following to your heroku.yml file:
8
+ # staging:
9
+ # tag: ci/*
10
+ #
11
+ # production:
12
+ # tag: staging/*
13
+
14
+ # Builds will be deployed in the following sequence:
15
+ # 1. ci passes, creates ci tag from HEAD
16
+ # 2. deploy latest ci tag to staging
17
+ # 3. deploy latest staging tag to production
18
+
19
+ STAGES = %w[ci staging production]
20
+
21
+ def create_and_push(stage)
22
+ auto_tag = AutoTagger::Base.new(stages: STAGES, stage: stage, verbose: true, push_refs: false, refs_to_keep: 100)
23
+ tag = auto_tag.create_ref(auto_tag.last_ref_from_previous_stage.try(:sha))
24
+ sh "git push origin #{tag.name}"
25
+ auto_tag.delete_locally
26
+ auto_tag.delete_on_remote
27
+ end
28
+
29
+ task :before_deploy do
30
+ sh "git fetch --tags"
31
+ end
32
+
33
+ task :after_deploy do
34
+ each_heroku_app do |stage|
35
+ create_and_push(stage.name)
36
+ end
37
+ Rake::Task['autotag:list'].invoke
38
+ end
39
+
40
+ namespace :autotag do
41
+ desc "Create an autotag for stage, default: #{STAGES.first}"
42
+ task :create, :stage do |t, args|
43
+ create_and_push(args[:stage] || STAGES.first)
44
+ end
45
+
46
+ desc "Show auto tagger history"
47
+ task :list do
48
+ puts "** AUTO TAGGER: release tag history:"
49
+ auto_tag = AutoTagger::Base.new(stages: STAGES, offline: true)
50
+ STAGES.each do |stage|
51
+ ref = auto_tag.refs_for_stage(stage).last
52
+ if ref
53
+ log = %x{git log -1 --format=oneline #{ref.sha}}.chomp
54
+ log = log[0..5] + log[40..-1]
55
+ else
56
+ log = "none"
57
+ end
58
+ puts " ** %-12s %s" % [stage, log]
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,6 +1,6 @@
1
1
  Feature: Command Line
2
2
 
3
- Scenario: Config file can be formatted like Rails' database.yml
3
+ Background:
4
4
  Given I run `rails new heroku_san_test -O`
5
5
  And I cd to "heroku_san_test"
6
6
  And I overwrite "Gemfile" with:
@@ -8,6 +8,8 @@ Feature: Command Line
8
8
  source :rubygems
9
9
  gem 'heroku_san', :path => '../../../.'
10
10
  """
11
+
12
+ Scenario: Config file can be formatted like Rails' database.yml
11
13
  Given a file named "config/heroku.yml" with:
12
14
  """
13
15
  production:
@@ -18,20 +20,13 @@ Feature: Command Line
18
20
  app: awesomeapp-demo
19
21
  """
20
22
 
21
- When I run `rake heroku:apps:local`
23
+ When I run `rake --trace heroku:apps:local`
22
24
 
23
25
  Then the output should contain "production is shorthand for the Heroku app awesomeapp"
24
26
  And the output should contain "staging is shorthand for the Heroku app awesomeapp-staging"
25
27
  And the output should contain "demo is shorthand for the Heroku app awesomeapp-demo"
26
28
 
27
29
  Scenario: Config file still accepts the heroku_san format
28
- Given I run `rails new heroku_san_test -O`
29
- And I cd to "heroku_san_test"
30
- And I overwrite "Gemfile" with:
31
- """
32
- source :rubygems
33
- gem 'heroku_san', :path => '../../../.'
34
- """
35
30
  Given a file named "config/heroku.yml" with:
36
31
  """
37
32
  apps:
@@ -40,8 +35,37 @@ Feature: Command Line
40
35
  demo: awesomeapp-demo
41
36
  """
42
37
 
43
- When I run `rake heroku:apps:local`
38
+ When I run `rake --trace heroku:apps:local`
44
39
 
45
40
  Then the output should contain "production is shorthand for the Heroku app awesomeapp"
46
41
  And the output should contain "staging is shorthand for the Heroku app awesomeapp-staging"
47
42
  And the output should contain "demo is shorthand for the Heroku app awesomeapp-demo"
43
+
44
+ Scenario: Tag information can be listed
45
+ Given a file named "config/heroku.yml" with:
46
+ """
47
+ production:
48
+ app: awesomeapp
49
+ tag: ci/*
50
+ staging:
51
+ app: awesomeapp-staging
52
+ tag: staging/*
53
+ demo:
54
+ app: awesomeapp-demo
55
+ """
56
+
57
+ When I run `rake --trace all heroku:apps:local`
58
+
59
+ Then the output should contain "the production TAG is 'ci/*'"
60
+ And the output should contain "the staging TAG is 'staging/*'"
61
+
62
+ Scenario: heroku:create_config
63
+ When I run `rake --trace heroku:create_config`
64
+ Then a file named "config/heroku.yml" should exist
65
+ And the output should match /Copied example config to ".*.config.heroku.yml"/
66
+ And the output should match /Please edit ".*.config.heroku.yml" with your application's settings./
67
+
68
+ Scenario: heroku:create_config with an EDITOR set in the environment
69
+ When I run `rake EDITOR=echo --trace heroku:create_config`
70
+ And the output should match /Copied example config to ".*.config.heroku.yml"/
71
+ And the output should match /^echo .*.config.heroku.yml$/
@@ -1,6 +1,6 @@
1
1
  Feature: Extended config
2
2
 
3
- Scenario: Config information can be pulled from a separate git repository
3
+ Background:
4
4
  Given I run `rails new heroku_san_test -O`
5
5
  And I cd to "heroku_san_test"
6
6
  And I overwrite "Gemfile" with:
@@ -8,6 +8,7 @@ Feature: Extended config
8
8
  source :rubygems
9
9
  gem 'heroku_san', :path => '../../../.'
10
10
  """
11
+ Scenario: Config information can be pulled from a separate git repository
11
12
  Given a file named "config/heroku.yml" with:
12
13
  """
13
14
  config_repo: 'file:///<%= File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', '..', 'features', 'data', 'test-config') %>'
@@ -19,19 +20,12 @@ Feature: Extended config
19
20
  app: awesomeapp-demo
20
21
  """
21
22
 
22
- When I run `rake all heroku:config:list:local`
23
+ When I run `rake --trace all heroku:config:list:local`
23
24
 
24
25
  Then the output should contain "production TEST_REMOTE: 'hello_world'"
25
26
  And the output should contain "staging TEST_REMOTE: 'goodbye_world'"
26
27
 
27
28
  Scenario: Config information can be listed
28
- Given I run `rails new heroku_san_test -O`
29
- And I cd to "heroku_san_test"
30
- And I overwrite "Gemfile" with:
31
- """
32
- source :rubygems
33
- gem 'heroku_san', :path => '../../../.'
34
- """
35
29
  Given a file named "config/heroku.yml" with:
36
30
  """
37
31
  production:
@@ -46,19 +40,12 @@ Feature: Extended config
46
40
  app: awesomeapp-demo
47
41
  """
48
42
 
49
- When I run `rake all heroku:config:list:local`
43
+ When I run `rake --trace all heroku:config:list:local`
50
44
 
51
45
  Then the output should contain "production TEST_LOCAL: 'hello_world'"
52
46
  And the output should contain "staging TEST_LOCAL: 'goodbye_world'"
53
47
 
54
48
  Scenario: Config information can be merged between local and remote
55
- Given I run `rails new heroku_san_test -O`
56
- And I cd to "heroku_san_test"
57
- And I overwrite "Gemfile" with:
58
- """
59
- source :rubygems
60
- gem 'heroku_san', :path => '../../../.'
61
- """
62
49
  Given a file named "config/heroku.yml" with:
63
50
  """
64
51
  config_repo: 'file:///<%= File.join(File.expand_path(File.dirname(__FILE__)), '..', '..', '..', 'features', 'data', 'test-config') %>'
@@ -73,7 +60,7 @@ Feature: Extended config
73
60
  TEST_REMOTE: 'overridden_by_remote'
74
61
  """
75
62
 
76
- When I run `rake all heroku:config:list:local`
63
+ When I run `rake --trace all heroku:config:list:local`
77
64
 
78
65
  Then the output should contain "production TEST_LOCAL: 'hello_world'"
79
66
  And the output should contain "production TEST_REMOTE: 'hello_world'"
@@ -0,0 +1,57 @@
1
+ @announce @slow_process
2
+ Feature: Works with Heroku
3
+
4
+ Background:
5
+ Given I run `git clone git://github.com/kmayer/heroku_san_demo.git`
6
+ And I cd to "heroku_san_demo"
7
+ And I run `bundle install`
8
+
9
+ Scenario: Remote commands
10
+ When I run `rake demo deploy`
11
+ Then the output should match /(http:.*-demo.heroku.com deployed to Heroku)|(Everything up-to-date)/
12
+
13
+ When I run `rake demo heroku:maintenance_on`
14
+ Then the output should contain "Maintenance mode enabled."
15
+
16
+ When I run `rake demo restart`
17
+ Then the output should contain "Restarting processes... done"
18
+
19
+ When I run `rake demo heroku:maintenance_off`
20
+ Then the output should contain "Maintenance mode disabled."
21
+
22
+ When I run `rake demo heroku:rake[db:seed]`
23
+ Then I run `curl -s http://heroku-san-demo-demo.heroku.com/droids.text`
24
+ And the output should contain "C3PO, Marvin, R2D2, Robby"
25
+
26
+ When I run `rake demo logs`
27
+ Then the output should contain "Starting process with command `rake db:seed`"
28
+
29
+ When I run `git co staging`
30
+ And I run `rake deploy`
31
+ Then the output should contain "Defaulting to 'staging' as it matches the current branch"
32
+ Then the output should match /(http:.*-staging.heroku.com deployed to Heroku)|(Everything up-to-date)/
33
+ When I run `curl -s http://heroku-san-demo-staging.heroku.com`
34
+ And the output should contain "Ruby on Rails: Welcome aboard"
35
+
36
+ When I run `rake production deploy:force`
37
+ Then the output should match /^git update-ref refs.heroku_san.deploy \w{40}$/
38
+ # Runs a before_deploy
39
+ # Runs an after_deploy
40
+ # Adds a collaborator
41
+
42
+ # Given I run `rake heroku:create_config`
43
+
44
+ # When I run `rake demo heroku:create`
45
+ # Then the output should contain "somthing about the created app"
46
+ # $ rake all heroku:create
47
+ # heroku create heroku-san-demo-demo
48
+ # Creating heroku-san-demo-demo.... done, stack is bamboo-mri-1.9.2
49
+ # http://heroku-san-demo-demo.heroku.com/ | git@heroku.com:heroku-san-demo-demo.git
50
+ # Git remote heroku added
51
+ # heroku create heroku-san-demo-production
52
+ # Creating heroku-san-demo-production... done, stack is bamboo-mri-1.9.2
53
+ # http://heroku-san-demo-production.heroku.com/ | git@heroku.com:heroku-san-demo-production.git
54
+ # heroku create heroku-san-demo-staging
55
+ # Creating heroku-san-demo-staging... done, stack is bamboo-mri-1.9.2
56
+ # http://heroku-san-demo-staging.heroku.com/ | git@heroku.com:heroku-san-demo-staging.git
57
+
@@ -6,3 +6,12 @@ require 'aruba/cucumber'
6
6
  unless File.readable? File.join(File.dirname(__FILE__), '..', 'data', 'test-config', 'config.yml')
7
7
  `git submodule init && git submodule update`
8
8
  end
9
+
10
+ Before do
11
+ @aruba_timeout_seconds = 15
12
+ end
13
+
14
+ Before('@slow_process') do
15
+ @aruba_timeout_seconds = 60
16
+ # @aruba_io_wait_seconds = 15
17
+ end
data/heroku_san.gemspec CHANGED
@@ -1,16 +1,16 @@
1
1
  # -*- encoding: utf-8 -*-
2
+ require File.join(File.dirname(__FILE__), 'lib/heroku_san/version')
2
3
 
3
4
  Gem::Specification.new do |s|
4
5
  s.name = %q{heroku_san}
5
-
6
- s.version = "1.3.0"
7
- s.date = %q{2011-11-07}
6
+ s.version = HerokuSan::VERSION
8
7
 
9
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
- s.authors = ["Elijah Miller", "Glenn Roberts", "Ryan Ahearn"]
9
+
10
+ s.authors = ["Elijah Miller", "Glenn Roberts", "Ryan Ahearn", "Ken Mayer"]
11
11
  s.description = %q{Manage multiple Heroku instances/apps for a single Rails app using Rake}
12
12
  s.email = %q{elijah.miller@gmail.com}
13
- s.homepage = %q{http://github.com/glennr/heroku_san}
13
+ s.homepage = %q{http://github.com/fastestforward/heroku_san}
14
14
  s.files = `git ls-files`.split("\n")
15
15
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
16
  s.require_paths = ["lib"]
@@ -24,9 +24,11 @@ Gem::Specification.new do |s|
24
24
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
25
25
  s.add_runtime_dependency(%q<rails>, ['>= 2'])
26
26
  s.add_runtime_dependency(%q<heroku>, ['>= 2'])
27
- s.add_development_dependency(%q<rails>, ['>= 3'])
27
+ s.add_runtime_dependency(%q<rake>)
28
28
  s.add_development_dependency(%q<aruba>)
29
29
  s.add_development_dependency(%q<cucumber>)
30
+ s.add_development_dependency(%q<rake>)
31
+ s.add_development_dependency(%q<bundler>, ['~> 1.0'])
30
32
  else
31
33
  s.add_dependency(%q<rails>, ['>= 2'])
32
34
  s.add_dependency(%q<heroku>, ['>= 2'])
data/lib/git.rb ADDED
@@ -0,0 +1,39 @@
1
+ require 'rake'
2
+ require 'rake/dsl_definition'
3
+
4
+ module Git
5
+ include Rake::DSL
6
+
7
+ def git_clone(repos, dir)
8
+ sh "git clone #{repos} #{dir}"
9
+ end
10
+
11
+ def git_active_branch
12
+ %x{git branch}.split("\n").select { |b| b =~ /^\*/ }.first.split(" ").last.strip
13
+ end
14
+
15
+ def git_push(commit, repo, options = [])
16
+ commit ||= "HEAD"
17
+ options ||= []
18
+ begin
19
+ sh "git update-ref refs/heroku_san/deploy #{commit}"
20
+ sh "git push #{repo} #{options.join(' ')} refs/heroku_san/deploy:refs/heads/master"
21
+ ensure
22
+ sh "git update-ref -d refs/heroku_san/deploy"
23
+ end
24
+ end
25
+
26
+ def git_tag(glob)
27
+ return nil if glob.nil?
28
+ %x{git tag -l '#{glob}'}.split("\n").last
29
+ end
30
+
31
+ def git_rev_parse(ref)
32
+ return nil if ref.nil?
33
+ %x{git rev-parse #{ref}}.split("\n").first
34
+ end
35
+
36
+ def git_parsed_tag(tag)
37
+ git_rev_parse(git_tag(tag))
38
+ end
39
+ end
@@ -0,0 +1,111 @@
1
+ module HerokuSan
2
+ class Project
3
+ attr_reader :config_file
4
+
5
+ include Git
6
+
7
+ def initialize(config_file)
8
+ @apps = []
9
+ @config_file = config_file
10
+ @app_settings = {}
11
+ config = parse(@config_file)
12
+ config.each do |stage, settings|
13
+ @app_settings[stage] = HerokuSan::Stage.new(stage, settings)
14
+ end
15
+ end
16
+
17
+ def create_config
18
+ template = File.expand_path(File.join(File.dirname(__FILE__), '../templates', 'heroku.example.yml'))
19
+ if File.exists?(@config_file)
20
+ false
21
+ else
22
+ FileUtils.cp(template, @config_file)
23
+ true
24
+ end
25
+ end
26
+
27
+ def all
28
+ @app_settings.keys
29
+ end
30
+
31
+ def [](stage)
32
+ @app_settings[stage]
33
+ end
34
+
35
+ def <<(*app)
36
+ app.flatten.each do |a|
37
+ @apps << a if all.include?(a)
38
+ end
39
+ self
40
+ end
41
+
42
+ def apps
43
+ if !@apps.empty?
44
+ @apps
45
+ else
46
+ case all.size
47
+ when 1
48
+ $stdout.puts "Defaulting to #{all.first.inspect} since only one app is defined"
49
+ all
50
+ else
51
+ active_branch = self.git_active_branch
52
+ all.select do |app|
53
+ app == active_branch and ($stdout.puts("Defaulting to '#{app}' as it matches the current branch") || true)
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def each_app
60
+ raise NoApps if apps.empty?
61
+ apps.each do |stage|
62
+ yield(self[stage])
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def parse_yaml(config_file)
69
+ if File.exists?(config_file)
70
+ if defined?(ERB)
71
+ YAML.load(ERB.new(File.read(config_file)).result)
72
+ else
73
+ YAML.load_file(config_file)
74
+ end
75
+ else
76
+ {}
77
+ end
78
+ end
79
+
80
+ def parse(config_file)
81
+ app_settings = parse_yaml(config_file)
82
+
83
+ # support heroku_san format
84
+ if app_settings.has_key? 'apps'
85
+ app_settings = app_settings['apps']
86
+ app_settings.each_pair do |stage, app_name|
87
+ app_settings[stage] = {'app' => app_name}
88
+ end
89
+ end
90
+
91
+ # load external config
92
+ if (config_repo = app_settings.delete('config_repo'))
93
+ require 'tmpdir'
94
+ tmp_config_dir = Dir.mktmpdir
95
+ tmp_config_file = File.join tmp_config_dir, 'config.yml'
96
+ git_clone(config_repo, tmp_config_dir)
97
+ extra_config = parse_yaml(tmp_config_file)
98
+ else
99
+ extra_config = {}
100
+ end
101
+
102
+ # make sure each app has a 'config' section & merge w/extra
103
+ app_settings.keys.each do |name|
104
+ app_settings[name]['config'] ||= {}
105
+ app_settings[name]['config'].merge!(extra_config[name]) if extra_config[name]
106
+ end
107
+
108
+ app_settings
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,78 @@
1
+ module HerokuSan
2
+ class Stage
3
+ attr_reader :name
4
+
5
+ def initialize(stage, options = {})
6
+ @name = stage
7
+ @options = options
8
+ end
9
+
10
+ def app
11
+ @options['app']
12
+ end
13
+
14
+ def repo
15
+ @options['repo'] ||= "git@heroku.com:#{app}.git"
16
+ end
17
+
18
+ def stack
19
+ @options['stack'] ||= %x"heroku stack --app #{app}".split("\n").select { |b| b =~ /^\* / }.first.gsub(/^\* /, '')
20
+ end
21
+
22
+ def tag
23
+ @options['tag']
24
+ end
25
+
26
+ def config
27
+ @options['config'] ||= {}
28
+ end
29
+
30
+ def run(command, args = nil)
31
+ if stack =~ /cedar/
32
+ sh_heroku "run #{command} #{args}"
33
+ else
34
+ sh_heroku "run:#{command} #{args}"
35
+ end
36
+ end
37
+
38
+ def migrate
39
+ run 'rake', 'db:migrate'
40
+ sh_heroku "restart"
41
+ end
42
+
43
+ def maintenance(action)
44
+ raise ArgumentError, "Action #{action.inspect} must be one of (:on, :off)", caller if ![:on, :off].include?(action)
45
+ sh_heroku "maintenance:#{action}"
46
+ end
47
+
48
+ def create
49
+ sh "heroku apps:create #{app}" + (@options['stack'] ? " --stack #{@options['stack']}" : '')
50
+ end
51
+
52
+ def sharing_add(email)
53
+ sh_heroku "sharing:add #{email.chomp}"
54
+ end
55
+
56
+ def sharing_remove(email)
57
+ sh_heroku "sharing:remove #{email.chomp}"
58
+ end
59
+
60
+ def long_config
61
+ sh_heroku 'config --long'
62
+ end
63
+
64
+ def restart
65
+ sh_heroku 'restart'
66
+ end
67
+
68
+ def logs
69
+ sh_heroku 'logs'
70
+ end
71
+
72
+ private
73
+
74
+ def sh_heroku command
75
+ sh "heroku #{command} --app #{app}"
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,3 @@
1
+ module HerokuSan
2
+ VERSION = "2.0.rc.1"
3
+ end
data/lib/heroku_san.rb CHANGED
@@ -1 +1,9 @@
1
- require 'heroku_san/railtie.rb' if defined?(Rails) && Rails::VERSION::MAJOR == 3
1
+ require 'railtie' if defined?(Rails) && Rails::VERSION::MAJOR == 3
2
+ require 'git'
3
+ require 'heroku_san/stage'
4
+ require 'heroku_san/project'
5
+
6
+ module HerokuSan
7
+ class NoApps < StandardError; end
8
+ class Deprecated < StandardError; end
9
+ end
@@ -4,7 +4,7 @@ require 'rails'
4
4
  module HerokuSan
5
5
  class Railtie < Rails::Railtie
6
6
  rake_tasks do
7
- load 'heroku_san/tasks.rb'
7
+ load 'tasks.rb'
8
8
  end
9
9
  end
10
10
  end
@@ -1 +1 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'heroku_san', 'tasks'))
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'tasks'))