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.
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