git-issue 0.8.7 → 0.9.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/lib/git_issue.rb +32 -18
- data/lib/git_issue/bitbucket.rb +445 -0
- data/lib/git_issue/redmine.rb +1 -1
- data/lib/git_issue/version.rb +1 -1
- metadata +6 -5
data/lib/git_issue.rb
CHANGED
@@ -58,8 +58,9 @@ module GitIssue
|
|
58
58
|
|
59
59
|
def its_klass_of(its_type)
|
60
60
|
case its_type
|
61
|
-
when /redmine/i
|
62
|
-
when /github/i
|
61
|
+
when /redmine/i then GitIssue::Redmine
|
62
|
+
when /github/i then GitIssue::Github
|
63
|
+
when /bitbucket/i then GitIssue::Bitbucket
|
63
64
|
else
|
64
65
|
raise "unknown issue tracker type : #{its_type}"
|
65
66
|
end
|
@@ -73,8 +74,11 @@ module GitIssue
|
|
73
74
|
editor.shellsplit
|
74
75
|
end
|
75
76
|
|
76
|
-
def
|
77
|
-
|
77
|
+
def work_dir
|
78
|
+
dir = RUBY_PLATFORM.downcase =~ /mswin(?!ce)|mingw|bccwin|cygwin/ ?
|
79
|
+
`git rev-parse -q --git-dir 2> NUL`.strip :
|
80
|
+
`git rev-parse -q --git-dir 2> /dev/null`.strip
|
81
|
+
dir.empty? ? Dir.tmpdir : dir
|
78
82
|
end
|
79
83
|
|
80
84
|
def split_head_and_body(text)
|
@@ -94,7 +98,7 @@ module GitIssue
|
|
94
98
|
f = open(file)
|
95
99
|
body = f.read
|
96
100
|
f.close
|
97
|
-
body
|
101
|
+
body
|
98
102
|
end
|
99
103
|
|
100
104
|
def get_title_and_body_from_editor(message=nil)
|
@@ -113,21 +117,25 @@ module GitIssue
|
|
113
117
|
end
|
114
118
|
|
115
119
|
def open_editor(message = nil, abort_if_not_modified = true , &block)
|
116
|
-
message_file = File.join(
|
120
|
+
message_file = File.join(work_dir, 'ISSUE_MESSAGE')
|
117
121
|
File.open(message_file, 'w') { |msg|
|
118
122
|
msg.puts message
|
119
123
|
}
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
124
|
+
begin
|
125
|
+
edit_cmd = Array(git_editor).dup
|
126
|
+
edit_cmd << '-c' << 'set ft=gitcommit' if edit_cmd[0] =~ /^[mg]?vim$/
|
127
|
+
edit_cmd << message_file
|
128
|
+
|
129
|
+
system(*edit_cmd)
|
130
|
+
abort "can't open text editor for issue message" unless $?.success?
|
131
|
+
|
132
|
+
text = read_body(message_file)
|
133
|
+
abort "Aborting cause messages didn't modified." if message == text && abort_if_not_modified
|
134
|
+
ensure
|
135
|
+
File.unlink(message_file)
|
136
|
+
end
|
129
137
|
|
130
|
-
yield text
|
138
|
+
yield text.strip
|
131
139
|
end
|
132
140
|
|
133
141
|
module_function :configured_value, :global_configured_value, :configure_error, :its_klass_of, :get_title_and_body_from_editor, :get_body_from_editor
|
@@ -144,10 +152,15 @@ module GitIssue
|
|
144
152
|
github_user = Helper.global_configured_value('github.user')
|
145
153
|
unless github_user.blank?
|
146
154
|
its_type = 'github'
|
155
|
+
else
|
156
|
+
bitbucket_user = Helper.global_configured_value('bitbucket.user')
|
157
|
+
unless bitbucket_user.blank?
|
158
|
+
its_type = 'bitbucket'
|
159
|
+
end
|
147
160
|
end
|
148
161
|
end
|
149
162
|
|
150
|
-
Helper.configure_error('type (redmine | github)', "git config issue.type redmine") if its_type.blank?
|
163
|
+
Helper.configure_error('type (redmine | github | bitbucket)', "git config issue.type redmine") if its_type.blank?
|
151
164
|
|
152
165
|
its_klass = Helper.its_klass_of(its_type)
|
153
166
|
status = its_klass.new(ARGV).execute || true
|
@@ -163,5 +176,6 @@ module GitIssue
|
|
163
176
|
end
|
164
177
|
|
165
178
|
require File.dirname(__FILE__) + '/git_issue/base'
|
166
|
-
require File.dirname(__FILE__) + '/git_issue/
|
179
|
+
require File.dirname(__FILE__) + '/git_issue/bitbucket'
|
167
180
|
require File.dirname(__FILE__) + '/git_issue/github'
|
181
|
+
require File.dirname(__FILE__) + '/git_issue/redmine'
|
@@ -0,0 +1,445 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'base64'
|
3
|
+
require 'pit'
|
4
|
+
|
5
|
+
module GitIssue
|
6
|
+
class GitIssue::Bitbucket < GitIssue::Base
|
7
|
+
def initialize(args, options = {})
|
8
|
+
super(args, options)
|
9
|
+
|
10
|
+
@repo = configured_value('issue.repo')
|
11
|
+
if @repo.blank?
|
12
|
+
url = `git config remote.origin.url`.strip
|
13
|
+
@repo = url.match(/bitbucket.org[:\/](.+)\.git/)[1]
|
14
|
+
end
|
15
|
+
|
16
|
+
@user = options[:user] || configured_value('issue.user')
|
17
|
+
@user = global_configured_value('bitbucket.user') if @user.blank?
|
18
|
+
@user = Pit.get("bitbucket", :require => {
|
19
|
+
"user" => "Your user name in Bitbucket",
|
20
|
+
})["user"] if @user.blank?
|
21
|
+
|
22
|
+
configure_error('user', "git config issue.user yuroyoro") if @user.blank?
|
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
|
32
|
+
end
|
33
|
+
|
34
|
+
def commands
|
35
|
+
cl = super
|
36
|
+
cl << GitIssue::Command.new(:mention, :men, 'create a comment to given issue')
|
37
|
+
cl << GitIssue::Command.new(:close , :cl, 'close an issue with comment. comment is optional.')
|
38
|
+
end
|
39
|
+
|
40
|
+
def show(options = {})
|
41
|
+
ticket = options[:ticket_id]
|
42
|
+
raise 'ticket_id is required.' unless ticket
|
43
|
+
issue = fetch_issue(ticket, options)
|
44
|
+
|
45
|
+
if options[:oneline]
|
46
|
+
puts oneline_issue(issue, options)
|
47
|
+
else
|
48
|
+
comments = []
|
49
|
+
|
50
|
+
if issue['comment_count'] > 0
|
51
|
+
comments = fetch_comments(ticket) unless options[:supperss_comments]
|
52
|
+
end
|
53
|
+
puts ""
|
54
|
+
puts format_issue(issue, comments, options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def list(options = {})
|
59
|
+
state = options[:status] || "open"
|
60
|
+
|
61
|
+
query_names = [:status, :milestone, :assignee, :mentioned, :labels, :sort, :direction]
|
62
|
+
params = query_names.inject({}){|h,k| h[k] = options[k] if options[k];h}
|
63
|
+
params[:status] ||= "open"
|
64
|
+
params[:per_page] = options[:max_count] || 30
|
65
|
+
|
66
|
+
url = to_url("repositories", @repo, 'issues')
|
67
|
+
|
68
|
+
issues = fetch_json(url, options, params)
|
69
|
+
issues = issues['issues']
|
70
|
+
issues = issues.sort_by{|i| i['local_id']} unless params[:sort] || params[:direction]
|
71
|
+
|
72
|
+
t_max = issues.map{|i| mlength(i['title'])}.max
|
73
|
+
l_max = issues.map{|i| mlength(i['metadata']['kind'])}.max
|
74
|
+
u_max = issues.map{|i| mlength(i['reported_by']['username'])}.max
|
75
|
+
|
76
|
+
or_zero = lambda{|v| v.blank? ? "0" : v }
|
77
|
+
|
78
|
+
issues.each do |i|
|
79
|
+
puts sprintf("%s %s %s %s %s c:%s v:%s p:%s %s %s",
|
80
|
+
apply_fmt_colors(:id, sprintf('#%-4d', i['local_id'])),
|
81
|
+
apply_fmt_colors(:state, i['status']),
|
82
|
+
mljust(i['title'], t_max),
|
83
|
+
apply_fmt_colors(:login, mljust(i['reported_by']['username'], u_max)),
|
84
|
+
apply_fmt_colors(:labels, mljust(i['metadata']['kind'], l_max)),
|
85
|
+
or_zero.call(i['comment_count']),
|
86
|
+
or_zero.call(i['votes']),
|
87
|
+
or_zero.call(i['position']),
|
88
|
+
to_date(i['created_on']),
|
89
|
+
to_date(i['utc_last_updated'])
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
def mine(options = {})
|
96
|
+
raise "Not implemented yet."
|
97
|
+
|
98
|
+
list(options.merge(:assignee => @user))
|
99
|
+
end
|
100
|
+
|
101
|
+
def add(options = {})
|
102
|
+
property_names = [:title, :content, :assignee, :milestone, :labels]
|
103
|
+
|
104
|
+
message = <<-MSG
|
105
|
+
### Write title here ###
|
106
|
+
|
107
|
+
### descriptions here ###
|
108
|
+
MSG
|
109
|
+
|
110
|
+
unless options[:title]
|
111
|
+
options[:title], options[:content] = get_title_and_body_from_editor(message)
|
112
|
+
end
|
113
|
+
|
114
|
+
url = to_url("repositories", @repo, 'issues')
|
115
|
+
|
116
|
+
issue = post_json(url, nil, options)
|
117
|
+
puts "created issue #{oneline_issue(issue)}"
|
118
|
+
end
|
119
|
+
|
120
|
+
def update(options = {})
|
121
|
+
|
122
|
+
ticket = options[:ticket_id]
|
123
|
+
raise 'ticket_id is required.' unless ticket
|
124
|
+
|
125
|
+
property_names = [:title, :content, :assignee, :milestone, :labels, :status]
|
126
|
+
|
127
|
+
if options.slice(*property_names).empty?
|
128
|
+
issue = fetch_issue(ticket)
|
129
|
+
message = "#{issue['title']}\n\n#{issue['content']}"
|
130
|
+
options[:title], options[:content] = get_title_and_body_from_editor(message)
|
131
|
+
end
|
132
|
+
|
133
|
+
url = to_url("repositories", @repo, 'issues', ticket)
|
134
|
+
|
135
|
+
issue = put_json(url, nil, options) # use POST instead of PATCH.
|
136
|
+
puts "updated issue #{oneline_issue(issue)}"
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
def mention(options = {})
|
141
|
+
|
142
|
+
ticket = options[:ticket_id]
|
143
|
+
raise 'ticket_id is required.' unless ticket
|
144
|
+
|
145
|
+
unless options[:content]
|
146
|
+
options[:content] = get_body_from_editor("### comment here ###")
|
147
|
+
end
|
148
|
+
raise 'comment content is required.' unless options[:content]
|
149
|
+
|
150
|
+
url = to_url("repositories", @repo, 'issues', ticket, 'comments')
|
151
|
+
|
152
|
+
issue = post_json(url, nil, options)
|
153
|
+
|
154
|
+
issue = fetch_issue(ticket)
|
155
|
+
puts "commented issue #{oneline_issue(issue)}"
|
156
|
+
end
|
157
|
+
|
158
|
+
def branch(options = {})
|
159
|
+
ticket = options[:ticket_id]
|
160
|
+
raise 'ticket_id is required.' unless ticket
|
161
|
+
|
162
|
+
branch_name = ticket_branch(ticket)
|
163
|
+
|
164
|
+
if options[:force]
|
165
|
+
system "git branch -D #{branch_name}" if options[:force]
|
166
|
+
system "git checkout -b #{branch_name}"
|
167
|
+
else
|
168
|
+
if %x(git branch -l | grep "#{branch_name}").strip.empty?
|
169
|
+
system "git checkout -b #{branch_name}"
|
170
|
+
else
|
171
|
+
system "git checkout #{branch_name}"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
show(options)
|
176
|
+
end
|
177
|
+
|
178
|
+
def close(options = {})
|
179
|
+
|
180
|
+
ticket = options[:ticket_id]
|
181
|
+
raise 'ticket_id is required.' unless ticket
|
182
|
+
|
183
|
+
unless options[:content]
|
184
|
+
options[:content] = get_body_from_editor("### comment here ###")
|
185
|
+
end
|
186
|
+
|
187
|
+
options[:status] = "resolved" unless options[:status]
|
188
|
+
|
189
|
+
url = to_url("repositories", @repo, 'issues', ticket)
|
190
|
+
|
191
|
+
issue = put_json(url, nil, options)
|
192
|
+
|
193
|
+
comment_url = to_url("repositories", @repo, 'issues', ticket, 'comments')
|
194
|
+
post_json(comment_url, nil, options)
|
195
|
+
|
196
|
+
puts "closed issue #{oneline_issue(issue)}"
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
ROOT = 'https://api.bitbucket.org/1.0/'
|
202
|
+
def to_url(*path_list)
|
203
|
+
URI.join(ROOT, path_list.join("/"))
|
204
|
+
end
|
205
|
+
|
206
|
+
def fetch_json(url, options = {}, params = {})
|
207
|
+
response = send_request(url, {},options, params, :get)
|
208
|
+
json = JSON.parse(response.body)
|
209
|
+
|
210
|
+
raise error_message(json) unless response_success?(response)
|
211
|
+
|
212
|
+
if @debug
|
213
|
+
puts '-' * 80
|
214
|
+
puts url
|
215
|
+
pp json
|
216
|
+
puts '-' * 80
|
217
|
+
end
|
218
|
+
|
219
|
+
json
|
220
|
+
end
|
221
|
+
|
222
|
+
def fetch_issue(ticket_id, params = {})
|
223
|
+
url = to_url("repositories", @repo, 'issues', ticket_id)
|
224
|
+
# url += "?" + params.map{|k,v| "#{k}=#{v}"}.join("&") unless params.empty?
|
225
|
+
json = fetch_json(url, {}, params)
|
226
|
+
|
227
|
+
issue = json['issue'] || json
|
228
|
+
raise "no such issue #{ticket} : #{base}" unless issue
|
229
|
+
|
230
|
+
issue
|
231
|
+
end
|
232
|
+
|
233
|
+
def fetch_comments(ticket_id)
|
234
|
+
url = to_url("repositories", @repo, 'issues', ticket_id, 'comments')
|
235
|
+
json = fetch_json(url) || []
|
236
|
+
end
|
237
|
+
|
238
|
+
def build_issue_json(options, property_names)
|
239
|
+
json = property_names.inject({}){|h,k| h[k] = options[k] if options[k]; h}
|
240
|
+
json[:labels] = json[:labels].split(",") if json[:labels]
|
241
|
+
json
|
242
|
+
end
|
243
|
+
|
244
|
+
def post_json(url, json, options, params = {})
|
245
|
+
response = send_request(url, json, options, params, :post)
|
246
|
+
json = JSON.parse(response.body)
|
247
|
+
|
248
|
+
raise error_message(json) unless response_success?(response)
|
249
|
+
json
|
250
|
+
end
|
251
|
+
|
252
|
+
def put_json(url, json, options, params = {})
|
253
|
+
response = send_request(url, json, options, params, :put)
|
254
|
+
json = JSON.parse(response.body)
|
255
|
+
|
256
|
+
raise error_message(json) unless response_success?(response)
|
257
|
+
json
|
258
|
+
end
|
259
|
+
|
260
|
+
def error_message(json)
|
261
|
+
msg = [json['message']]
|
262
|
+
msg += json['errors'].map(&:pretty_inspect) if json['errors']
|
263
|
+
msg.join("\n ")
|
264
|
+
end
|
265
|
+
|
266
|
+
def send_request(url, json = {}, options = {}, params = {}, method = :post)
|
267
|
+
url = "#{url}"
|
268
|
+
uri = URI.parse(url)
|
269
|
+
|
270
|
+
if @debug
|
271
|
+
puts '-' * 80
|
272
|
+
puts url
|
273
|
+
pp json
|
274
|
+
puts '-' * 80
|
275
|
+
end
|
276
|
+
|
277
|
+
https = connection(uri.host, uri.port)
|
278
|
+
https.use_ssl = true
|
279
|
+
https.verify_mode = @ssl_options[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_NONE
|
280
|
+
|
281
|
+
store = OpenSSL::X509::Store.new
|
282
|
+
if @ssl_options[:ssl_ca_cert].present?
|
283
|
+
if File.directory? @ssl_options[:ssl_ca_cert]
|
284
|
+
store.add_path @ssl_options[:ssl_ca_cert]
|
285
|
+
else
|
286
|
+
store.add_file @ssl_options[:ssl_ca_cert]
|
287
|
+
end
|
288
|
+
http.cert_store = store
|
289
|
+
else
|
290
|
+
store.set_default_paths
|
291
|
+
end
|
292
|
+
https.cert_store = store
|
293
|
+
|
294
|
+
https.set_debug_output $stderr if @debug && https.respond_to?(:set_debug_output)
|
295
|
+
|
296
|
+
https.start{|http|
|
297
|
+
|
298
|
+
path = "#{uri.path}"
|
299
|
+
if method == :post or method == :put then
|
300
|
+
post_options = options.map{|k,v| "#{k}=#{v}"}.join("&")
|
301
|
+
else
|
302
|
+
path += "?" + params.map{|k,v| "#{k}=#{v}"}.join("&") unless params.empty?
|
303
|
+
end
|
304
|
+
|
305
|
+
request = case method
|
306
|
+
when :post then Net::HTTP::Post.new(path)
|
307
|
+
when :put then Net::HTTP::Put.new(path)
|
308
|
+
when :get then Net::HTTP::Get.new(path)
|
309
|
+
else raise "unknown method #{method}"
|
310
|
+
end
|
311
|
+
|
312
|
+
password = options[:password] || get_password(@user)
|
313
|
+
|
314
|
+
request.basic_auth @user, password
|
315
|
+
|
316
|
+
if json != nil then
|
317
|
+
request.set_content_type("application/json")
|
318
|
+
request.body = json.to_json if json.present?
|
319
|
+
elsif method == :post or method == :put then
|
320
|
+
request.set_content_type("application/x-www-form-urlencoded")
|
321
|
+
request.body = post_options
|
322
|
+
end
|
323
|
+
|
324
|
+
response = http.request(request)
|
325
|
+
if @debug
|
326
|
+
puts "#{response.code}: #{response.msg}"
|
327
|
+
puts response.body
|
328
|
+
end
|
329
|
+
|
330
|
+
response
|
331
|
+
}
|
332
|
+
end
|
333
|
+
|
334
|
+
def get_password(user)
|
335
|
+
Pit.get("bitbucket", :require => {
|
336
|
+
"password" => "Your password in Bitbucket",
|
337
|
+
})["password"]
|
338
|
+
end
|
339
|
+
|
340
|
+
def oneline_issue(issue, options = {})
|
341
|
+
issue_title(issue)
|
342
|
+
end
|
343
|
+
|
344
|
+
def format_issue(issue, comments, options)
|
345
|
+
msg = [""]
|
346
|
+
|
347
|
+
msg << issue_title(issue)
|
348
|
+
msg << "-" * 80
|
349
|
+
msg << issue_author(issue)
|
350
|
+
msg << ""
|
351
|
+
|
352
|
+
props = []
|
353
|
+
props << ['comments', issue['comments']]
|
354
|
+
props << ['votes', issue['votes']]
|
355
|
+
props << ['position', issue['position']]
|
356
|
+
props << ['milestone', issue['milestone']['title']] unless issue['milestone'].blank?
|
357
|
+
|
358
|
+
props.each_with_index do |p,n|
|
359
|
+
row = sprintf("%s : %s", mljust(p.first, 18), mljust(p.last.to_s, 24))
|
360
|
+
if n % 2 == 0
|
361
|
+
msg << row
|
362
|
+
else
|
363
|
+
msg[-1] = "#{msg.last} #{row}"
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
msg << sprintf("%s : %s", mljust('kind', 18), apply_fmt_colors(:labels, issue['metadata']['kind']))
|
368
|
+
msg << sprintf("%s : %s", mljust('updated_at', 18), Time.parse(issue['utc_last_updated']))
|
369
|
+
|
370
|
+
# display description
|
371
|
+
msg << "-" * 80
|
372
|
+
msg << "#{issue['content']}"
|
373
|
+
msg << ""
|
374
|
+
|
375
|
+
# display comments
|
376
|
+
if comments && !comments.empty?
|
377
|
+
msg << "-" * 80
|
378
|
+
msg << ""
|
379
|
+
cmts = format_comments(comments)
|
380
|
+
msg += cmts.map{|s| " #{s}"}
|
381
|
+
end
|
382
|
+
|
383
|
+
msg.join("\n")
|
384
|
+
end
|
385
|
+
|
386
|
+
def issue_title(issue)
|
387
|
+
|
388
|
+
"[#{apply_fmt_colors(:state, issue['status'])}] #{apply_fmt_colors(:id, "##{issue['local_id']}")} #{issue['title']}"
|
389
|
+
end
|
390
|
+
|
391
|
+
def issue_author(issue)
|
392
|
+
author = issue['reported_by']['username']
|
393
|
+
created_on = issue['created_on']
|
394
|
+
|
395
|
+
msg = "#{apply_fmt_colors(:login, author)} opened this issue #{Time.parse(created_on)}"
|
396
|
+
msg
|
397
|
+
end
|
398
|
+
|
399
|
+
def format_comments(comments)
|
400
|
+
cmts = []
|
401
|
+
comments.sort_by{|c| c['utc_created_on']}.each_with_index do |c,n|
|
402
|
+
cmts += format_comment(c,n)
|
403
|
+
end
|
404
|
+
cmts
|
405
|
+
end
|
406
|
+
|
407
|
+
def format_comment(c, n)
|
408
|
+
cmts = []
|
409
|
+
|
410
|
+
cmts << "##{n + 1} - #{c['author_info']['username']}が#{time_ago_in_words(c['utc_created_on'])}に更新"
|
411
|
+
cmts << "-" * 78
|
412
|
+
cmts += c['content'].split("\n").to_a if c['content']
|
413
|
+
cmts << ""
|
414
|
+
end
|
415
|
+
|
416
|
+
def opt_parser
|
417
|
+
opts = super
|
418
|
+
opts.on("--supperss_comments", "-sc", "show issue journals"){|v| @options[:supperss_comments] = true}
|
419
|
+
opts.on("--title=VALUE", "Title of issue.Use the given value to create/update issue."){|v| @options[:title] = v}
|
420
|
+
opts.on("--body=VALUE", "Body content of issue.Use the given value to create/update issue."){|v| @options[:content] = v}
|
421
|
+
opts.on("--state=VALUE", "Use the given value to create/update issue. or query of listing issues.Where 'state' is either 'open' or 'closed'"){|v| @options[:status] = v}
|
422
|
+
opts.on("--milestone=VALUE", "Use the given value to create/update issue. or query of listing issues, (Integer Milestone number)"){|v| @options[:milestone] = v }
|
423
|
+
opts.on("--assignee=VALUE", "Use the given value to create/update issue. or query of listing issues, (String User login)"){|v| @options[:assignee] = v }
|
424
|
+
opts.on("--mentioned=VALUE", "Query of listing issues, (String User login)"){|v| @options[:mentioned] = v }
|
425
|
+
opts.on("--labels=VALUE", "Use the given value to create/update issue. or query of listing issues, (String list of comma separated Label names)"){|v| @options[:labels] = v }
|
426
|
+
opts.on("--sort=VALUE", "Query of listing issues, (created, updated, comments, default: created)"){|v| @options[:sort] = v }
|
427
|
+
opts.on("--direction=VALUE", "Query of listing issues, (asc or desc, default: desc.)"){|v| @options[:direction] = v }
|
428
|
+
opts.on("--since=VALUE", "Query of listing issue, (Optional string of a timestamp in ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ)"){|v| @options[:since] = v }
|
429
|
+
|
430
|
+
opts.on("--password=VALUE", "For Authorizaion of create/update issue. Github API v3 doesn't supports API token base authorization for now. then, use Basic Authorizaion instead token." ){|v| @options[:password]}
|
431
|
+
opts.on("--sslnoverify", "don't verify SSL"){|v| @options[:sslNoVerify] = true}
|
432
|
+
opts
|
433
|
+
end
|
434
|
+
|
435
|
+
def apply_fmt_colors(key, str)
|
436
|
+
fmt_colors[key.to_sym] ? apply_colors(str, *Array(fmt_colors[key.to_sym])) : str
|
437
|
+
end
|
438
|
+
|
439
|
+
def fmt_colors
|
440
|
+
@fmt_colors ||= { :id => [:bold, :cyan], :state => :blue,
|
441
|
+
:login => :magenta, :labels => :yellow}
|
442
|
+
end
|
443
|
+
|
444
|
+
end
|
445
|
+
end
|
data/lib/git_issue/redmine.rb
CHANGED
@@ -409,7 +409,7 @@ class Redmine < GitIssue::Base
|
|
409
409
|
def format_changesets(changesets)
|
410
410
|
cs = []
|
411
411
|
changesets.sort_by{|c| c['committed_on'] }.each do |c|
|
412
|
-
cs << "リビジョン: #{apply_colors(c['revision'][0..10], :cyan)} #{apply_fmt_colors(:assigned_to, c['user']['name'])}が#{time_ago_in_words(c['committed_on'])}に追加"
|
412
|
+
cs << "リビジョン: #{apply_colors((c['revision'] || "")[0..10], :cyan)} #{apply_fmt_colors(:assigned_to, (c['user'] || {})['name'])}が#{time_ago_in_words(c['committed_on'])}に追加"
|
413
413
|
cs += c['comments'].split("\n").to_a
|
414
414
|
cs << ""
|
415
415
|
end
|
data/lib/git_issue/version.rb
CHANGED
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:
|
4
|
+
hash: 59
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 9
|
9
|
+
- 0
|
10
|
+
version: 0.9.0
|
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-
|
18
|
+
date: 2012-11-19 00:00:00 +09:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -83,6 +83,7 @@ files:
|
|
83
83
|
- images/git-issue_screenshot-2.png
|
84
84
|
- lib/git_issue.rb
|
85
85
|
- lib/git_issue/base.rb
|
86
|
+
- lib/git_issue/bitbucket.rb
|
86
87
|
- lib/git_issue/github.rb
|
87
88
|
- lib/git_issue/redmine.rb
|
88
89
|
- lib/git_issue/version.rb
|