socialcast-git-extensions 0.4.0

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Ryan Sonnek
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,17 @@
1
+ = socialcast-git-extensions
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Ryan Sonnek. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "socialcast-git-extensions"
8
+ gem.summary = %Q{git extension scripts for socialcast workflow}
9
+ gem.description = %Q{git extension scripts for socialcast workflow}
10
+ gem.email = "ryan@socialcast.com"
11
+ gem.homepage = "http://github.com/wireframe/socialcast-git-extensions"
12
+ gem.authors = ["Ryan Sonnek"]
13
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ gem.add_runtime_dependency "grit", ">= 0"
15
+ gem.add_runtime_dependency "jira4r", ">= 0"
16
+ gem.add_runtime_dependency "soap4r", ">= 0"
17
+ gem.add_runtime_dependency "activesupport", ">= 2.3.5"
18
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
19
+ end
20
+ Jeweler::GemcutterTasks.new
21
+ rescue LoadError
22
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
23
+ end
24
+
25
+ require 'rake/testtask'
26
+ Rake::TestTask.new(:test) do |test|
27
+ test.libs << 'lib' << 'test'
28
+ test.pattern = 'test/**/test_*.rb'
29
+ test.verbose = true
30
+ end
31
+
32
+ begin
33
+ require 'rcov/rcovtask'
34
+ Rcov::RcovTask.new do |test|
35
+ test.libs << 'test'
36
+ test.pattern = 'test/**/test_*.rb'
37
+ test.verbose = true
38
+ end
39
+ rescue LoadError
40
+ task :rcov do
41
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
42
+ end
43
+ end
44
+
45
+ task :test => :check_dependencies
46
+
47
+ task :default => :test
48
+
49
+ require 'rake/rdoctask'
50
+ Rake::RDocTask.new do |rdoc|
51
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "socialcast-git-extensions #{version}"
55
+ rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
data/bin/git-integrate ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'grit'
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'socialcast-git-extensions.rb')
5
+ include Socialcast
6
+
7
+ repo = Grit::Repo.new(Dir.pwd)
8
+ branch = Grit::Head.current(repo).name
9
+
10
+ update(branch)
11
+ integrate(branch, 'staging')
12
+
13
+ ticket = ARGV.shift
14
+ if ticket
15
+ update_ticket ticket, {:branch => branch, :in_staging => true}
16
+ start_ticket ticket
17
+ end
data/bin/git-promote ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'grit'
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'socialcast-git-extensions.rb')
5
+ include Socialcast
6
+
7
+ repo = Grit::Repo.new(Dir.pwd)
8
+ branch = Grit::Head.current(repo).name
9
+
10
+ ticket = ARGV.shift
11
+ raise 'JIRA ticket is required in order to move into next_release'
12
+
13
+ update(branch)
14
+ integrate(branch, 'next_release')
15
+
16
+ update_ticket ticket, {:branch => branch, :in_staging => true}
17
+ resolve_ticket ticket
18
+
19
+ integrate(branch, 'staging')
data/bin/git-release ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'grit'
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'socialcast-git-extensions.rb')
5
+ require 'readline'
6
+ include Socialcast
7
+
8
+ repo = Grit::Repo.new(Dir.pwd)
9
+ branch = Grit::Head.current(repo).name
10
+
11
+ raise "Cannot release reserved branch" if %w{master staging}.include?(branch)
12
+
13
+ exit unless Readline.readline("This will release #{branch} to production. Are you sure (y/n)? ") == 'y'
14
+ run_cmd 'git checkout master'
15
+ run_cmd 'git pull origin master'
16
+ run_cmd "git pull . #{branch}"
17
+ run_cmd 'git push origin HEAD'
18
+ run_cmd "grb rm #{branch}"
19
+
20
+ run_cmd "git integrate"
data/bin/git-wtf ADDED
@@ -0,0 +1,364 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ HELP = <<EOS
4
+ git-wtf displays the state of your repository in a readable, easy-to-scan
5
+ format. It's useful for getting a summary of how a branch relates to a remote
6
+ server, and for wrangling many topic branches.
7
+
8
+ git-wtf can show you:
9
+ - How a branch relates to the remote repo, if it's a tracking branch.
10
+ - How a branch relates to integration branches, if it's a feature branch.
11
+ - How a branch relates to the feature branches, if it's an integration
12
+ branch.
13
+
14
+ git-wtf is best used before a git push, or between a git fetch and a git
15
+ merge. Be sure to set color.ui to auto or yes for maximum viewing pleasure.
16
+ EOS
17
+
18
+ KEY = <<EOS
19
+ KEY:
20
+ () branch only exists locally
21
+ {} branch only exists on a remote repo
22
+ [] branch exists locally and remotely
23
+
24
+ x merge occurs both locally and remotely
25
+ ~ merge occurs only locally
26
+ (space) branch isn't merged in
27
+
28
+ (It's possible for merges to occur remotely and not locally, of course, but
29
+ that's a less common case and git-wtf currently doesn't display anything
30
+ special for it.)
31
+ EOS
32
+
33
+ USAGE = <<EOS
34
+ Usage: git wtf [branch+] [options]
35
+
36
+ If [branch] is not specified, git-wtf will use the current branch. The possible
37
+ [options] are:
38
+
39
+ -l, --long include author info and date for each commit
40
+ -a, --all show all branches across all remote repos, not just
41
+ those from origin
42
+ -A, --all-commits show all commits, not just the first 5
43
+ -s, --short don't show commits
44
+ -k, --key show key
45
+ -r, --relations show relation to features / integration branches
46
+ --dump-config print out current configuration and exit
47
+
48
+ git-wtf uses some heuristics to determine which branches are integration
49
+ branches, and which are feature branches. (Specifically, it assumes the
50
+ integration branches are named "master", "next" and "edge".) If it guesses
51
+ incorrectly, you will have to create a .git-wtfrc file.
52
+
53
+ To start building a configuration file, run "git-wtf --dump-config >
54
+ .git-wtfrc" and edit it. The config file is a YAML file that specifies the
55
+ integration branches, any branches to ignore, and the max number of commits to
56
+ display when --all-commits isn't used. git-wtf will look for a .git-wtfrc file
57
+ starting in the current directory, and recursively up to the root.
58
+
59
+ IMPORTANT NOTE: all local branches referenced in .git-wtfrc must be prefixed
60
+ with heads/, e.g. "heads/master". Remote branches must be of the form
61
+ remotes/<remote>/<branch>.
62
+ EOS
63
+
64
+ COPYRIGHT = <<EOS
65
+ git-wtf Copyright 2008--2009 William Morgan <wmorgan at the masanjin dot nets>.
66
+ This program is free software: you can redistribute it and/or modify it
67
+ under the terms of the GNU General Public License as published by the Free
68
+ Software Foundation, either version 3 of the License, or (at your option)
69
+ any later version.
70
+
71
+ This program is distributed in the hope that it will be useful, but WITHOUT
72
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
73
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
74
+ more details.
75
+
76
+ You can find the GNU General Public License at: http://www.gnu.org/licenses/
77
+ EOS
78
+
79
+ require 'yaml'
80
+ CONFIG_FN = ".git-wtfrc"
81
+
82
+ class Numeric; def pluralize s; "#{to_s} #{s}" + (self != 1 ? "s" : "") end end
83
+
84
+ if ARGV.delete("--help") || ARGV.delete("-h")
85
+ puts USAGE
86
+ exit
87
+ end
88
+
89
+ ## poor man's trollop
90
+ $long = ARGV.delete("--long") || ARGV.delete("-l")
91
+ $short = ARGV.delete("--short") || ARGV.delete("-s")
92
+ $all = ARGV.delete("--all") || ARGV.delete("-a")
93
+ $all_commits = ARGV.delete("--all-commits") || ARGV.delete("-A")
94
+ $dump_config = ARGV.delete("--dump-config")
95
+ $key = ARGV.delete("--key") || ARGV.delete("-k")
96
+ $show_relations = ARGV.delete("--relations") || ARGV.delete("-r")
97
+ ARGV.each { |a| abort "Error: unknown argument #{a}." if a =~ /^--/ }
98
+
99
+ ## search up the path for a file
100
+ def find_file fn
101
+ while true
102
+ return fn if File.exist? fn
103
+ fn2 = File.join("..", fn)
104
+ return nil if File.expand_path(fn2) == File.expand_path(fn)
105
+ fn = fn2
106
+ end
107
+ end
108
+
109
+ want_color = `git config color.wtf`
110
+ want_color = `git config color.ui` if want_color.empty?
111
+ $color = case want_color.chomp
112
+ when "true"; true
113
+ when "auto"; $stdout.tty?
114
+ end
115
+
116
+ def red s; $color ? "\033[31m#{s}\033[0m" : s end
117
+ def green s; $color ? "\033[32m#{s}\033[0m" : s end
118
+ def yellow s; $color ? "\033[33m#{s}\033[0m" : s end
119
+ def cyan s; $color ? "\033[36m#{s}\033[0m" : s end
120
+ def grey s; $color ? "\033[1;30m#{s}\033[0m" : s end
121
+ def purple s; $color ? "\033[35m#{s}\033[0m" : s end
122
+
123
+ ## the set of commits in 'to' that aren't in 'from'.
124
+ ## if empty, 'to' has been merged into 'from'.
125
+ def commits_between from, to
126
+ if $long
127
+ `git log --pretty=format:"- %s [#{yellow "%h"}] (#{purple "%ae"}; %ar)" #{from}..#{to}`
128
+ else
129
+ `git log --pretty=format:"- %s [#{yellow "%h"}]" #{from}..#{to}`
130
+ end.split(/[\r\n]+/)
131
+ end
132
+
133
+ def show_commits commits, prefix=" "
134
+ if commits.empty?
135
+ puts "#{prefix} none"
136
+ else
137
+ max = $all_commits ? commits.size : $config["max_commits"]
138
+ max -= 1 if max == commits.size - 1 # never show "and 1 more"
139
+ commits[0 ... max].each { |c| puts "#{prefix}#{c}" }
140
+ puts grey("#{prefix}... and #{commits.size - max} more (use -A to see all).") if commits.size > max
141
+ end
142
+ end
143
+
144
+ def ahead_behind_string ahead, behind
145
+ [ahead.empty? ? nil : "#{ahead.size.pluralize 'commit'} ahead",
146
+ behind.empty? ? nil : "#{behind.size.pluralize 'commit'} behind"].
147
+ compact.join("; ")
148
+ end
149
+
150
+ def widget merged_in, remote_only=false, local_only=false, local_only_merge=false
151
+ left, right = case
152
+ when remote_only; %w({ })
153
+ when local_only; %w{( )}
154
+ else %w([ ])
155
+ end
156
+ middle = case
157
+ when merged_in && local_only_merge; green("~")
158
+ when merged_in; green("x")
159
+ else " "
160
+ end
161
+ print left, middle, right
162
+ end
163
+
164
+ def show b
165
+ have_both = b[:local_branch] && b[:remote_branch]
166
+
167
+ pushc, pullc, oosync = if have_both
168
+ [x = commits_between(b[:remote_branch], b[:local_branch]),
169
+ y = commits_between(b[:local_branch], b[:remote_branch]),
170
+ !x.empty? && !y.empty?]
171
+ end
172
+
173
+ if b[:local_branch]
174
+ puts "Local branch: " + green(b[:local_branch].sub(/^heads\//, ""))
175
+
176
+ if have_both
177
+ if pushc.empty?
178
+ puts "#{widget true} in sync with remote"
179
+ else
180
+ action = oosync ? "push after rebase / merge" : "push"
181
+ puts "#{widget false} NOT in sync with remote (you should #{action})"
182
+ show_commits pushc unless $short
183
+ end
184
+ end
185
+ end
186
+
187
+ if b[:remote_branch]
188
+ puts "Remote branch: #{cyan b[:remote_branch]} (#{b[:remote_url]})"
189
+
190
+ if have_both
191
+ if pullc.empty?
192
+ puts "#{widget true} in sync with local"
193
+ else
194
+ action = pushc.empty? ? "merge" : "rebase / merge"
195
+ puts "#{widget false} NOT in sync with local (you should #{action})"
196
+ show_commits pullc unless $short
197
+ end
198
+ end
199
+ end
200
+
201
+ puts "\n#{red "WARNING"}: local and remote branches have diverged. A merge will occur unless you rebase." if oosync
202
+ end
203
+
204
+ def show_relations b, all_branches
205
+ ibs, fbs = all_branches.partition { |name, br| $config["integration-branches"].include?(br[:local_branch]) || $config["integration-branches"].include?(br[:remote_branch]) }
206
+ if $config["integration-branches"].include? b[:local_branch]
207
+ puts "\nFeature branches:" unless fbs.empty?
208
+ fbs.each do |name, br|
209
+ next if $config["ignore"].member?(br[:local_branch]) || $config["ignore"].member?(br[:remote_branch])
210
+ next if br[:ignore]
211
+ local_only = br[:remote_branch].nil?
212
+ remote_only = br[:local_branch].nil?
213
+ name = if local_only
214
+ purple br[:name]
215
+ elsif remote_only
216
+ cyan br[:name]
217
+ else
218
+ green br[:name]
219
+ end
220
+
221
+ ## for remote_only branches, we'll compute wrt the remote branch head. otherwise, we'll
222
+ ## use the local branch head.
223
+ head = remote_only ? br[:remote_branch] : br[:local_branch]
224
+
225
+ remote_ahead = b[:remote_branch] ? commits_between(b[:remote_branch], head) : []
226
+ local_ahead = b[:local_branch] ? commits_between(b[:local_branch], head) : []
227
+
228
+ if local_ahead.empty? && remote_ahead.empty?
229
+ puts "#{widget true, remote_only, local_only} #{name} #{local_only ? "(local-only) " : ""}is merged in"
230
+ elsif local_ahead.empty?
231
+ puts "#{widget true, remote_only, local_only, true} #{name} merged in (only locally)"
232
+ else
233
+ behind = commits_between head, (br[:local_branch] || br[:remote_branch])
234
+ ahead = remote_only ? remote_ahead : local_ahead
235
+ puts "#{widget false, remote_only, local_only} #{name} #{local_only ? "(local-only) " : ""}is NOT merged in (#{ahead_behind_string ahead, behind})"
236
+ show_commits ahead unless $short
237
+ end
238
+ end
239
+ else
240
+ puts "\nIntegration branches:" unless ibs.empty? # unlikely
241
+ ibs.sort_by { |v, br| v }.each do |v, br|
242
+ next if $config["ignore"].member?(br[:local_branch]) || $config["ignore"].member?(br[:remote_branch])
243
+ next if br[:ignore]
244
+ local_only = br[:remote_branch].nil?
245
+ remote_only = br[:local_branch].nil?
246
+ name = remote_only ? cyan(br[:name]) : green(br[:name])
247
+
248
+ ahead = commits_between v, (b[:local_branch] || b[:remote_branch])
249
+ if ahead.empty?
250
+ puts "#{widget true, local_only} merged into #{name}"
251
+ else
252
+ #behind = commits_between b[:local_branch], v
253
+ puts "#{widget false, local_only} NOT merged into #{name} (#{ahead.size.pluralize 'commit'} ahead)"
254
+ show_commits ahead unless $short
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ #### EXECUTION STARTS HERE ####
261
+
262
+ ## find config file and load it
263
+ $config = { "integration-branches" => %w(heads/master heads/next heads/edge), "ignore" => [], "max_commits" => 5 }.merge begin
264
+ fn = find_file CONFIG_FN
265
+ if fn && (h = YAML::load_file(fn)) # yaml turns empty files into false
266
+ h["integration-branches"] ||= h["versions"] # support old nomenclature
267
+ h
268
+ else
269
+ {}
270
+ end
271
+ end
272
+
273
+ if $dump_config
274
+ puts $config.to_yaml
275
+ exit
276
+ end
277
+
278
+ ## first, index registered remotes
279
+ remotes = `git config --get-regexp ^remote\.\*\.url`.split(/[\r\n]+/).inject({}) do |hash, l|
280
+ l =~ /^remote\.(.+?)\.url (.+)$/ or next hash
281
+ hash[$1] ||= $2
282
+ hash
283
+ end
284
+
285
+ ## next, index followed branches
286
+ branches = `git config --get-regexp ^branch\.`.split(/[\r\n]+/).inject({}) do |hash, l|
287
+ case l
288
+ when /branch\.(.*?)\.remote (.+)/
289
+ name, remote = $1, $2
290
+
291
+ hash[name] ||= {}
292
+ hash[name].merge! :remote => remote, :remote_url => remotes[remote]
293
+ when /branch\.(.*?)\.merge ((refs\/)?heads\/)?(.+)/
294
+ name, remote_branch = $1, $4
295
+ hash[name] ||= {}
296
+ hash[name].merge! :remote_mergepoint => remote_branch
297
+ end
298
+ hash
299
+ end
300
+
301
+ ## finally, index all branches
302
+ remote_branches = {}
303
+ `git show-ref`.split(/[\r\n]+/).each do |l|
304
+ sha1, ref = l.chomp.split " refs/"
305
+
306
+ if ref =~ /^heads\/(.+)$/ # local branch
307
+ name = $1
308
+ next if name == "HEAD"
309
+ branches[name] ||= {}
310
+ branches[name].merge! :name => name, :local_branch => ref
311
+ elsif ref =~ /^remotes\/(.+?)\/(.+)$/ # remote branch
312
+ remote, name = $1, $2
313
+ remote_branches["#{remote}/#{name}"] = true
314
+ next if name == "HEAD"
315
+ ignore = !($all || remote == "origin")
316
+
317
+ branch = name
318
+ if branches[name] && branches[name][:remote] == remote
319
+ # nothing
320
+ else
321
+ name = "#{remote}/#{branch}"
322
+ end
323
+
324
+ branches[name] ||= {}
325
+ branches[name].merge! :name => name, :remote => remote, :remote_branch => "#{remote}/#{branch}", :remote_url => remotes[remote], :ignore => ignore
326
+ end
327
+ end
328
+
329
+ ## assemble remotes
330
+ branches.each do |k, b|
331
+ next unless b[:remote] && b[:remote_mergepoint]
332
+ b[:remote_branch] = if b[:remote] == "."
333
+ b[:remote_mergepoint]
334
+ else
335
+ t = "#{b[:remote]}/#{b[:remote_mergepoint]}"
336
+ remote_branches[t] && t # only if it's still alive
337
+ end
338
+ end
339
+
340
+ show_dirty = ARGV.empty?
341
+ targets = if ARGV.empty?
342
+ [`git symbolic-ref HEAD`.chomp.sub(/^refs\/heads\//, "")]
343
+ else
344
+ ARGV.map { |x| x.sub(/^heads\//, "") }
345
+ end.map { |t| branches[t] or abort "Error: can't find branch #{t.inspect}." }
346
+
347
+ targets.each do |t|
348
+ show t
349
+ show_relations t, branches if $show_relations || t[:remote_branch].nil?
350
+ end
351
+
352
+ modified = show_dirty && `git ls-files -m` != ""
353
+ uncommitted = show_dirty && `git diff-index --cached HEAD` != ""
354
+
355
+ if $key
356
+ puts
357
+ puts KEY
358
+ end
359
+
360
+ puts if modified || uncommitted
361
+ puts "#{red "NOTE"}: working directory contains modified files." if modified
362
+ puts "#{red "NOTE"}: staging area contains staged but uncommitted files." if uncommitted
363
+
364
+ # the end!
@@ -0,0 +1,87 @@
1
+ require 'jira4r'
2
+ require 'activesupport'
3
+
4
+ module Socialcast
5
+ GIT_BRANCH_FIELD = 'customfield_10010'
6
+ IN_STAGING_FIELD = 'customfield_10020'
7
+ JIRA_CREDENTIALS_FILE = File.expand_path('~/.jira_key')
8
+
9
+ def jira_credentials
10
+ @credentials ||= YAML.load_file(JIRA_CREDENTIALS_FILE).symbolize_keys!
11
+ @credentials
12
+ end
13
+ def jira_server
14
+ #make sure soap4r is installed
15
+ require 'jira4r'
16
+ require "highline/import.rb"
17
+
18
+ return @jira if @jira
19
+ if !File.exists?(JIRA_CREDENTIALS_FILE)
20
+ input = {}
21
+ input[:username] = HighLine.ask("JIRA username: ")
22
+ input[:password] = HighLine.ask("JIRA password: ") { |q| q.echo = "*" }
23
+
24
+ File.open(JIRA_CREDENTIALS_FILE, "w") do |f|
25
+ f.write input.to_yaml
26
+ end
27
+ end
28
+ File.chmod 0600, JIRA_CREDENTIALS_FILE
29
+ credentials = jira_credentials
30
+
31
+ begin
32
+ @jira = Jira4R::JiraTool.new 2, "https://issues.socialcast.com"
33
+ @jira.login credentials[:username], credentials[:password]
34
+ return @jira
35
+ rescue => e
36
+ puts "Error: #{e.message}"
37
+ File.delete config_file
38
+ raise e
39
+ end
40
+ end
41
+
42
+ def update_ticket(ticket, options = {})
43
+ fields = []
44
+ fields << Jira4R::V2::RemoteFieldValue.new(GIT_BRANCH_FIELD, [options[:branch]]) if options[:branch]
45
+ fields << Jira4R::V2::RemoteFieldValue.new(IN_STAGING_FIELD, ['true']) if options[:in_staging]
46
+ jira_server.updateIssue ticket, fields
47
+ end
48
+ def start_ticket(ticket)
49
+ issue = jira_server.getIssue ticket
50
+ if issue.status == '1'
51
+ puts "Transitioning ticket from 'Open' to 'In Progress'"
52
+ start_work_action = '11'
53
+ jira_server.progressWorkflowAction ticket, start_work_action, []
54
+ end
55
+ end
56
+ def resolve_ticket(ticket)
57
+ issue = jira_server.getIssue ticket
58
+ if issue.status == '3'
59
+ puts 'Transitioning ticket from "In Progress" to "Resolved"'
60
+ finish_work_action = '21'
61
+ jira_server.progressWorkflowAction ticket, finish_work_action, []
62
+ end
63
+ end
64
+
65
+ def run_cmd(cmd)
66
+ puts "\nRunning: #{cmd}"
67
+ raise "#{cmd} failed" unless system cmd
68
+ end
69
+
70
+ def update(branch)
71
+ puts "updating #{branch} to have most recent changes from master"
72
+ run_cmd "git pull origin #{branch}" rescue nil
73
+ run_cmd 'git pull origin master'
74
+ run_cmd 'git push origin HEAD'
75
+ end
76
+ def integrate(branch, destination_branch = 'staging')
77
+ puts "integrating #{branch} into #{destination_branch}"
78
+ run_cmd "git remote prune origin"
79
+ run_cmd "git branch -D #{destination_branch}" rescue nil
80
+ run_cmd "grb track #{destination_branch}"
81
+ run_cmd "git checkout #{destination_branch}"
82
+ run_cmd "git pull . #{branch}"
83
+ run_cmd "git push origin HEAD"
84
+
85
+ run_cmd "git checkout #{branch}"
86
+ end
87
+ end
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{socialcast-git-extensions}
8
+ s.version = "0.4.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ryan Sonnek"]
12
+ s.date = %q{2010-07-02}
13
+ s.description = %q{git extension scripts for socialcast workflow}
14
+ s.email = %q{ryan@socialcast.com}
15
+ s.executables = ["git-integrate", "git-promote", "git-release", "git-wtf"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bin/git-integrate",
28
+ "bin/git-promote",
29
+ "bin/git-release",
30
+ "bin/git-wtf",
31
+ "lib/socialcast-git-extensions.rb",
32
+ "socialcast-git-extensions.gemspec",
33
+ "test/helper.rb",
34
+ "test/test_socialcast-git-extensions.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/wireframe/socialcast-git-extensions}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.7}
40
+ s.summary = %q{git extension scripts for socialcast workflow}
41
+ s.test_files = [
42
+ "test/helper.rb",
43
+ "test/test_socialcast-git-extensions.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
52
+ s.add_runtime_dependency(%q<grit>, [">= 0"])
53
+ s.add_runtime_dependency(%q<jira4r>, [">= 0"])
54
+ s.add_runtime_dependency(%q<soap4r>, [">= 0"])
55
+ s.add_runtime_dependency(%q<activesupport>, [">= 2.3.5"])
56
+ else
57
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
58
+ s.add_dependency(%q<grit>, [">= 0"])
59
+ s.add_dependency(%q<jira4r>, [">= 0"])
60
+ s.add_dependency(%q<soap4r>, [">= 0"])
61
+ s.add_dependency(%q<activesupport>, [">= 2.3.5"])
62
+ end
63
+ else
64
+ s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
65
+ s.add_dependency(%q<grit>, [">= 0"])
66
+ s.add_dependency(%q<jira4r>, [">= 0"])
67
+ s.add_dependency(%q<soap4r>, [">= 0"])
68
+ s.add_dependency(%q<activesupport>, [">= 2.3.5"])
69
+ end
70
+ end
71
+
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'socialcast-git-extensions'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestSocialcastGitExtensions < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: socialcast-git-extensions
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
+ platform: ruby
12
+ authors:
13
+ - Ryan Sonnek
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-02 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: thoughtbot-shoulda
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: grit
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: jira4r
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ - !ruby/object:Gem::Dependency
64
+ name: soap4r
65
+ prerelease: false
66
+ requirement: &id004 !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ hash: 3
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ type: :runtime
76
+ version_requirements: *id004
77
+ - !ruby/object:Gem::Dependency
78
+ name: activesupport
79
+ prerelease: false
80
+ requirement: &id005 !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 9
86
+ segments:
87
+ - 2
88
+ - 3
89
+ - 5
90
+ version: 2.3.5
91
+ type: :runtime
92
+ version_requirements: *id005
93
+ description: git extension scripts for socialcast workflow
94
+ email: ryan@socialcast.com
95
+ executables:
96
+ - git-integrate
97
+ - git-promote
98
+ - git-release
99
+ - git-wtf
100
+ extensions: []
101
+
102
+ extra_rdoc_files:
103
+ - LICENSE
104
+ - README.rdoc
105
+ files:
106
+ - .document
107
+ - .gitignore
108
+ - LICENSE
109
+ - README.rdoc
110
+ - Rakefile
111
+ - VERSION
112
+ - bin/git-integrate
113
+ - bin/git-promote
114
+ - bin/git-release
115
+ - bin/git-wtf
116
+ - lib/socialcast-git-extensions.rb
117
+ - socialcast-git-extensions.gemspec
118
+ - test/helper.rb
119
+ - test/test_socialcast-git-extensions.rb
120
+ has_rdoc: true
121
+ homepage: http://github.com/wireframe/socialcast-git-extensions
122
+ licenses: []
123
+
124
+ post_install_message:
125
+ rdoc_options:
126
+ - --charset=UTF-8
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ hash: 3
135
+ segments:
136
+ - 0
137
+ version: "0"
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ none: false
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ hash: 3
144
+ segments:
145
+ - 0
146
+ version: "0"
147
+ requirements: []
148
+
149
+ rubyforge_project:
150
+ rubygems_version: 1.3.7
151
+ signing_key:
152
+ specification_version: 3
153
+ summary: git extension scripts for socialcast workflow
154
+ test_files:
155
+ - test/helper.rb
156
+ - test/test_socialcast-git-extensions.rb