stephencelis-ghi 0.0.4 → 0.0.5
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/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
|
|