stephencelis-ghi 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +15 -0
- data/README.rdoc +7 -6
- data/lib/ghi.rb +45 -3
- data/lib/ghi/api.rb +4 -5
- data/lib/ghi/cli.rb +127 -46
- metadata +2 -2
data/History.rdoc
CHANGED
@@ -1,9 +1,24 @@
|
|
1
|
+
=== 0.0.5 / 2009/04/25
|
2
|
+
|
3
|
+
* 3 major enhancements
|
4
|
+
|
5
|
+
* Flag to claim/label issues with your GitHub username (thanks, Jamie).
|
6
|
+
* Flag for commenting on issues.
|
7
|
+
* Prompt for GitHub login and token if absent from gitconfig.
|
8
|
+
|
9
|
+
|
10
|
+
* 1 minor enhancement
|
11
|
+
|
12
|
+
* Opening issues with a title bypasses your $EDITOR.
|
13
|
+
|
14
|
+
|
1
15
|
=== 0.0.4 / 2009-04-24
|
2
16
|
|
3
17
|
* 1 major enhancement
|
4
18
|
|
5
19
|
* Cache messages created in $EDITOR.
|
6
20
|
|
21
|
+
|
7
22
|
* 2 minor enhancements
|
8
23
|
|
9
24
|
* Refactoring and cleanup.
|
data/README.rdoc
CHANGED
@@ -12,12 +12,6 @@ Get:
|
|
12
12
|
% gem install stephencelis-ghi --source=http://gems.github.com
|
13
13
|
|
14
14
|
|
15
|
-
Set (http://github.com/blog/180-local-github-config):
|
16
|
-
|
17
|
-
% git config --global github.user username
|
18
|
-
% git config --global github.token 6ef8395fecf207165f1a82178ae1b984
|
19
|
-
|
20
|
-
|
21
15
|
Go:
|
22
16
|
|
23
17
|
% ghi
|
@@ -28,6 +22,7 @@ Go:
|
|
28
22
|
-c, --closed, --close [number]
|
29
23
|
-e, --edit [number]
|
30
24
|
-r, --repo, --repository [name]
|
25
|
+
--claim [number]
|
31
26
|
|
32
27
|
|
33
28
|
== EXAMPLE?
|
@@ -42,9 +37,15 @@ Go:
|
|
42
37
|
ghi -e1 # Edits issue number 1 (in your $EDITOR)
|
43
38
|
ghi -c1 # Closes issue 1
|
44
39
|
ghi -o1 # Reopens 1
|
40
|
+
ghi --claim 1 # Tags issue 1 with your GitHub username
|
45
41
|
|
46
42
|
|
47
43
|
`ghi` also works anywhere:
|
48
44
|
|
49
45
|
ghi -rghi -l # Your fork of "ghi"
|
50
46
|
ghi -rstephencelis/ghi -l # Mine: "stephencelis/ghi"
|
47
|
+
|
48
|
+
|
49
|
+
== CONTRIBUTORS
|
50
|
+
|
51
|
+
* Jamie Macey (http://blog.tracefunc.com)
|
data/lib/ghi.rb
CHANGED
@@ -1,11 +1,53 @@
|
|
1
|
+
require "net/http"
|
2
|
+
require "yaml"
|
3
|
+
|
1
4
|
module GHI
|
2
|
-
VERSION = "0.0.
|
5
|
+
VERSION = "0.0.5"
|
3
6
|
|
4
7
|
def self.login
|
5
|
-
|
8
|
+
return @login if defined? @login
|
9
|
+
@login = `git config --get github.user`.chomp
|
10
|
+
if @login.empty?
|
11
|
+
begin
|
12
|
+
print "Please enter your GitHub username: "
|
13
|
+
@login = gets.chomp
|
14
|
+
valid = user? @login
|
15
|
+
warn "invalid username" unless valid
|
16
|
+
end until valid
|
17
|
+
`git config --global github.user #@login`
|
18
|
+
end
|
19
|
+
@login
|
6
20
|
end
|
7
21
|
|
8
22
|
def self.token
|
9
|
-
|
23
|
+
return @token if defined? @token
|
24
|
+
@token = `git config --get github.token`.chomp
|
25
|
+
if @token.empty?
|
26
|
+
begin
|
27
|
+
print "GitHub token (https://github.com/account): "
|
28
|
+
@token = gets.chomp
|
29
|
+
valid = token? @token
|
30
|
+
warn "invalid token for #{GHI.login}" unless valid
|
31
|
+
end until valid
|
32
|
+
`git config --global github.token #@token`
|
33
|
+
end
|
34
|
+
@token
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def self.user?(username)
|
40
|
+
url = "http://github.com/api/v2/yaml/user/show/#{username}"
|
41
|
+
!YAML.load(Net::HTTP.get(URI.parse(url)))["user"].nil?
|
42
|
+
rescue ArgumentError # Failure to parse YAML.
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.token?(token)
|
47
|
+
url = "http://github.com/api/v2/yaml/user/show/#{GHI.login}"
|
48
|
+
url += "?login=#{GHI.login}&token=#{token}"
|
49
|
+
!YAML.load(Net::HTTP.get(URI.parse(url)))["user"].nil?
|
50
|
+
rescue ArgumentError, NoMethodError # Failure to parse YAML.
|
51
|
+
false
|
10
52
|
end
|
11
53
|
end
|
data/lib/ghi/api.rb
CHANGED
@@ -46,20 +46,19 @@ class GHI::API
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def reopen(number)
|
49
|
-
GHI::Issue.new post(:reopen, number)
|
49
|
+
GHI::Issue.new post(:reopen, number)["issue"]
|
50
50
|
end
|
51
51
|
|
52
52
|
def add_label(label, number)
|
53
|
-
post
|
54
|
-
p res
|
53
|
+
post("label/add", label, number)["labels"]
|
55
54
|
end
|
56
55
|
|
57
56
|
def remove_label(label, number)
|
58
|
-
post
|
57
|
+
post("label/remove", label, number)["labels"]
|
59
58
|
end
|
60
59
|
|
61
60
|
def comment(number, comment)
|
62
|
-
post(:comment, number, comment)["comment"]
|
61
|
+
post(:comment, number, :comment => comment)["comment"]
|
63
62
|
end
|
64
63
|
|
65
64
|
private
|
data/lib/ghi/cli.rb
CHANGED
@@ -16,12 +16,13 @@ module GHI::CLI #:nodoc:
|
|
16
16
|
else
|
17
17
|
Tempfile.new message_filename, &file_proc(issue)
|
18
18
|
end
|
19
|
+
return @message if comment?
|
19
20
|
return @message.shift.strip, @message.join.sub(/\b\n\b/, " ").strip
|
20
21
|
end
|
21
22
|
|
22
23
|
def delete_message
|
23
24
|
File.delete message_path
|
24
|
-
rescue TypeError
|
25
|
+
rescue Errno::ENOENT, TypeError
|
25
26
|
nil
|
26
27
|
end
|
27
28
|
|
@@ -36,11 +37,7 @@ module GHI::CLI #:nodoc:
|
|
36
37
|
end
|
37
38
|
|
38
39
|
def gitdir
|
39
|
-
@gitdir ||=
|
40
|
-
dirs = []
|
41
|
-
Dir.pwd.count("/").times { |n| dirs << ([".."] * n << ".git") * "/" }
|
42
|
-
Dir[*dirs].first
|
43
|
-
end
|
40
|
+
@gitdir ||= `git rev-parse --git-dir`.chomp
|
44
41
|
end
|
45
42
|
|
46
43
|
def message_filename
|
@@ -79,11 +76,15 @@ module GHI::CLI #:nodoc:
|
|
79
76
|
|
80
77
|
def edit_format(issue)
|
81
78
|
l = []
|
82
|
-
l << issue.title
|
79
|
+
l << issue.title if issue.title && !comment?
|
83
80
|
l << ""
|
84
|
-
l << issue.body
|
85
|
-
|
86
|
-
|
81
|
+
l << issue.body if issue.body && !comment?
|
82
|
+
if comment?
|
83
|
+
l << "# Please enter your comment."
|
84
|
+
else
|
85
|
+
l << "# Please explain the issue. The first line will become the title."
|
86
|
+
end
|
87
|
+
l << "# Lines beginning '#' will be ignored; ghi aborts empty messages."
|
87
88
|
l << "# All line breaks will be honored in accordance with GFM:"
|
88
89
|
l << "#"
|
89
90
|
l << "# http://github.github.com/github-flavored-markdown"
|
@@ -104,26 +105,38 @@ module GHI::CLI #:nodoc:
|
|
104
105
|
l << " updated at: #{issue.updated_at}" if issue.updated_at
|
105
106
|
return l unless verbose
|
106
107
|
l << ""
|
107
|
-
l += issue.body
|
108
|
+
l += indent(issue.body)[0..-2]
|
108
109
|
end
|
109
110
|
|
110
|
-
def action_format(
|
111
|
-
key = "#{action.to_s.capitalize.sub(/e?$/, "ed")} issue #{
|
112
|
-
"#{key}: #{truncate
|
111
|
+
def action_format(value = nil)
|
112
|
+
key = "#{action.to_s.capitalize.sub(/e?$/, "ed")} issue #{number}"
|
113
|
+
"#{key}: #{truncate value.to_s, 78 - key.length}"
|
113
114
|
end
|
114
115
|
|
115
116
|
def truncate(string, length)
|
116
|
-
result = string.scan(/.{0,#{length}}(?:\s
|
117
|
+
result = string.scan(/.{0,#{length}}(?:\s|\Z)/).first.strip
|
117
118
|
result << "..." if result != string
|
118
119
|
result
|
119
120
|
end
|
121
|
+
|
122
|
+
def indent(string, level = 4)
|
123
|
+
string.scan(/.{0,#{78 - level}}(?:\s|\Z)/).map { |line|
|
124
|
+
" " * level + line
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def comment?
|
131
|
+
![:open, :edit].include?(action)
|
132
|
+
end
|
120
133
|
end
|
121
134
|
|
122
135
|
class Executable
|
123
136
|
include FileHelper, FormattingHelper
|
124
137
|
|
125
|
-
attr_reader :message, :user, :repo, :api, :action, :state, :
|
126
|
-
:
|
138
|
+
attr_reader :message, :user, :repo, :api, :action, :state, :search_term,
|
139
|
+
:number, :title, :body, :label
|
127
140
|
|
128
141
|
def initialize
|
129
142
|
option_parser.parse!(ARGV)
|
@@ -134,22 +147,30 @@ module GHI::CLI #:nodoc:
|
|
134
147
|
@api = GHI::API.new user, repo
|
135
148
|
|
136
149
|
case action
|
137
|
-
when :search
|
138
|
-
when :list
|
139
|
-
when :show
|
140
|
-
when :open
|
141
|
-
when :edit
|
142
|
-
when :close
|
143
|
-
when :reopen
|
150
|
+
when :search then search
|
151
|
+
when :list then list
|
152
|
+
when :show then show
|
153
|
+
when :open then open
|
154
|
+
when :edit then edit
|
155
|
+
when :close then close
|
156
|
+
when :reopen then reopen
|
157
|
+
when :comment then comment
|
158
|
+
|
159
|
+
when :claim then label
|
144
160
|
else puts option_parser
|
145
161
|
end
|
146
162
|
rescue GHI::API::InvalidConnection
|
147
163
|
warn "#{File.basename $0}: not a GitHub repo"
|
148
164
|
exit 1
|
149
|
-
rescue GHI::API::InvalidRequest
|
165
|
+
rescue GHI::API::InvalidRequest => e
|
150
166
|
warn "#{File.basename $0}: #{e.message} (#{user}/#{repo})"
|
167
|
+
delete_message
|
151
168
|
exit 1
|
152
|
-
rescue
|
169
|
+
rescue GHI::API::ResponseError => e
|
170
|
+
warn "#{File.basename $0}: #{e.message} (#{user}/#{repo})"
|
171
|
+
exit 1
|
172
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument,
|
173
|
+
OptionParser::AmbiguousOption => e
|
153
174
|
warn "#{File.basename $0}: #{e.message}"
|
154
175
|
exit 1
|
155
176
|
end
|
@@ -177,7 +198,7 @@ module GHI::CLI #:nodoc:
|
|
177
198
|
end
|
178
199
|
end
|
179
200
|
|
180
|
-
opts.on("-o", "--open", "--reopen [number]") do |v|
|
201
|
+
opts.on("-o", "--open", "--reopen [title|number]") do |v|
|
181
202
|
@action = :open
|
182
203
|
case v
|
183
204
|
when /^\d+$/
|
@@ -186,6 +207,8 @@ module GHI::CLI #:nodoc:
|
|
186
207
|
when /^l$/
|
187
208
|
@action = :list
|
188
209
|
@state = :open
|
210
|
+
when /^m$/
|
211
|
+
raise OptionParser::AmbiguousOption # TODO: Parse args for message.
|
189
212
|
else
|
190
213
|
@title = v
|
191
214
|
end
|
@@ -210,6 +233,8 @@ module GHI::CLI #:nodoc:
|
|
210
233
|
@action = :edit
|
211
234
|
@state = :closed
|
212
235
|
@number = v.to_i
|
236
|
+
when /^m$/
|
237
|
+
raise OptionParser::AmbiguousOption # TODO: Parse args for message.
|
213
238
|
else
|
214
239
|
raise OptionParser::MissingArgument
|
215
240
|
end
|
@@ -221,55 +246,111 @@ module GHI::CLI #:nodoc:
|
|
221
246
|
@user, @repo = repo
|
222
247
|
end
|
223
248
|
|
224
|
-
opts.on("-
|
249
|
+
opts.on("-m", "--comment [number|comment]") do |v|
|
250
|
+
case v
|
251
|
+
when /^\d+$/, nil
|
252
|
+
@action ||= :comment
|
253
|
+
@number ||= v
|
254
|
+
@comment = true
|
255
|
+
else
|
256
|
+
@body = v
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
opts.on("--claim [number]") do |v|
|
261
|
+
@action = :claim
|
262
|
+
@number = v.to_i
|
263
|
+
@label = GHI.login
|
264
|
+
end
|
265
|
+
|
266
|
+
opts.on_tail("-V", "--version") do
|
225
267
|
puts "#{File.basename($0)}: v#{GHI::VERSION}"
|
226
268
|
exit
|
227
269
|
end
|
228
270
|
|
229
|
-
opts.
|
271
|
+
opts.on_tail("-h", "--help") do
|
230
272
|
puts opts
|
231
273
|
exit
|
232
274
|
end
|
233
275
|
}
|
234
276
|
end
|
235
277
|
|
236
|
-
def search
|
237
|
-
issues = api.search
|
238
|
-
puts list_format(issues,
|
278
|
+
def search
|
279
|
+
issues = api.search search_term, state
|
280
|
+
puts list_format(issues, search_term)
|
239
281
|
end
|
240
282
|
|
241
|
-
def list
|
283
|
+
def list
|
242
284
|
issues = api.list state
|
243
285
|
puts list_format(issues)
|
244
286
|
end
|
245
287
|
|
246
|
-
def show
|
288
|
+
def show
|
247
289
|
issue = api.show number
|
248
290
|
puts show_format(issue)
|
249
291
|
end
|
250
292
|
|
251
|
-
def open
|
252
|
-
title
|
253
|
-
|
293
|
+
def open
|
294
|
+
if title.nil?
|
295
|
+
new_title, new_body = gets_from_editor GHI::Issue.new("title" => body)
|
296
|
+
elsif @comment && body.nil?
|
297
|
+
new_title, new_body = gets_from_editor GHI::Issue.new("title" => title)
|
298
|
+
end
|
299
|
+
new_title ||= title
|
300
|
+
new_body ||= body
|
301
|
+
issue = api.open new_title, new_body
|
254
302
|
delete_message
|
255
|
-
|
303
|
+
@number = issue.number
|
304
|
+
puts action_format(issue.title)
|
256
305
|
end
|
257
306
|
|
258
|
-
def edit
|
259
|
-
|
260
|
-
|
307
|
+
def edit
|
308
|
+
shown = api.show number
|
309
|
+
new_title, new_body = gets_from_editor(shown) if body.nil?
|
310
|
+
new_title ||= shown.title
|
311
|
+
new_body ||= body
|
312
|
+
issue = api.edit number, new_title, new_body
|
261
313
|
delete_message
|
262
|
-
puts action_format(issue)
|
314
|
+
puts action_format(issue.title)
|
263
315
|
end
|
264
316
|
|
265
|
-
def close
|
317
|
+
def close
|
266
318
|
issue = api.close number
|
267
|
-
|
319
|
+
if @comment
|
320
|
+
body ||= gets_from_editor issue
|
321
|
+
comment = api.comment number, body
|
322
|
+
end
|
323
|
+
puts action_format(issue.title)
|
324
|
+
puts "comment #{comment["status"]}" if comment
|
268
325
|
end
|
269
326
|
|
270
|
-
def reopen
|
327
|
+
def reopen
|
271
328
|
issue = api.reopen number
|
272
|
-
|
329
|
+
if @comment
|
330
|
+
body ||= gets_from_editor issue
|
331
|
+
comment = api.comment number, body
|
332
|
+
end
|
333
|
+
puts action_format(issue.title)
|
334
|
+
puts "comment #{comment["status"]}" if comment
|
335
|
+
end
|
336
|
+
|
337
|
+
def label
|
338
|
+
labels = api.add_label label, number
|
339
|
+
puts action_format
|
340
|
+
puts indent(labels.join(", "))
|
341
|
+
end
|
342
|
+
|
343
|
+
def unlabel
|
344
|
+
labels = api.add_label label, number
|
345
|
+
puts action_format
|
346
|
+
puts indent(labels.join(", "))
|
347
|
+
end
|
348
|
+
|
349
|
+
def comment
|
350
|
+
body = gets_from_editor api.show(number)
|
351
|
+
comment = api.comment(number, body)
|
352
|
+
delete_message
|
353
|
+
puts "comment #{comment["status"]}"
|
273
354
|
end
|
274
355
|
end
|
275
356
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stephencelis-ghi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stephen Celis
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
12
|
+
date: 2009-04-25 00:00:00 -07:00
|
13
13
|
default_executable: ghi
|
14
14
|
dependencies: []
|
15
15
|
|