git-issue 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -3,3 +3,4 @@ source "http://rubygems.org"
3
3
  # Specify your gem's dependencies in named_let.gemspec
4
4
  gem 'activesupport'
5
5
  gem 'pit'
6
+ gem 'term-ansicolor'
@@ -21,7 +21,7 @@ now available issue-tracker system is Redmine and Github-issues.
21
21
  $ gem install jeweler
22
22
  $ rake install
23
23
 
24
- ## Configuration
24
+ ## Configuration(Redmine)
25
25
 
26
26
  set type of issue traking system(redmine or github)
27
27
 
@@ -43,6 +43,16 @@ set your account name if using github.
43
43
 
44
44
  $ git config issue.user yuroyoro
45
45
 
46
+ ## Configuration(Github Issues)
47
+
48
+ set type of issue traking system(redmine or github)
49
+
50
+ $ git config issue.type github
51
+
52
+ set user and password of github(for authentication)
53
+
54
+ $ EDITOR=vim pit set github
55
+
46
56
  ## Usage(Redmine)
47
57
 
48
58
  git issue <command> [ticket_id] [<args>]
@@ -16,6 +16,7 @@ require 'optparse'
16
16
  require 'tempfile'
17
17
  require 'active_support/all'
18
18
  require 'shellwords'
19
+ require 'term/ansicolor'
19
20
 
20
21
  module GitIssue
21
22
  class Command
@@ -39,8 +40,9 @@ module GitIssue
39
40
 
40
41
 
41
42
  def configured_value(name, trim = true)
42
- res = `git config issue.#{name}`
43
- trim ? res.strip : res
43
+ res = `git config #{name}`
44
+ res = trim ? res.strip : res
45
+ res
44
46
  end
45
47
 
46
48
  def global_configured_value(name)
@@ -129,7 +131,7 @@ module GitIssue
129
131
  status = true
130
132
 
131
133
  begin
132
- its_type = Helper.configured_value('type')
134
+ its_type = Helper.configured_value('issue.type')
133
135
 
134
136
  # Use global config for hub
135
137
  if its_type.blank?
@@ -2,6 +2,7 @@
2
2
 
3
3
  class GitIssue::Base
4
4
  include GitIssue::Helper
5
+ include Term::ANSIColor
5
6
 
6
7
  attr_reader :apikey, :command, :tickets, :options
7
8
  attr_accessor :sysout, :syserr
@@ -251,6 +252,7 @@ class GitIssue::Base
251
252
  opts.on("--remote=VALUE", 'on publish, remote repository to push branch ') {|v| @options[:remote] = v}
252
253
  opts.on("--onto=VALUE", 'on rebase, start new branch with HEAD equal to "newbase" ') {|v| @options[:onto] = v}
253
254
 
255
+ opts.on("--no-color", "turn off colored output"){@no_color = true }
254
256
  opts.on("--debug", "debug print"){@debug= true }
255
257
  }
256
258
 
@@ -264,6 +266,8 @@ class GitIssue::Base
264
266
  @syserr.puts msg
265
267
  end
266
268
 
267
-
269
+ def apply_colors(str, *colors)
270
+ @no_color.present? ? str : (colors.map(&method(:send)) + [str, reset]).join
271
+ end
268
272
  end
269
273
 
@@ -7,17 +7,28 @@ class GitIssue::Github < GitIssue::Base
7
7
  def initialize(args, options = {})
8
8
  super(args, options)
9
9
 
10
- url = `git config remote.origin.url`.strip
11
- @repo = url.match(/github.com[:\/](.+)\.git/)[1]
10
+ @repo = configured_value('issue.repo')
11
+ if @repo.blank?
12
+ url = `git config remote.origin.url`.strip
13
+ @repo = url.match(/github.com[:\/](.+)\.git/)[1]
14
+ end
12
15
 
13
- @user = options[:user] || configured_value('user')
16
+ @user = options[:user] || configured_value('issue.user')
14
17
  @user = global_configured_value('github.user') if @user.blank?
15
18
  @user = Pit.get("github", :require => {
16
19
  "user" => "Your user name in GitHub",
17
20
  })["user"] if @user.blank?
18
21
 
19
22
  configure_error('user', "git config issue.user yuroyoro") if @user.blank?
20
- @sslNoVerify = @options[:sslNoVerify] && RUBY_VERSION < '1.9.0'
23
+ @ssl_options = {}
24
+ if @options.key?(:sslNoVerify) && RUBY_VERSION < "1.9.0"
25
+ @ssl_options[:ssl_verify_mode] = OpenSSL::SSL::VERIFY_NONE
26
+ elsif configured_value('http.sslVerify') == "false"
27
+ @ssl_options[:ssl_verify_mode] = OpenSSL::SSL::VERIFY_NONE
28
+ end
29
+ if (ssl_cert = configured_value('http.sslCert'))
30
+ @ssl_options[:ssl_ca_cert] = ssl_cert
31
+ end
21
32
  end
22
33
 
23
34
  def commands
@@ -54,7 +65,7 @@ class GitIssue::Github < GitIssue::Base
54
65
 
55
66
  url = to_url("repos", @repo, 'issues')
56
67
 
57
- issues = fetch_json(url, params)
68
+ issues = fetch_json(url, options, params)
58
69
  issues = issues.sort_by{|i| i['number'].to_i} unless params[:sort] || params[:direction]
59
70
 
60
71
  t_max = issues.map{|i| mlength(i['title'])}.max
@@ -64,12 +75,12 @@ class GitIssue::Github < GitIssue::Base
64
75
  or_zero = lambda{|v| v.blank? ? "0" : v }
65
76
 
66
77
  issues.each do |i|
67
- puts sprintf("#%-4d %s %s %s %s c:%s v:%s p:%s %s %s",
68
- i['number'].to_i,
69
- i['state'],
78
+ puts sprintf("%s %s %s %s %s c:%s v:%s p:%s %s %s",
79
+ apply_fmt_colors(:id, sprintf('#%-4d', i['number'].to_i)),
80
+ apply_fmt_colors(:state, i['state']),
70
81
  mljust(i['title'], t_max),
71
- mljust(i['user']['login'], u_max),
72
- mljust(i['labels'].map{|l| l['name']}.join(','), l_max),
82
+ apply_fmt_colors(:login, mljust(i['user']['login'], u_max)),
83
+ apply_fmt_colors(:labels, mljust(i['labels'].map{|l| l['name']}.join(','), l_max)),
73
84
  or_zero.call(i['comments']),
74
85
  or_zero.call(i['votes']),
75
86
  or_zero.call(i['position']),
@@ -185,18 +196,9 @@ class GitIssue::Github < GitIssue::Base
185
196
  URI.join(ROOT, path_list.join("/"))
186
197
  end
187
198
 
188
- def fetch_json(url, params = {})
189
- url += "?" + params.map{|k,v| "#{k}=#{v}"}.join("&") unless params.empty?
190
-
191
- puts url if @debug
192
-
193
- password = options[:password] || get_password(@user)
194
- opt = {"Authorization" => "Basic " + Base64.encode64("#{@user}:#{password}")}
195
-
196
- opt[:ssl_verify_mode] = OpenSSL::SSL::VERIFY_NONE if @sslNoVerify
197
- json = open(url, opt) {|io|
198
- JSON.parse(io.read)
199
- }
199
+ def fetch_json(url, options = {}, params = {})
200
+ response = send_request(url, {},options, params, :get)
201
+ json = JSON.parse(response.body)
200
202
 
201
203
  if @debug
202
204
  puts '-' * 80
@@ -210,8 +212,8 @@ class GitIssue::Github < GitIssue::Base
210
212
 
211
213
  def fetch_issue(ticket_id, params = {})
212
214
  url = to_url("repos", @repo, 'issues', ticket_id)
213
- url += "?" + params.map{|k,v| "#{k}=#{v}"}.join("&") unless params.empty?
214
- json = fetch_json(url)
215
+ # url += "?" + params.map{|k,v| "#{k}=#{v}"}.join("&") unless params.empty?
216
+ json = fetch_json(url, {}, params)
215
217
 
216
218
  issue = json['issue'] || json
217
219
  raise "no such issue #{ticket} : #{base}" unless issue
@@ -231,7 +233,7 @@ class GitIssue::Github < GitIssue::Base
231
233
  end
232
234
 
233
235
  def post_json(url, json, options, params = {})
234
- response = send_json(url, json, options, params, :post)
236
+ response = send_request(url, json, options, params, :post)
235
237
  json = JSON.parse(response.body)
236
238
 
237
239
  raise error_message(json) unless response_success?(response)
@@ -239,7 +241,7 @@ class GitIssue::Github < GitIssue::Base
239
241
  end
240
242
 
241
243
  def put_json(url, json, options, params = {})
242
- response = send_json(url, json, options, params, :put)
244
+ response = send_request(url, json, options, params, :put)
243
245
  json = JSON.parse(response.body)
244
246
 
245
247
  raise error_message(json) unless response_success?(response)
@@ -252,7 +254,7 @@ class GitIssue::Github < GitIssue::Base
252
254
  msg.join("\n ")
253
255
  end
254
256
 
255
- def send_json(url, json, options, params = {}, method = :post)
257
+ def send_request(url, json = {}, options = {}, params = {}, method = :post)
256
258
  url = "#{url}"
257
259
  uri = URI.parse(url)
258
260
 
@@ -265,8 +267,23 @@ class GitIssue::Github < GitIssue::Base
265
267
 
266
268
  https = Net::HTTP.new(uri.host, uri.port)
267
269
  https.use_ssl = true
268
- https.verify_mode = OpenSSL::SSL::VERIFY_NONE
270
+ https.verify_mode = @ssl_options[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_NONE
271
+
272
+ store = OpenSSL::X509::Store.new
273
+ if @ssl_options[:ssl_ca_cert].present?
274
+ if File.directory? @ssl_options[:ssl_ca_cert]
275
+ store.add_path @ssl_options[:ssl_ca_cert]
276
+ else
277
+ store.add_file @ssl_options[:ssl_ca_cert]
278
+ end
279
+ http.cert_store = store
280
+ else
281
+ store.set_default_paths
282
+ end
283
+ https.cert_store = store
284
+
269
285
  https.set_debug_output $stderr if @debug && https.respond_to?(:set_debug_output)
286
+
270
287
  https.start{|http|
271
288
 
272
289
  path = "#{uri.path}"
@@ -275,6 +292,7 @@ class GitIssue::Github < GitIssue::Base
275
292
  request = case method
276
293
  when :post then Net::HTTP::Post.new(path)
277
294
  when :put then Net::HTTP::Put.new(path)
295
+ when :get then Net::HTTP::Get.new(path)
278
296
  else raise "unknown method #{method}"
279
297
  end
280
298
 
@@ -287,7 +305,7 @@ class GitIssue::Github < GitIssue::Base
287
305
  request.basic_auth @user, password
288
306
 
289
307
  request.set_content_type("application/json")
290
- request.body = json.to_json
308
+ request.body = json.to_json if json.present?
291
309
 
292
310
  response = http.request(request)
293
311
  if @debug
@@ -331,7 +349,7 @@ class GitIssue::Github < GitIssue::Base
331
349
  end
332
350
  end
333
351
 
334
- msg << sprintf("%s : %s", mljust('labels', 18), issue['labels'].map{|l| l['name']}.join(","))
352
+ msg << sprintf("%s : %s", mljust('labels', 18), apply_fmt_colors(:labels, issue['labels'].map{|l| l['name']}.join(",")))
335
353
  msg << sprintf("%s : %s", mljust('html_url', 18), issue['html_url'])
336
354
  msg << sprintf("%s : %s", mljust('updated_at', 18), Time.parse(issue['updated_at']))
337
355
 
@@ -352,14 +370,14 @@ class GitIssue::Github < GitIssue::Base
352
370
  end
353
371
 
354
372
  def issue_title(issue)
355
- "[#{issue['state']}] ##{issue['number']} #{issue['title']}"
373
+ "[#{apply_fmt_colors(:state, issue['state'])}] #{apply_fmt_colors(:id, "##{issue['number']}")} #{issue['title']}"
356
374
  end
357
375
 
358
376
  def issue_author(issue)
359
377
  author = issue['user']['login']
360
378
  created_at = issue['created_at']
361
379
 
362
- msg = "#{author} opened this issue #{Time.parse(created_at)}"
380
+ msg = "#{apply_fmt_colors(:login, author)} opened this issue #{Time.parse(created_at)}"
363
381
  msg
364
382
  end
365
383
 
@@ -399,5 +417,14 @@ class GitIssue::Github < GitIssue::Base
399
417
  opts
400
418
  end
401
419
 
420
+ def apply_fmt_colors(key, str)
421
+ fmt_colors[key.to_sym] ? apply_colors(str, *Array(fmt_colors[key.to_sym])) : str
422
+ end
423
+
424
+ def fmt_colors
425
+ @fmt_colors ||= { :id => [:bold, :cyan], :state => :blue,
426
+ :login => :magenta, :labels => :yellow}
427
+ end
428
+
402
429
  end
403
430
  end
@@ -6,15 +6,15 @@ class Redmine < GitIssue::Base
6
6
  def initialize(args, options = {})
7
7
  super(args, options)
8
8
 
9
- @apikey = options[:apikey] || configured_value('apikey')
9
+ @apikey = options[:apikey] || configured_value('issue.apikey')
10
10
  configure_error('apikey', "git config issue.apikey some_api_key") if @apikey.blank?
11
11
 
12
- @url = options[:url] || configured_value('url')
12
+ @url = options[:url] || configured_value('issue.url')
13
13
  configure_error('url', "git config issue.url http://example.com/redmine") if @url.blank?
14
14
  end
15
15
 
16
16
  def default_cmd
17
- Helper.configured_value('project').blank? ? :list : :project
17
+ Helper.configured_value('issue.project').blank? ? :list : :project
18
18
  end
19
19
 
20
20
  def commands
@@ -71,7 +71,7 @@ class Redmine < GitIssue::Base
71
71
  def add(options = {})
72
72
  property_names = [:project_id, :subject, :description, :done_ratio, :status_id, :priority_id, :tracker_id, :assigned_to_id, :category_id, :fixed_version_id, :notes]
73
73
 
74
- project_id = options[:project_id] || Helper.configured_value('project')
74
+ project_id = options[:project_id] || Helper.configured_value('issue.project')
75
75
  if options.slice(*property_names).empty?
76
76
  issue = read_issue_from_editor({"project" => {"id" => project_id}}, options)
77
77
  description = issue.delete(:notes)
@@ -85,7 +85,7 @@ class Redmine < GitIssue::Base
85
85
  end
86
86
 
87
87
  json = build_issue_json(options, property_names)
88
- json["issue"][:project_id] ||= Helper.configured_value('project')
88
+ json["issue"][:project_id] ||= Helper.configured_value('issue.project')
89
89
 
90
90
  url = to_url('issues')
91
91
 
@@ -144,7 +144,7 @@ class Redmine < GitIssue::Base
144
144
  end
145
145
 
146
146
  def project(options = {})
147
- project_id = Helper.configured_value('project')
147
+ project_id = Helper.configured_value('issue.project')
148
148
  project_id = options[:ticket_id] if project_id.blank?
149
149
  raise 'project_id is required.' unless project_id
150
150
  list(options.merge(:query => "project_id=#{project_id}"))
@@ -204,6 +204,10 @@ class Redmine < GitIssue::Base
204
204
  end
205
205
 
206
206
  http = Net::HTTP.new(uri.host, uri.port)
207
+ if uri.scheme == 'https'
208
+ http.use_ssl = true
209
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
210
+ end
207
211
  http.set_debug_output $stderr if @debug && http.respond_to?(:set_debug_output)
208
212
  http.start{|http|
209
213
 
@@ -238,7 +242,7 @@ class Redmine < GitIssue::Base
238
242
 
239
243
 
240
244
  def issue_title(issue)
241
- "[#{issue['project']['name']}] #{issue['tracker']['name']} ##{issue['id']} #{issue['subject']}"
245
+ "[#{apply_colors(issue['project']['name'], :green)}] #{apply_colors(issue['tracker']['name'], :yellow)} #{apply_fmt_colors(:id, "##{issue['id']}")} #{issue['subject']}"
242
246
  end
243
247
 
244
248
  def issue_author(issue)
@@ -246,7 +250,7 @@ class Redmine < GitIssue::Base
246
250
  created_on = issue['created_on']
247
251
  updated_on = issue['updated_on']
248
252
 
249
- msg = "#{author}が#{time_ago_in_words(created_on)}に追加"
253
+ msg = "#{apply_fmt_colors(:assigned_to, author)}が#{time_ago_in_words(created_on)}に追加"
250
254
  msg += ", #{time_ago_in_words(updated_on)}に更新" unless created_on == updated_on
251
255
  msg
252
256
  end
@@ -276,13 +280,13 @@ class Redmine < GitIssue::Base
276
280
  add_prop = Proc.new{|name|
277
281
  title = property_title(name)
278
282
  value = issue[name] || ""
279
- props << [title, value]
283
+ props << [title, value, name]
280
284
  }
281
285
  add_prop_name = Proc.new{|name|
282
286
  title = property_title(name)
283
287
  value = ''
284
288
  value = prop_name.call(name)
285
- props << [title, value]
289
+ props << [title, value, name]
286
290
  }
287
291
 
288
292
  add_prop_name.call('status')
@@ -297,12 +301,13 @@ class Redmine < GitIssue::Base
297
301
  # acd custom_fields if it have value.
298
302
  if custom_fields = issue[:custom_fields] && custom_fields.reject{|cf| cf['value'].nil? || cf['value'].empty? }
299
303
  custom_fields.each do |cf|
300
- props << [cf['name'], cf['value']]
304
+ props << [cf['name'], cf['value'], cf['name']]
301
305
  end
302
306
  end
303
307
 
304
308
  props.each_with_index do |p,n|
305
- row = sprintf("%s : %s", mljust(p.first, 18), mljust(p.last.to_s, 24))
309
+ title, value, name = p
310
+ row = sprintf("%s : %s", mljust(title, 18), apply_fmt_colors(name, mljust(value.to_s, 24)))
306
311
  if n % 2 == 0
307
312
  msg << row
308
313
  else
@@ -367,7 +372,7 @@ class Redmine < GitIssue::Base
367
372
  def format_jounal(j, n)
368
373
  jnl = []
369
374
 
370
- jnl << "##{n + 1} - #{j['user']['name']}が#{time_ago_in_words(j['created_on'])}に更新"
375
+ jnl << "##{n + 1} - #{apply_fmt_colors(:assigned_to, j['user']['name'])}が#{time_ago_in_words(j['created_on'])}に更新"
371
376
  jnl << "-" * 78
372
377
  j['details'].each do |d|
373
378
  log = "#{property_title(d['name'])}を"
@@ -385,7 +390,7 @@ class Redmine < GitIssue::Base
385
390
  def format_changesets(changesets)
386
391
  cs = []
387
392
  changesets.sort_by{|c| c['committed_on'] }.each do |c|
388
- cs << "リビジョン: #{c['revision'][0..10]} #{c['user']['name']}が#{time_ago_in_words(c['committed_on'])}に追加"
393
+ cs << "リビジョン: #{apply_colors(c['revision'][0..10], :cyan)} #{apply_fmt_colors(:assigned_to, c['user']['name'])}が#{time_ago_in_words(c['committed_on'])}に追加"
389
394
  cs += c['comments'].split("\n").to_a
390
395
  cs << ""
391
396
  end
@@ -395,10 +400,12 @@ class Redmine < GitIssue::Base
395
400
  def format_relations(relations)
396
401
  relations.map{|r|
397
402
  issue = fetch_issue(r['issue_id'])
398
- "#{relations_label(r['relation_type'])} #{issue_title(issue)} #{issue['status']['name']} #{issue['start_date']} "
403
+ "#{relations_label(r['relation_type'])} #{issue_title(issue)} #{apply_fmt_colors(:status, issue['status']['name'])} #{issue['start_date']} "
399
404
  }
400
405
  end
401
406
 
407
+ DEFAULT_FORMAT = "%I %S | %A | %s %T %P | %V %C |"
408
+
402
409
  def format_issue_tables(issues_json)
403
410
  name_of = lambda{|issue, name| issue[name]['name'] rescue ""}
404
411
 
@@ -429,7 +436,9 @@ class Redmine < GitIssue::Base
429
436
  :subject => 80
430
437
  }
431
438
 
432
- fmt = configured_value('defaultformat', false)
439
+ fmt = configured_value('issue.defaultformat', false)
440
+ fmt = DEFAULT_FORMAT unless fmt.present?
441
+
433
442
  fmt_chars = { :I => :id, :S => :subject,
434
443
  :A => :assigned_to, :s => :status, :T => :tracker,
435
444
  :P => :priority, :p => :project, :V => :version,
@@ -440,7 +449,9 @@ class Redmine < GitIssue::Base
440
449
  fmt_chars.each do |k, v|
441
450
  res.gsub!(/\%(\d*)#{k}/) do |s|
442
451
  max = $1.blank? ? max_length[v] : $1.to_i
443
- max ? mljust(i[v], max) : i[v]
452
+ str = max ? mljust(i[v], max) : i[v]
453
+ colored = fmt_colors[v] ? apply_fmt_colors(v, str) : str
454
+ colored
444
455
  end
445
456
  end
446
457
  res
@@ -449,6 +460,16 @@ class Redmine < GitIssue::Base
449
460
  issues.map{|i| format_to.call(i) }
450
461
  end
451
462
 
463
+ def apply_fmt_colors(key, str)
464
+ fmt_colors[key.to_sym] ? apply_colors(str, *Array(fmt_colors[key.to_sym])) : str
465
+ end
466
+
467
+ def fmt_colors
468
+ @fmt_colors ||= { :id => [:bold, :cyan], :status => :blue,
469
+ :priority => :green, :assigned_to => :magenta,
470
+ :tracker => :yellow}
471
+ end
472
+
452
473
  def output_issues(issues)
453
474
  if options[:raw_id]
454
475
  issues.each do |i|
@@ -482,7 +503,7 @@ class Redmine < GitIssue::Base
482
503
  def read_issue_from_editor(issue, options = {})
483
504
  id_of = lambda{|name| issue[name] ? sprintf('%2s : %s', issue[name]["id"] , issue[name]['name'] ): ""}
484
505
 
485
- memofile = configured_value('memofile')
506
+ memofile = configured_value('issue.memofile')
486
507
  memo = File.open(memofile).read.lines.map{|l| "# #{l}"}.join("") unless memofile.blank?
487
508
 
488
509
  message = <<-MSG
@@ -1,3 +1,3 @@
1
1
  module GitIssue
2
- VERSION = "0.8.2"
2
+ VERSION = "0.8.3"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-issue
3
3
  version: !ruby/object:Gem::Version
4
- hash: 59
4
+ hash: 57
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 8
9
- - 2
10
- version: 0.8.2
9
+ - 3
10
+ version: 0.8.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tomohito Ozaki
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-04-19 00:00:00 +09:00
18
+ date: 2012-06-25 00:00:00 +09:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency