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.
Files changed (6) hide show
  1. data/History.rdoc +15 -0
  2. data/README.rdoc +7 -6
  3. data/lib/ghi.rb +45 -3
  4. data/lib/ghi/api.rb +4 -5
  5. data/lib/ghi/cli.rb +127 -46
  6. 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.4"
5
+ VERSION = "0.0.5"
3
6
 
4
7
  def self.login
5
- `git config --get github.user`.chomp
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
- `git config --get github.token`.chomp
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 "label/add", label, number
54
- p res
53
+ post("label/add", label, number)["labels"]
55
54
  end
56
55
 
57
56
  def remove_label(label, number)
58
- post "label/remove", label, number
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 ||= begin
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 if issue.title
79
+ l << issue.title if issue.title && !comment?
83
80
  l << ""
84
- l << issue.body if issue.body
85
- l << "# Please explain the issue. The first line will become the title."
86
- l << "# Lines beginning '#' will be ignored. Empty issues won't be filed."
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.scan(/.{0,75}(?:\s|$)/).map { |line| " #{line}" }
108
+ l += indent(issue.body)[0..-2]
108
109
  end
109
110
 
110
- def action_format(issue)
111
- key = "#{action.to_s.capitalize.sub(/e?$/, "ed")} issue #{issue.number}"
112
- "#{key}: #{truncate issue.title, 78 - key.length}"
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|$)/).first.strip
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, :number, :title,
126
- :search_term
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 then search search_term, state
138
- when :list then list state
139
- when :show then show number
140
- when :open then open title
141
- when :edit then edit number
142
- when :close then close number
143
- when :reopen then reopen number
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, GHI::API::ResponseError => e
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 OptionParser::InvalidOption, OptionParser::MissingArgument => e
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("-V", "--version") do
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.on("-h", "--help") do
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(term, state)
237
- issues = api.search term, state
238
- puts list_format(issues, term)
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(state)
283
+ def list
242
284
  issues = api.list state
243
285
  puts list_format(issues)
244
286
  end
245
287
 
246
- def show(number)
288
+ def show
247
289
  issue = api.show number
248
290
  puts show_format(issue)
249
291
  end
250
292
 
251
- def open(title)
252
- title, body = gets_from_editor GHI::Issue.new(:title => title)
253
- issue = api.open title, body
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
- puts action_format(issue)
303
+ @number = issue.number
304
+ puts action_format(issue.title)
256
305
  end
257
306
 
258
- def edit(number)
259
- title, body = gets_from_editor api.show(number)
260
- issue = api.edit number, title, body
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(number)
317
+ def close
266
318
  issue = api.close number
267
- puts action_format(issue)
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(number)
327
+ def reopen
271
328
  issue = api.reopen number
272
- puts action_format(issue)
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
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-22 00:00:00 -07:00
12
+ date: 2009-04-25 00:00:00 -07:00
13
13
  default_executable: ghi
14
14
  dependencies: []
15
15