heroku_release 0.0.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 ADDED
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ doc/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in heroku_deploy.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,30 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ heroku_release (0.0.1)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.2)
10
+ mocha (0.9.9)
11
+ rake
12
+ rake (0.8.7)
13
+ rspec (2.0.0)
14
+ rspec-core (= 2.0.0)
15
+ rspec-expectations (= 2.0.0)
16
+ rspec-mocks (= 2.0.0)
17
+ rspec-core (2.0.0)
18
+ rspec-expectations (2.0.0)
19
+ diff-lcs (>= 1.1.2)
20
+ rspec-mocks (2.0.0)
21
+ rspec-core (= 2.0.0)
22
+ rspec-expectations (= 2.0.0)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ heroku_release!
29
+ mocha (~> 0.9.9)
30
+ rspec (~> 2.0.0)
data/README.rdoc ADDED
@@ -0,0 +1,45 @@
1
+ = Heroku Deploy
2
+
3
+ Simple RubyGem for tagging and deploying versioned releases of an application to Heroku with the ability to do rollbacks.
4
+
5
+ == Usage
6
+
7
+ If you are using Bundler, add a gem dependency like this:
8
+
9
+ group :development, :test do
10
+ gem 'heroku_deploy', '~> version known to work'
11
+ end
12
+
13
+ Require the Rake tasks in your Rakefile:
14
+
15
+ require 'heroku_deploy/tasks'
16
+
17
+ You have a few configuration options. If you are using a Git remote other than "heroku" then you must configure that. You can choose to have the release version written to a file on deploy so you can check the version on the live server. Here is an example:
18
+
19
+ HerokuDeploy.config.heroku_remote = "production" # git remote for heroku, defaults to "heroku"
20
+ HerokuDeploy.config.version_file_path = "public/version"
21
+
22
+ To deploy the master branch to production, use the heroku_deploy rake task:
23
+
24
+ rake heroku_deploy COMMENT="This is a comment describing what changed since the last release"
25
+
26
+ If the deploy went horribly wrong and you need to do a rollback you can do so:
27
+
28
+ rake heroku_deploy:rollback
29
+
30
+ In order to see the changelog:
31
+
32
+ rake heroku_deploy:log
33
+
34
+ To examine git commits since the last release you can do:
35
+
36
+ rake heroku_deploy:pending
37
+
38
+ == TODO
39
+
40
+ It seems Heroku {is working on}[http://github.com/adamwiggins/heroku-releases] incorporating rollback functionality into the heroku command. Something to look into...
41
+
42
+ == Credit
43
+
44
+ This gem was inspired by
45
+ {this gist}[http://gist.github.com/raw/307543/c762d1cb136588fb76edff907d65bbf80a1e254e/deploy.rake].
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+ Bundler.require
4
+
5
+ require 'rspec/core'
6
+ require 'rspec/core/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new(:default)
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "heroku_release/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "heroku_release"
7
+ s.version = HerokuRelease::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Peter Marklund"]
10
+ s.email = ["peter@marklunds.com"]
11
+ s.homepage = "http://rubygems.org/gems/heroku_release"
12
+ s.summary = %q{Simple RubyGem for tagging and deploying versioned releases of an application to Heroku with the ability to do rollbacks.}
13
+
14
+ s.rubyforge_project = "heroku_release"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_development_dependency "rspec", "~> 2.0.0"
22
+ s.add_development_dependency "mocha", "~> 0.9.9"
23
+ end
@@ -0,0 +1 @@
1
+ Dir[File.join(File.dirname(__FILE__), "..", "tasks", "*.rake")].each { |rake_file| load rake_file }
@@ -0,0 +1,3 @@
1
+ module HerokuRelease
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,135 @@
1
+ require 'ostruct'
2
+
3
+ module HerokuRelease
4
+ @@config = OpenStruct.new(
5
+ :heroku_remote => "heroku"
6
+ )
7
+
8
+ def self.config
9
+ @@config
10
+ end
11
+
12
+ def self.config=(config)
13
+ @@config = config
14
+ end
15
+
16
+ class Task
17
+ def self.tasks
18
+ {
19
+ :push => nil,
20
+ :tag => nil,
21
+ :log => "Produces a changelog from release tags and their comments. Assumes tag comments have no newlines in them.",
22
+ :current_release => "Show version of current release",
23
+ :previous_release => "Show version of previous release",
24
+ :pending => "Show git commits since last released version",
25
+ :rollback => "Rollback to previous release and remove current release tag"
26
+ }
27
+ end
28
+
29
+ def push
30
+ output 'Deploying site to Heroku ...'
31
+ execute "git push #{config.heroku_remote} master"
32
+ end
33
+
34
+ def tag
35
+ release_name = get_release_name
36
+ commit_version_file(release_name) if config.version_file_path
37
+ comment = ENV['COMMENT'] || 'Tagged release'
38
+ output "Tagging release as '#{release_name}' with comment '#{comment}'"
39
+ execute "git tag -a #{release_name} -m '#{comment}'"
40
+ execute "git push --tags origin"
41
+ execute "git push --tags #{config.heroku_remote}"
42
+ end
43
+
44
+ def log
45
+ change_log = git_tags_with_comments.scan(/^\s*(release-\d+-\d+)\s*(.+)$/).reverse.map do |release, comment|
46
+ "- #{release}\n\n#{comment}\n\n"
47
+ end.join
48
+ output "\n" + change_log
49
+ end
50
+
51
+ def current_release
52
+ output current_release_version
53
+ end
54
+
55
+ def previous_release
56
+ if previous_release_version
57
+ output previous_release_version
58
+ else
59
+ output "no previous release found"
60
+ end
61
+ end
62
+
63
+ def pending
64
+ execute "git log #{current_release_version}..HEAD"
65
+ end
66
+
67
+ def rollback
68
+ # Store releases in local variables so they don't change during tag deletion
69
+ current = current_release_version
70
+ previous = previous_release_version
71
+ if previous
72
+ output "Rolling back to '#{previous}' ..."
73
+ execute "git push -f #{config.heroku_remote} #{previous}:master"
74
+ output "Deleting rollbacked release '#{current}' ..."
75
+ execute "git tag -d #{current}"
76
+ execute "git push #{config.heroku_remote} :refs/tags/#{current}"
77
+ execute "git push origin :refs/tags/#{current}"
78
+ output 'Rollback completed'
79
+ else
80
+ output "No release tags found - cannot do rollback"
81
+ output releases
82
+ end
83
+ end
84
+
85
+ private
86
+
87
+ def output(message)
88
+ puts message
89
+ end
90
+
91
+ def execute(command)
92
+ output `#{command}`.strip
93
+ end
94
+
95
+ def releases
96
+ @releases ||= git_tags.split("\n").select { |t| t[0..7] == 'release-' }.sort
97
+ end
98
+
99
+ def current_release_version
100
+ releases.last
101
+ end
102
+
103
+ def previous_release_version
104
+ if releases.length >= 2 && previous = releases[-2]
105
+ previous
106
+ else
107
+ nil
108
+ end
109
+ end
110
+
111
+ def commit_version_file(release_name)
112
+ output "Committing version file for release #{release_name}"
113
+ File.open(config.version_file_path, "w") { |f| f.print release_name }
114
+ execute "git add #{config.version_file_path}"
115
+ execute "git commit -m 'Updated version file to #{release_name}'"
116
+ execute "git push origin master"
117
+ end
118
+
119
+ def get_release_name
120
+ "release-#{Time.now.utc.strftime("%Y%m%d-%H%M%S")}"
121
+ end
122
+
123
+ def git_tags
124
+ `git tag`
125
+ end
126
+
127
+ def git_tags_with_comments
128
+ `git tag -n`
129
+ end
130
+
131
+ def config
132
+ HerokuRelease.config
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,11 @@
1
+ desc "Tag and push master branch to heroku"
2
+ task :heroku_release => ['heroku_release:tag', 'heroku_release:push']
3
+
4
+ namespace :heroku_release do
5
+ HerokuRelease::Task.tasks.each do |task_name, description|
6
+ desc(description) if description
7
+ task task_name do
8
+ HerokuRelease::Task.new.send(task_name)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe HerokuRelease do
4
+ before(:each) do
5
+ @config_before = HerokuRelease.config.dup
6
+ end
7
+
8
+ after(:each) do
9
+ HerokuRelease.config = @config_before
10
+ end
11
+
12
+ describe "config" do
13
+ it "defaults heroku_remote to heroku" do
14
+ config.heroku_remote.should == "heroku"
15
+ end
16
+
17
+ it "can set heroku_remote" do
18
+ config.heroku_remote = "production"
19
+ config.heroku_remote.should == "production"
20
+ end
21
+
22
+ it "defaults version_file_path to nil" do
23
+ config.version_file_path.should be_nil
24
+ end
25
+
26
+ it "can set version_file_path" do
27
+ config.version_file_path = "public/system/version"
28
+ config.version_file_path.should == "public/system/version"
29
+ end
30
+ end
31
+
32
+ def config
33
+ HerokuRelease.config
34
+ end
35
+ end
@@ -0,0 +1,9 @@
1
+ require 'bundler'
2
+ Bundler.require(:development)
3
+
4
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
5
+ require 'heroku_release'
6
+
7
+ RSpec.configure do |config|
8
+ config.mock_with :mocha
9
+ end
data/spec/task_spec.rb ADDED
@@ -0,0 +1,208 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+
4
+ describe HerokuRelease::Task do
5
+ before(:each) do
6
+ @task = HerokuRelease::Task.new
7
+ @config = HerokuRelease.config
8
+ @config_before = HerokuRelease.config.dup
9
+ end
10
+
11
+ after(:each) do
12
+ HerokuRelease.config = @config_before
13
+ end
14
+
15
+ describe "tasks" do
16
+ it "is a Hash" do
17
+ HerokuRelease::Task.tasks.is_a?(Hash).should be_true
18
+ end
19
+
20
+ it "corresponds to public instance methods" do
21
+ HerokuRelease::Task.tasks.keys.map(&:to_s).sort.should == task_instance_methods
22
+ end
23
+
24
+ def task_instance_methods
25
+ HerokuRelease::Task.public_instance_methods(false).map(&:to_s).sort
26
+ end
27
+ end
28
+
29
+ describe "push" do
30
+ it "should execute a git push to heroku remote" do
31
+ @task.expects(:execute).with("git push #{@config.heroku_remote} master")
32
+ @task.push
33
+ end
34
+ end
35
+
36
+ describe "tag" do
37
+ it "create git tag with release name and comment and pushes it to origin and to heroku" do
38
+ ENV['COMMENT'] = "the comment"
39
+ @task.expects(:get_release_name).returns("release-123")
40
+ @task.expects(:commit_version_file).never
41
+ git = sequence('git')
42
+ @task.expects(:execute).with("git tag -a release-123 -m 'the comment'").in_sequence(git)
43
+ @task.expects(:execute).with("git push --tags origin").in_sequence(git)
44
+ @task.expects(:execute).with("git push --tags #{@config.heroku_remote}").in_sequence(git)
45
+
46
+ @task.tag
47
+ end
48
+
49
+ it "commits version file if version_file_path is set" do
50
+ ENV['COMMENT'] = nil
51
+ @config.version_file_path = "public/version"
52
+ @task.expects(:get_release_name).returns("release-123")
53
+ @task.expects(:commit_version_file).with("release-123")
54
+ git = sequence('git')
55
+ @task.expects(:execute).with("git tag -a release-123 -m 'Tagged release'").in_sequence(git)
56
+ @task.expects(:execute).with("git push --tags origin").in_sequence(git)
57
+ @task.expects(:execute).with("git push --tags #{@config.heroku_remote}").in_sequence(git)
58
+
59
+ @task.tag
60
+ end
61
+ end
62
+
63
+ describe "log" do
64
+ it "outputs a changelog from the git tags and their comments" do
65
+ git_tags = <<-END
66
+ release-20101026-143729 Initial release
67
+ release-20101027-225442 Some improvements
68
+ release-20101029-085937 Major new feature
69
+ END
70
+ @task.expects(:git_tags_with_comments).returns(git_tags)
71
+ @task.expects(:output).with(<<-END
72
+
73
+ - release-20101029-085937
74
+
75
+ Major new feature
76
+
77
+ - release-20101027-225442
78
+
79
+ Some improvements
80
+
81
+ - release-20101026-143729
82
+
83
+ Initial release
84
+
85
+ END
86
+ )
87
+
88
+ @task.log
89
+ end
90
+ end
91
+
92
+ describe "current_release" do
93
+ it "should output latest release" do
94
+ @task.expects(:releases).returns(%w(release-20100922-115151 release-20100922-122313))
95
+ @task.expects(:output).with("release-20100922-122313")
96
+ @task.current_release
97
+ end
98
+ end
99
+
100
+ describe "previous_release" do
101
+ it "outputs previous release if there is one" do
102
+ @task.stubs(:releases).returns(%w(release-20100922-115151 release-20100922-122313))
103
+ @task.expects(:output).with("release-20100922-115151")
104
+ @task.previous_release
105
+ end
106
+
107
+ it "says there is no previous release if there is none" do
108
+ @task.stubs(:releases).returns(%w(release-20100922-122313))
109
+ @task.expects(:output).with("no previous release found")
110
+ @task.previous_release
111
+ end
112
+ end
113
+
114
+ describe "pending" do
115
+ it "executes a git log between current release and HEAD" do
116
+ @task.expects(:current_release_version).returns("release-123")
117
+ @task.expects(:execute).with("git log release-123..HEAD")
118
+ @task.pending
119
+ end
120
+ end
121
+
122
+ describe "rollback" do
123
+ it "pushes previous release tag to heroku and deletes current release tag" do
124
+ git = sequence('git')
125
+ @task.expects(:previous_release_version).returns("release-previous")
126
+ @task.expects(:current_release_version).returns("release-current")
127
+ @task.expects(:execute).with("git push -f #{@config.heroku_remote} release-previous:master").in_sequence(git)
128
+ @task.expects(:execute).with("git tag -d release-current").in_sequence(git)
129
+ @task.expects(:execute).with("git push #{@config.heroku_remote} :refs/tags/release-current").in_sequence(git)
130
+ @task.expects(:execute).with("git push origin :refs/tags/release-current").in_sequence(git)
131
+
132
+ @task.rollback
133
+ end
134
+ end
135
+
136
+ describe "output" do
137
+ it "invokes puts with message" do
138
+ @task.expects(:puts).with("a message")
139
+ @task.send(:output, "a message")
140
+ end
141
+ end
142
+
143
+ describe "execute" do
144
+ it "executes a command and outputs the result" do
145
+ @task.expects(:output).with("foobar")
146
+ @task.send(:execute, "echo foobar")
147
+ end
148
+
149
+ it "outputs empty line if there is no output" do
150
+ @task.expects(:output).with("")
151
+ @task.send(:execute, "echo")
152
+ end
153
+ end
154
+
155
+ describe "get_release_name" do
156
+ it "generates a timestamped tag name" do
157
+ @task.send(:get_release_name).should =~ /release-\d\d\d\d\d\d\d\d-\d\d\d\d\d\d/
158
+ end
159
+ end
160
+
161
+ describe "commit_version_file(release_name)" do
162
+ before(:each) do
163
+ @path = "spec/version"
164
+ end
165
+
166
+ after(:each) do
167
+ FileUtils.rm_f(@path)
168
+ end
169
+
170
+ it "writes release_name to version_file_path and commits that file" do
171
+ git = sequence('git')
172
+ @task.expects(:execute).with("git add #{@path}").in_sequence(git)
173
+ @task.expects(:execute).with("git commit -m 'Updated version file to release-123'").in_sequence(git)
174
+ @task.expects(:execute).with("git push origin master").in_sequence(git)
175
+
176
+ HerokuRelease.config.version_file_path = @path
177
+ @task.send(:commit_version_file, "release-123")
178
+
179
+ File.read(@path).should == "release-123"
180
+ end
181
+ end
182
+
183
+ describe "git_tags_with_comments" do
184
+ it "works" do
185
+ @task.send(:git_tags_with_comments)
186
+ end
187
+ end
188
+
189
+ describe "releases" do
190
+ it "parses out list of releases from git tags" do
191
+ @task.expects(:git_tags).returns(<<-END
192
+ release-20100922-115151
193
+ release-20100922-122313
194
+ foobar
195
+ release-20100926-173016
196
+ END
197
+ )
198
+
199
+ @task.send(:releases).should == %w(release-20100922-115151 release-20100922-122313 release-20100926-173016)
200
+ end
201
+ end
202
+
203
+ describe "git_tags" do
204
+ it "works" do
205
+ @task.send(:git_tags)
206
+ end
207
+ end
208
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: heroku_release
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Peter Marklund
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-03 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 0
30
+ - 0
31
+ version: 2.0.0
32
+ type: :development
33
+ prerelease: false
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: mocha
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 0
44
+ - 9
45
+ - 9
46
+ version: 0.9.9
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: *id002
50
+ description:
51
+ email:
52
+ - peter@marklunds.com
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ extra_rdoc_files: []
58
+
59
+ files:
60
+ - .gitignore
61
+ - Gemfile
62
+ - Gemfile.lock
63
+ - README.rdoc
64
+ - Rakefile
65
+ - heroku_release.gemspec
66
+ - lib/heroku_release.rb
67
+ - lib/heroku_release/tasks.rb
68
+ - lib/heroku_release/version.rb
69
+ - lib/tasks/heroku_release.rake
70
+ - spec/heroku_release_spec.rb
71
+ - spec/spec_helper.rb
72
+ - spec/task_spec.rb
73
+ has_rdoc: true
74
+ homepage: http://rubygems.org/gems/heroku_release
75
+ licenses: []
76
+
77
+ post_install_message:
78
+ rdoc_options: []
79
+
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 746099489
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ hash: 746099489
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ requirements: []
101
+
102
+ rubyforge_project: heroku_release
103
+ rubygems_version: 1.3.7
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: Simple RubyGem for tagging and deploying versioned releases of an application to Heroku with the ability to do rollbacks.
107
+ test_files:
108
+ - spec/heroku_release_spec.rb
109
+ - spec/spec_helper.rb
110
+ - spec/task_spec.rb