release_party 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/lib/release_party.rb +1 -0
- data/lib/release_party/environment.rb +57 -0
- data/lib/release_party/release_file.rb +22 -0
- data/lib/release_party/release_party.rb +202 -0
- metadata +163 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'release_party/release_party'
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'release_file')
|
|
2
|
+
|
|
3
|
+
class Environment
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
@attributes = {}
|
|
7
|
+
load_defaults
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def method_missing(method_id, *args, &block)
|
|
11
|
+
case method_id.to_s
|
|
12
|
+
when /\A(.*)=\Z/
|
|
13
|
+
@attributes[$1.to_sym] = args.first
|
|
14
|
+
|
|
15
|
+
when /\A(.*)\?\Z/
|
|
16
|
+
@attributes.key?($1.to_sym)
|
|
17
|
+
|
|
18
|
+
else
|
|
19
|
+
unless @attributes.key?(method_id)
|
|
20
|
+
raise ArgumentError, "No value for #{method_id} defined"
|
|
21
|
+
end
|
|
22
|
+
value = @attributes[method_id.to_sym]
|
|
23
|
+
return value.call if value.is_a?(Proc)
|
|
24
|
+
value
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def load_release_file
|
|
30
|
+
# Process the Releasefile and merge the attributes loaded
|
|
31
|
+
release_file = ReleaseFile.new
|
|
32
|
+
@attributes = @attributes.merge release_file.attributes
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def load_capistrano_defaults(cap_config)
|
|
36
|
+
# General cap info
|
|
37
|
+
self.user = cap_config.fetch(:user, `git config user.name`.chomp)
|
|
38
|
+
self.branch = cap_config.fetch(:branch, 'master')
|
|
39
|
+
self.stage = cap_config.fetch(:stage, 'staging')
|
|
40
|
+
self.repository = cap_config.fetch(:repository, nil)
|
|
41
|
+
self.domain = cap_config.fetch(:domain, nil)
|
|
42
|
+
self.display_name = cap_config.fetch(:display_name, 'Release Party')
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def load_defaults
|
|
46
|
+
self.from_address = 'Release Party <releaseparty@noreply.org>'
|
|
47
|
+
self.smtp_address = 'localhost'
|
|
48
|
+
self.smtp_port = 25
|
|
49
|
+
self.subject = Proc.new {"A release of #{display_name} was released to #{domain}"}
|
|
50
|
+
self.finished_stories = []
|
|
51
|
+
self.known_bugs = []
|
|
52
|
+
self.send_email = true
|
|
53
|
+
self.template_engine = :haml
|
|
54
|
+
self.deliver_stories = false
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class ReleaseFile
|
|
2
|
+
|
|
3
|
+
RELEASE_FILE = 'Releasefile'
|
|
4
|
+
|
|
5
|
+
attr_reader :attributes
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@attributes = {}
|
|
9
|
+
|
|
10
|
+
capfile_path = File.join(Dir.pwd, RELEASE_FILE)
|
|
11
|
+
if File.exists?(capfile_path)
|
|
12
|
+
eval File.read(capfile_path)
|
|
13
|
+
else
|
|
14
|
+
puts "Warning: No #{RELEASE_FILE} found, you should put one in your project root."
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def method_missing(method_id, *args, &block)
|
|
19
|
+
@attributes[method_id.to_sym] = args.first || block
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
require 'capistrano'
|
|
2
|
+
require 'pivotal-tracker'
|
|
3
|
+
require 'mail'
|
|
4
|
+
require 'grit'
|
|
5
|
+
|
|
6
|
+
require File.join(File.dirname(__FILE__), 'environment')
|
|
7
|
+
|
|
8
|
+
module Capistrano::ReleaseParty
|
|
9
|
+
|
|
10
|
+
def self.extended(configuration)
|
|
11
|
+
configuration.load do
|
|
12
|
+
|
|
13
|
+
after 'deploy', 'release_party:finished'
|
|
14
|
+
|
|
15
|
+
namespace 'release_party' do
|
|
16
|
+
task 'started' do
|
|
17
|
+
@env = Environment.new
|
|
18
|
+
|
|
19
|
+
# Record when the release began
|
|
20
|
+
@env.release_started = Time.now
|
|
21
|
+
|
|
22
|
+
@env.load_capistrano_defaults(self)
|
|
23
|
+
@env.load_release_file
|
|
24
|
+
|
|
25
|
+
announce "beginning deployment, project details obtained."
|
|
26
|
+
|
|
27
|
+
load_git_progress @env
|
|
28
|
+
load_tracker_progress @env
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
task 'finished' do
|
|
32
|
+
raise ArgumentError, "Release finished without being started" if @env.nil?
|
|
33
|
+
|
|
34
|
+
begin
|
|
35
|
+
announce "celebrating release, deployment finished successfully!"
|
|
36
|
+
|
|
37
|
+
# Record when the release finished
|
|
38
|
+
@env.release_finished = Time.now
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Do pivotal story delivery
|
|
42
|
+
if @env.deliver_stories
|
|
43
|
+
commit_tracker_progress @env
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Send an email notification
|
|
47
|
+
if @env.send_email
|
|
48
|
+
body = load_email_content @env
|
|
49
|
+
deliver_notification(@env, body)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
#rescue ArgumentError => error
|
|
53
|
+
# puts "ERROR: #{error.message}"
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def commit_tracker_progress(env)
|
|
61
|
+
# Go through each finished story id we've seen in the git repo
|
|
62
|
+
# and deliver each story that is marked as finished
|
|
63
|
+
env.finished_store_ids.each do |story_id|
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def load_git_progress(env)
|
|
69
|
+
#repo = Grit::Repo.new(Dir.pwd)
|
|
70
|
+
repo = Grit::Repo.new("/Users/john/Projects/breeze")
|
|
71
|
+
config = Grit::Config.new(repo)
|
|
72
|
+
|
|
73
|
+
feature_branch = config.fetch('gitflow.prefix.feature', 'feature/')
|
|
74
|
+
release_branch = config.fetch('gitflow.prefix.release', 'release/')
|
|
75
|
+
puts "Feature branch: #{feature_branch}"
|
|
76
|
+
puts "Release branch: #{release_branch}"
|
|
77
|
+
|
|
78
|
+
# Find the last release
|
|
79
|
+
latest_release = \
|
|
80
|
+
repo.commits('master').find do |commit|
|
|
81
|
+
commit.message =~ /\AMerge branch '#{release_branch}(\d+)/
|
|
82
|
+
end
|
|
83
|
+
latest_release_tag = $1
|
|
84
|
+
|
|
85
|
+
puts "Commit: #{latest_release} #{latest_release.message}"
|
|
86
|
+
previous_release = \
|
|
87
|
+
repo.commits('master', 100).find do |commit|
|
|
88
|
+
if commit.message =~ /\AMerge branch '#{release_branch}(\d+)/
|
|
89
|
+
latest_release_tag != $1
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
previous_release_tag = $1
|
|
93
|
+
puts "Commit: #{previous_release} #{previous_release.message}"
|
|
94
|
+
|
|
95
|
+
env.finished_story_ids =
|
|
96
|
+
repo.commits('develop').collect do |commit|
|
|
97
|
+
$1 if commit.message =~ /\AMerge branch '#{feature_branch}(\d+)/
|
|
98
|
+
end.compact
|
|
99
|
+
puts env.finished_story_ids
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def load_email_content(env)
|
|
103
|
+
arguments_required env, :template_engine, :template
|
|
104
|
+
|
|
105
|
+
template = env.template
|
|
106
|
+
raise ArgumentError, "Missing template file #{template}" unless File.exists?(template)
|
|
107
|
+
|
|
108
|
+
engine = \
|
|
109
|
+
case env.template_engine
|
|
110
|
+
when :haml
|
|
111
|
+
load_haml
|
|
112
|
+
Haml::Engine.new(File.read(template))
|
|
113
|
+
|
|
114
|
+
when :erb
|
|
115
|
+
load_erb
|
|
116
|
+
ERB.new(File.read(template))
|
|
117
|
+
|
|
118
|
+
else
|
|
119
|
+
raise ArgumentError, "Unsupported template engine #{env.template_engine}"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
engine.render env
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def load_tracker_progress(env)
|
|
126
|
+
arguments_required env, :project_id, :project_api_key
|
|
127
|
+
|
|
128
|
+
PivotalTracker::Client.token = env.project_api_key
|
|
129
|
+
|
|
130
|
+
begin
|
|
131
|
+
env.project = PivotalTracker::Project.find(env.project_id)
|
|
132
|
+
rescue RestClient::Request::Unauthorized => e
|
|
133
|
+
puts 'Unable to authenticate with pivotal, perhaps your API key is wrong.' +
|
|
134
|
+
"Message: #{e.message}"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
env.instance_eval do
|
|
138
|
+
self.finished_stories = \
|
|
139
|
+
project.stories.all(:state => 'finished')
|
|
140
|
+
self.known_bugs = \
|
|
141
|
+
project.stories.all(:state => ['unstarted', 'started'], :story_type => 'bug')
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def deliver_notification(env, body)
|
|
146
|
+
arguments_required env,
|
|
147
|
+
:smtp_address, :smtp_port, :smtp_domain, :from_address,
|
|
148
|
+
:email_notification_to, :subject
|
|
149
|
+
|
|
150
|
+
Mail.defaults do
|
|
151
|
+
delivery_method :smtp,
|
|
152
|
+
:address => env.smtp_address,
|
|
153
|
+
:port => env.smtp_port,
|
|
154
|
+
:domain => env.smtp_domain
|
|
155
|
+
end
|
|
156
|
+
announce "delivering deployment notice to #{env.email_notification_to.inspect}"
|
|
157
|
+
Mail.deliver do
|
|
158
|
+
from env.from_address
|
|
159
|
+
to env.email_notification_to
|
|
160
|
+
subject env.subject
|
|
161
|
+
html_part do
|
|
162
|
+
content_type 'text/html; charset=UTF-8'
|
|
163
|
+
body body
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
announce "deployment notice sent!"
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def announce(message)
|
|
170
|
+
puts "ReleaseParty: #{message}"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def arguments_required(env, *arguments)
|
|
174
|
+
missing = arguments.reject do |argument|
|
|
175
|
+
env.send("#{argument}?".to_sym)
|
|
176
|
+
end
|
|
177
|
+
unless missing.empty?
|
|
178
|
+
raise ArgumentError, "Parameters #{missing.join(', ')} must be defined"
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def load_haml
|
|
183
|
+
require 'haml'
|
|
184
|
+
rescue LoadError => error
|
|
185
|
+
raise ArgumentError,
|
|
186
|
+
'Unable to load HAML, you need to make sure the haml gem is installed '
|
|
187
|
+
'if it is specified as the template engine'
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def load_erb
|
|
191
|
+
require 'erb'
|
|
192
|
+
rescue LoadError => error
|
|
193
|
+
raise ArgumentError,
|
|
194
|
+
'Unable to load ERB, you need to make sure the erb gem is installed '
|
|
195
|
+
'if it is specified as the template engine'
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
if Capistrano::Configuration.instance
|
|
201
|
+
Capistrano::Configuration.instance.extend(Capistrano::Configuration.instance)
|
|
202
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: release_party
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 29
|
|
5
|
+
prerelease:
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 0
|
|
9
|
+
- 1
|
|
10
|
+
version: 0.0.1
|
|
11
|
+
platform: ruby
|
|
12
|
+
authors:
|
|
13
|
+
- John Barker
|
|
14
|
+
autorequire:
|
|
15
|
+
bindir: bin
|
|
16
|
+
cert_chain: []
|
|
17
|
+
|
|
18
|
+
date: 2011-05-18 00:00:00 +08:00
|
|
19
|
+
default_executable:
|
|
20
|
+
dependencies:
|
|
21
|
+
- !ruby/object:Gem::Dependency
|
|
22
|
+
name: capistrano
|
|
23
|
+
prerelease: false
|
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
hash: 23
|
|
30
|
+
segments:
|
|
31
|
+
- 2
|
|
32
|
+
- 6
|
|
33
|
+
- 0
|
|
34
|
+
version: 2.6.0
|
|
35
|
+
type: :runtime
|
|
36
|
+
version_requirements: *id001
|
|
37
|
+
- !ruby/object:Gem::Dependency
|
|
38
|
+
name: pivotal-tracker
|
|
39
|
+
prerelease: false
|
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ">="
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
hash: 23
|
|
46
|
+
segments:
|
|
47
|
+
- 0
|
|
48
|
+
- 2
|
|
49
|
+
- 0
|
|
50
|
+
version: 0.2.0
|
|
51
|
+
type: :runtime
|
|
52
|
+
version_requirements: *id002
|
|
53
|
+
- !ruby/object:Gem::Dependency
|
|
54
|
+
name: grit
|
|
55
|
+
prerelease: false
|
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
hash: 29
|
|
62
|
+
segments:
|
|
63
|
+
- 2
|
|
64
|
+
- 4
|
|
65
|
+
- 1
|
|
66
|
+
version: 2.4.1
|
|
67
|
+
type: :runtime
|
|
68
|
+
version_requirements: *id003
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: mail
|
|
71
|
+
prerelease: false
|
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
74
|
+
requirements:
|
|
75
|
+
- - ">="
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
hash: 11
|
|
78
|
+
segments:
|
|
79
|
+
- 2
|
|
80
|
+
- 1
|
|
81
|
+
- 0
|
|
82
|
+
version: 2.1.0
|
|
83
|
+
type: :runtime
|
|
84
|
+
version_requirements: *id004
|
|
85
|
+
- !ruby/object:Gem::Dependency
|
|
86
|
+
name: capistrano_colors
|
|
87
|
+
prerelease: false
|
|
88
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
|
89
|
+
none: false
|
|
90
|
+
requirements:
|
|
91
|
+
- - ">="
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
hash: 3
|
|
94
|
+
segments:
|
|
95
|
+
- 0
|
|
96
|
+
version: "0"
|
|
97
|
+
type: :runtime
|
|
98
|
+
version_requirements: *id005
|
|
99
|
+
- !ruby/object:Gem::Dependency
|
|
100
|
+
name: colored
|
|
101
|
+
prerelease: false
|
|
102
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
|
103
|
+
none: false
|
|
104
|
+
requirements:
|
|
105
|
+
- - ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
hash: 3
|
|
108
|
+
segments:
|
|
109
|
+
- 0
|
|
110
|
+
version: "0"
|
|
111
|
+
type: :runtime
|
|
112
|
+
version_requirements: *id006
|
|
113
|
+
description: Perform a number of common post deployment tasks such as delivering pivotal stories, sending out a release email, notifying campfire.
|
|
114
|
+
email: jebarker@gmail.com
|
|
115
|
+
executables: []
|
|
116
|
+
|
|
117
|
+
extensions: []
|
|
118
|
+
|
|
119
|
+
extra_rdoc_files: []
|
|
120
|
+
|
|
121
|
+
files:
|
|
122
|
+
- lib/release_party/environment.rb
|
|
123
|
+
- lib/release_party/release_file.rb
|
|
124
|
+
- lib/release_party/release_party.rb
|
|
125
|
+
- lib/release_party.rb
|
|
126
|
+
has_rdoc: true
|
|
127
|
+
homepage:
|
|
128
|
+
licenses: []
|
|
129
|
+
|
|
130
|
+
post_install_message:
|
|
131
|
+
rdoc_options: []
|
|
132
|
+
|
|
133
|
+
require_paths:
|
|
134
|
+
- lib
|
|
135
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
136
|
+
none: false
|
|
137
|
+
requirements:
|
|
138
|
+
- - ">="
|
|
139
|
+
- !ruby/object:Gem::Version
|
|
140
|
+
hash: 3
|
|
141
|
+
segments:
|
|
142
|
+
- 0
|
|
143
|
+
version: "0"
|
|
144
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
|
+
none: false
|
|
146
|
+
requirements:
|
|
147
|
+
- - ">"
|
|
148
|
+
- !ruby/object:Gem::Version
|
|
149
|
+
hash: 25
|
|
150
|
+
segments:
|
|
151
|
+
- 1
|
|
152
|
+
- 3
|
|
153
|
+
- 1
|
|
154
|
+
version: 1.3.1
|
|
155
|
+
requirements: []
|
|
156
|
+
|
|
157
|
+
rubyforge_project:
|
|
158
|
+
rubygems_version: 1.6.2
|
|
159
|
+
signing_key:
|
|
160
|
+
specification_version: 3
|
|
161
|
+
summary: Celebrate releases
|
|
162
|
+
test_files: []
|
|
163
|
+
|