gitcycle 0.3.3 → 0.3.4

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/gitcycle.rb CHANGED
@@ -15,12 +15,24 @@ require 'yajl'
15
15
  $:.unshift File.dirname(__FILE__)
16
16
 
17
17
  require "ext/string"
18
+ require "gitcycle/branch"
19
+ require "gitcycle/checkout"
20
+ require "gitcycle/commit"
21
+ require "gitcycle/discuss"
22
+ require "gitcycle/incident"
23
+ require "gitcycle/open"
24
+ require "gitcycle/pull"
25
+ require "gitcycle/push"
26
+ require "gitcycle/qa"
27
+ require "gitcycle/ready"
28
+ require "gitcycle/review"
29
+ require "gitcycle/setup"
18
30
 
19
31
  class Gitcycle
20
32
 
21
33
  API =
22
- if ENV['ENV'] == 'development'
23
- "http://127.0.0.1:8080/api"
34
+ if ENV['ENV'] == 'test'
35
+ "http://127.0.0.1:3000/api"
24
36
  else
25
37
  "http://gitcycle.bleacherreport.com/api"
26
38
  end
@@ -36,6 +48,19 @@ class Gitcycle
36
48
  :last_command_errored => 8,
37
49
  }
38
50
 
51
+ include Branch
52
+ include Checkout
53
+ include Commit
54
+ include Discuss
55
+ include Incident
56
+ include Open
57
+ include Pull
58
+ include Push
59
+ include QA
60
+ include Ready
61
+ include Review
62
+ include Setup
63
+
39
64
  def initialize(args=nil)
40
65
  $remotes = {}
41
66
 
@@ -51,524 +76,6 @@ class Gitcycle
51
76
  start(args) if args
52
77
  end
53
78
 
54
- def branch(*args)
55
- url = args.detect { |arg| arg =~ /^https?:\/\// }
56
- title = args.detect { |arg| arg =~ /\s/ }
57
-
58
- exec_git(:branch, args) unless url || title
59
-
60
- require_git && require_config
61
-
62
- params = {
63
- 'branch[source]' => branches(:current => true)
64
- }
65
-
66
- if url && url.include?('lighthouseapp.com/')
67
- params.merge!('branch[lighthouse_url]' => url)
68
- elsif url && url.include?('github.com/')
69
- params.merge!('branch[issue_url]' => url)
70
- elsif url
71
- puts "Gitcycle only supports Lighthouse or Github Issue URLs.".red
72
- exit ERROR[:unrecognized_url]
73
- elsif title
74
- params.merge!(
75
- 'branch[name]' => title,
76
- 'branch[title]' => title
77
- )
78
- else
79
- exec_git(:branch, args)
80
- end
81
-
82
- unless yes?("\nYour work will eventually merge into '#{params['branch[source]']}'. Is this correct?")
83
- params['branch[source]'] = q("What branch would you like to eventually merge into?")
84
- end
85
-
86
- source = params['branch[source]']
87
-
88
- puts "\nRetrieving branch information from gitcycle.\n".green
89
- branch = get('branch', params)
90
- name = branch['name']
91
-
92
- begin
93
- owner, repo = branch['repo'].split(':')
94
- branch['home'] ||= @git_login
95
-
96
- unless yes?("Would you like to name your branch '#{name}'?")
97
- name = q("\nWhat would you like to name your branch?")
98
- name = name.gsub(/[\s\W]/, '-')
99
- end
100
-
101
- checkout_remote_branch(
102
- :owner => owner,
103
- :repo => repo,
104
- :branch => branch['source'],
105
- :target => name
106
- )
107
-
108
- puts "Sending branch information to gitcycle.".green
109
- get('branch',
110
- 'branch[home]' => branch['home'],
111
- 'branch[name]' => branch['name'],
112
- 'branch[rename]' => name != branch['name'] ? name : nil,
113
- 'branch[source]' => branch['source']
114
- )
115
- rescue SystemExit, Interrupt
116
- puts "\nDeleting branch from gitcycle.\n".green
117
- get('branch',
118
- 'branch[name]' => branch['name'],
119
- 'create' => 0,
120
- 'reset' => 1
121
- )
122
- end
123
-
124
- puts "\n"
125
- end
126
-
127
- def checkout(*args)
128
- if args.length != 1 || options?(args)
129
- exec_git(:checkout, args)
130
- end
131
-
132
- require_git && require_config
133
-
134
- if args[0] =~ /^https?:\/\//
135
- puts "\nRetrieving branch information from gitcycle.\n".green
136
- branch = get('branch', 'branch[lighthouse_url]' => args[0], 'create' => 0)
137
- if branch
138
- checkout_or_track(:name => branch['name'], :remote => 'origin')
139
- else
140
- puts "\nBranch not found!\n".red
141
- puts "\nDid you mean: gitc branch #{args[0]}\n".yellow
142
- end
143
- elsif args[0] =~ /^\d*$/
144
- puts "\nLooking for a branch for LH ticket ##{args[0]}.\n".green
145
- results = branches(:array => true).select {|b| b.include?("-#{args[0]}-") }
146
- if results.size == 0
147
- puts "\nNo matches for ticket ##{args[0]} found.\n".red
148
- elsif results.size == 1
149
- branch = results.first
150
- if branch.strip == branches(:current => true).strip
151
- puts "Already on Github branch for LH ticket ##{args[0]} (#{branch})".yellow
152
- else
153
- puts "\nSwitching to branch '#{branch}'\n".green
154
- run("git checkout #{branch}")
155
- end
156
- else
157
- puts "\nFound #{results.size} matches with that LH ticket number:\n".yellow
158
- puts results
159
- puts "\nDid not switch branches. Please check your ticket number.\n".red
160
- end
161
- else
162
- remote, branch = args[0].split('/')
163
- remote, branch = nil, remote if branch.nil?
164
- collab = branch && remote
165
-
166
- unless branches(:match => branch)
167
- og_remote = nil
168
-
169
- puts "\nRetrieving repo information from gitcycle.\n".green
170
- repo = get('repo')
171
- remote = repo['owner'] unless collab
172
-
173
- output = add_remote_and_fetch(
174
- :catch => false,
175
- :owner => remote,
176
- :repo => @git_repo
177
- )
178
-
179
- if errored?(output)
180
- og_remote = remote
181
- remote = repo["owner"]
182
-
183
- add_remote_and_fetch(
184
- :owner => remote,
185
- :repo => @git_repo
186
- )
187
- end
188
-
189
- puts "Creating branch '#{branch}' from '#{remote}/#{branch}'.\n".green
190
- output = run("git branch --no-track #{branch} #{remote}/#{branch}", :catch => false)
191
-
192
- if errored?(output)
193
- puts "Could not find branch #{"'#{og_remote}/#{branch}' or " if og_remote}'#{remote}/#{branch}'.\n".red
194
- exit ERROR[:could_not_find_branch]
195
- end
196
- end
197
-
198
- if collab
199
- puts "Sending branch information to gitcycle.".green
200
- get('branch',
201
- 'branch[home]' => remote,
202
- 'branch[name]' => branch,
203
- 'branch[source]' => branch,
204
- 'branch[collab]' => 1,
205
- 'create' => 1
206
- )
207
- end
208
-
209
- puts "Checking out '#{branch}'.\n".green
210
- run("git checkout -q #{branch}")
211
- end
212
- end
213
- alias :co :checkout
214
-
215
- def commit(*args)
216
- msg = nil
217
- no_add = args.delete("--no-add")
218
-
219
- if args.empty?
220
- require_git && require_config
221
-
222
- puts "\nRetrieving branch information from gitcycle.\n".green
223
- branch = get('branch',
224
- 'branch[name]' => branches(:current => true),
225
- 'create' => 0
226
- )
227
-
228
- id = branch["lighthouse_url"].match(/tickets\/(\d+)/)[1] rescue nil
229
-
230
- if branch && id
231
- msg = "[##{id}]"
232
- msg += " #{branch["title"]}" if branch["title"]
233
- end
234
- end
235
-
236
- if no_add
237
- cmd = "git commit"
238
- else
239
- cmd = "git add . && git add . -u && git commit -a"
240
- end
241
-
242
- if File.exists?("#{Dir.pwd}/.git/MERGE_HEAD")
243
- Kernel.exec(cmd)
244
- elsif msg
245
- run(cmd + " -m #{msg.dump.gsub('`', "'")}")
246
- Kernel.exec("git commit --amend")
247
- elsif args.empty?
248
- Kernel.exec(cmd)
249
- else
250
- exec_git(:commit, args)
251
- end
252
- end
253
- alias :ci :commit
254
-
255
- def discuss(*issues)
256
- require_git && require_config
257
-
258
- if issues.empty?
259
- branch = create_pull_request
260
-
261
- if branch == false
262
- puts "Branch not found.\n".red
263
- elsif branch['issue_url']
264
- puts "\nLabeling issue as 'Discuss'.\n".green
265
- get('label',
266
- 'branch[name]' => branch['name'],
267
- 'labels' => [ 'Discuss' ]
268
- )
269
-
270
- puts "Opening issue: #{branch['issue_url']}\n".green
271
- Launchy.open(branch['issue_url'])
272
- else
273
- puts "You must push code before opening a pull request.\n".red
274
- end
275
- else
276
- puts "\nRetrieving branch information from gitcycle.\n".green
277
-
278
- get('branch', 'issues' => issues, 'scope' => 'repo').each do |branch|
279
- if branch['issue_url']
280
- puts "Opening issue: #{branch['issue_url']}\n".green
281
- Launchy.open(branch['issue_url'])
282
- end
283
- end
284
- end
285
- end
286
-
287
- def pull(*args)
288
- exec_git(:pull, args) if args.length > 0
289
-
290
- require_git && require_config
291
-
292
- current_branch = branches(:current => true)
293
-
294
- puts "\nRetrieving branch information from gitcycle.\n".green
295
- branch = get('branch',
296
- 'branch[name]' => current_branch,
297
- 'include' => [ 'repo' ],
298
- 'create' => 0
299
- )
300
-
301
- if branch && branch['collab']
302
- # Merge from collab
303
- merge_remote_branch(
304
- :owner => owner = branch['home'],
305
- :repo => branch['repo']['name'],
306
- :branch => branch['source']
307
- )
308
- elsif branch
309
- # Merge from upstream source branch
310
- merge_remote_branch(
311
- :owner => owner = branch['repo']['owner'],
312
- :repo => branch['repo']['name'],
313
- :branch => branch['source']
314
- )
315
- else
316
- puts "\nRetrieving repo information from gitcycle.\n".green
317
- repo = get('repo')
318
-
319
- # Merge from upstream branch with same name
320
- merge_remote_branch(
321
- :owner => owner = repo['owner'],
322
- :repo => repo['name'],
323
- :branch => current_branch
324
- )
325
- end
326
-
327
- unless branch && branch['collab'] || owner == @git_login
328
- # Merge from origin
329
- merge_remote_branch(
330
- :owner => @git_login,
331
- :repo => @git_repo,
332
- :branch => current_branch
333
- )
334
- end
335
-
336
- branch
337
- end
338
-
339
- def push(*args)
340
- exec_git(:push, args) if args.length > 0
341
-
342
- require_git && require_config
343
-
344
- branch = pull
345
-
346
- if branch && branch['collab']
347
- puts "\nPushing branch '#{branch['home']}/#{branch['name']}'.\n".green
348
- run("git push #{branch['home']} #{branch['name']} -q")
349
- elsif branch
350
- puts "\nPushing branch 'origin/#{branch['name']}'.\n".green
351
- run("git push origin #{branch['name']} -q")
352
- else
353
- current_branch = branches(:current => true)
354
- puts "\nPushing branch 'origin/#{current_branch}'.\n".green
355
- run("git push origin #{current_branch} -q")
356
- end
357
- end
358
-
359
- def qa(*issues)
360
- require_git && require_config
361
-
362
- if issues.empty?
363
- puts "\n"
364
- get('qa_branch').each do |branches|
365
- puts "qa_#{branches['source']}_#{branches['user']}".green
366
- branches['branches'].each do |branch|
367
- puts " #{"issue ##{branch['issue']}".yellow}\t#{branch['user']}/#{branch['branch']}"
368
- end
369
- puts "\n"
370
- end
371
- elsif issues.first == 'fail' || issues.first == 'pass'
372
- branch = branches(:current => true)
373
- pass_fail = issues.first
374
- label = pass_fail.capitalize
375
- issues = issues[1..-1]
376
-
377
- if pass_fail == 'pass' && !issues.empty?
378
- puts "\nWARNING: #{
379
- issues.length == 1 ? "This issue" : "These issues"
380
- } will merge straight into '#{branch}' without testing.\n".red
381
-
382
- if yes?("Continue?")
383
- qa_branch = create_qa_branch(
384
- :instructions => false,
385
- :issues => issues,
386
- :source => branch
387
- )
388
- `git checkout qa_#{qa_branch['source']}_#{qa_branch['user']} -q`
389
- $remotes = {}
390
- qa('pass')
391
- else
392
- exit ERROR[:told_not_to_merge]
393
- end
394
- elsif branch =~ /^qa_/
395
- puts "\nRetrieving branch information from gitcycle.\n".green
396
- qa_branch = get('qa_branch', :source => branch.gsub(/^qa_/, ''))
397
-
398
- if pass_fail == 'pass'
399
- checkout_or_track(:name => qa_branch['source'], :remote => 'origin')
400
- end
401
-
402
- if issues.empty?
403
- branches = qa_branch['branches']
404
- else
405
- branches = qa_branch['branches'].select do |b|
406
- issues.include?(b['issue'])
407
- end
408
- end
409
-
410
- if pass_fail == 'pass' && issues.empty?
411
- owner, repo = qa_branch['repo'].split(':')
412
- merge_remote_branch(
413
- :owner => owner,
414
- :repo => repo,
415
- :branch => "qa_#{qa_branch['source']}_#{qa_branch['user']}",
416
- :type => :from_qa
417
- )
418
- end
419
-
420
- unless issues.empty?
421
- branches.each do |branch|
422
- puts "\nLabeling issue #{branch['issue']} as '#{label}'.\n".green
423
- get('label',
424
- 'qa_branch[source]' => qa_branch['source'],
425
- 'issue' => branch['issue'],
426
- 'labels' => [ label ]
427
- )
428
- end
429
- end
430
-
431
- if issues.empty?
432
- puts "\nLabeling all issues as '#{label}'.\n".green
433
- get('label',
434
- 'qa_branch[source]' => qa_branch['source'],
435
- 'labels' => [ label ]
436
- )
437
- end
438
- else
439
- puts "\nYou are not in a QA branch.\n".red
440
- end
441
- elsif issues.first == 'resolved'
442
- branch = branches(:current => true)
443
-
444
- if branch =~ /^qa_/
445
- puts "\nRetrieving branch information from gitcycle.\n".green
446
- qa_branch = get('qa_branch', :source => branch.gsub(/^qa_/, ''))
447
-
448
- branches = qa_branch['branches']
449
- conflict = branches.detect { |branch| branch['conflict'] }
450
-
451
- if qa_branch && conflict
452
- puts "Committing merge resolution of #{conflict['branch']} (issue ##{conflict['issue']}).\n".green
453
- run("git add . && git add . -u && git commit -a -F .git/MERGE_MSG")
454
-
455
- puts "Pushing merge resolution of #{conflict['branch']} (issue ##{conflict['issue']}).\n".green
456
- run("git push origin qa_#{qa_branch['source']}_#{qa_branch['user']} -q")
457
-
458
- puts "\nDe-conflicting on gitcycle.\n".green
459
- get('qa_branch',
460
- 'issues' => branches.collect { |branch| branch['issue'] }
461
- )
462
-
463
- create_qa_branch(
464
- :preserve => true,
465
- :range => (branches.index(conflict)+1..-1),
466
- :qa_branch => qa_branch
467
- )
468
- else
469
- puts "Couldn't find record of a conflicted merge.\n".red
470
- end
471
- else
472
- puts "\nYou aren't on a QA branch.\n".red
473
- end
474
- else
475
- create_qa_branch(:issues => issues)
476
- end
477
- end
478
-
479
- def ready(*issues)
480
- require_git && require_config
481
-
482
- branch = pull
483
-
484
- if branch && !branch['collab']
485
- # Recreate pull request if force == true
486
- force = branch['labels'] && branch['labels'].include?('Pass')
487
- force ||= branch['state'] && branch['state'] == 'closed'
488
-
489
- branch = create_pull_request(branch, force)
490
- end
491
-
492
- if branch == false
493
- puts "Branch not found.\n".red
494
- elsif branch['collab']
495
- remote, branch = branch['home'], branch['source']
496
- puts "\nPushing branch '#{remote}/#{branch}'.\n".green
497
- run("git push #{remote} #{branch} -q")
498
- elsif branch['issue_url']
499
- puts "\nLabeling issue as 'Pending Review'.\n".green
500
- get('label',
501
- 'branch[name]' => branch['name'],
502
- 'labels' => [ 'Pending Review' ]
503
- )
504
-
505
- puts "Opening issue: #{branch['issue_url']}\n".green
506
- Launchy.open(branch['issue_url'])
507
- else
508
- puts "You have not pushed any commits to '#{branch['name']}'.\n".red
509
- end
510
- end
511
-
512
- def review(pass_fail, *issues)
513
- require_git && require_config
514
-
515
- if pass_fail == 'fail'
516
- label = 'Fail'
517
- else
518
- label = 'Pending QA'
519
- end
520
-
521
- if issues.empty?
522
- puts "\nLabeling issue as '#{label}'.\n".green
523
- get('label',
524
- 'branch[name]' => branches(:current => true),
525
- 'labels' => [ label ]
526
- )
527
- else
528
- puts "\nLabeling issues as '#{label}'.\n".green
529
- get('label',
530
- 'issues' => issues,
531
- 'labels' => [ label ],
532
- 'scope' => 'repo'
533
- )
534
- end
535
- end
536
-
537
- def open(*issues)
538
- require_git && require_config
539
-
540
- if issues.empty?
541
- branch = create_pull_request
542
-
543
- if branch == false
544
- puts "Branch not found.\n".red
545
- elsif branch['issue_url']
546
- puts "\nOpening the pull request in GitHub\n".green
547
-
548
- puts "Opening issue: #{branch['issue_url']}\n".green
549
- Launchy.open(branch['issue_url'])
550
- else
551
- puts "You must push code before opening a pull request.\n".red
552
- end
553
- else
554
- puts "\nRetrieving branch information from gitcycle.\n".green
555
-
556
- get('branch', 'issues' => issues, 'scope' => 'repo').each do |branch|
557
- if branch['issue_url']
558
- puts "Opening issue: #{branch['issue_url']}\n".green
559
- Launchy.open(branch['issue_url'])
560
- end
561
- end
562
- end
563
- end
564
-
565
- def setup(login, repo, token)
566
- repo = "#{login}/#{repo}" unless repo.include?('/')
567
- @config[repo] = [ login, token ]
568
- save_config
569
- puts "\nConfiguration saved.\n".green
570
- end
571
-
572
79
  def start(args=[])
573
80
  command = args.shift
574
81
 
@@ -700,84 +207,6 @@ class Gitcycle
700
207
  branch
701
208
  end
702
209
 
703
- def create_qa_branch(options)
704
- instructions = options[:instructions]
705
- issues = options[:issues]
706
- range = options[:range] || (0..-1)
707
- source = options[:source]
708
-
709
- if (issues && !issues.empty?) || options[:qa_branch]
710
- if options[:qa_branch]
711
- qa_branch = options[:qa_branch]
712
- else
713
- unless source
714
- source = branches(:current => true)
715
-
716
- unless yes?("\nDo you want to create a QA branch from '#{source}'?")
717
- source = q("What branch would you like to base this QA branch off of?")
718
- end
719
- end
720
-
721
- puts "\nRetrieving branch information from gitcycle.\n".green
722
- qa_branch = get('qa_branch', 'issues' => issues, 'source' => source)
723
- end
724
-
725
- source = qa_branch['source']
726
- name = "qa_#{source}_#{qa_branch['user']}"
727
-
728
- unless qa_branch['branches'].empty?
729
- qa_branch['branches'][range].each do |branch|
730
- if source != branch['source']
731
- puts "You can only QA issues based on '#{source}'\n".red
732
- exit ERROR[:cannot_qa]
733
- end
734
- end
735
-
736
- unless options[:preserve]
737
- if branches(:match => name, :all => true)
738
- puts "Deleting old QA branch '#{name}'.\n".green
739
- if branches(:match => name)
740
- run("git checkout master -q")
741
- run("git branch -D #{name}")
742
- end
743
- run("git push origin :#{name} -q")
744
- end
745
-
746
- checkout_remote_branch(
747
- :owner => @git_login,
748
- :repo => @git_repo,
749
- :branch => source,
750
- :target => name
751
- )
752
-
753
- puts "\n"
754
- end
755
-
756
- qa_branch['branches'][range].each do |branch|
757
- issue = branch['issue']
758
- owner, repo = branch['repo'].split(':')
759
- home = branch['home']
760
-
761
- output = merge_remote_branch(
762
- :owner => home,
763
- :repo => repo,
764
- :branch => branch['branch'],
765
- :issue => issue,
766
- :issues => qa_branch['branches'].collect { |b| b['issue'] },
767
- :type => :to_qa
768
- )
769
- end
770
-
771
- unless options[:instructions] == false
772
- puts "\nType '".yellow + "gitc qa pass".green + "' to approve all issues in this branch.\n".yellow
773
- puts "Type '".yellow + "gitc qa fail".red + "' to reject all issues in this branch.\n".yellow
774
- end
775
- end
776
-
777
- qa_branch
778
- end
779
- end
780
-
781
210
  def errored?(output)
782
211
  output.include?("fatal: ") || output.include?("ERROR: ") || $?.exitstatus != 0
783
212
  end
@@ -821,7 +250,9 @@ class Gitcycle
821
250
  :uid => (0...20).map{ ('a'..'z').to_a[rand(26)] }.join
822
251
  )
823
252
 
824
- puts "\nTransaction ID: #{hash[:uid]}".green
253
+ hash[:test] = 1 if ENV['ENV'] == 'test'
254
+
255
+ puts "Transaction ID: #{hash[:uid]}".green
825
256
 
826
257
  params = ''
827
258
  hash[:session] = 0
@@ -858,14 +289,6 @@ class Gitcycle
858
289
  end
859
290
  end
860
291
 
861
- def load_config
862
- if File.exists?(@config_path)
863
- @config = YAML.load(File.read(@config_path))
864
- else
865
- @config = {}
866
- end
867
- end
868
-
869
292
  def git_config_path(path)
870
293
  config = "#{path}/.git/config"
871
294
  if File.exists?(config)
@@ -878,6 +301,14 @@ class Gitcycle
878
301
  end
879
302
  end
880
303
 
304
+ def load_config
305
+ if File.exists?(@config_path)
306
+ @config = YAML.load(File.read(@config_path))
307
+ else
308
+ @config = {}
309
+ end
310
+ end
311
+
881
312
  def load_git
882
313
  path = git_config_path(Dir.pwd)
883
314
  if path
@@ -906,6 +337,11 @@ class Gitcycle
906
337
  def options?(args)
907
338
  args.any? { |arg| arg =~ /^-/ }
908
339
  end
340
+
341
+ def q(question, extra='')
342
+ puts "#{question.yellow}#{extra}"
343
+ $input ? $input.shift : $stdin.gets.strip
344
+ end
909
345
 
910
346
  def remotes(options={})
911
347
  b = `git remote`
@@ -915,11 +351,6 @@ class Gitcycle
915
351
  b
916
352
  end
917
353
  end
918
-
919
- def q(question, extra='')
920
- puts "#{question.yellow}#{extra}"
921
- $input ? $input.shift : $stdin.gets.strip
922
- end
923
354
 
924
355
  def require_config
925
356
  unless @login && @token