pivotal-slacker 1.8.0 → 1.8.1
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/bin/pivotal-slacker +82 -38
- data/lib/formatter.rb +5 -1
- metadata +2 -2
data/bin/pivotal-slacker
CHANGED
@@ -8,6 +8,9 @@ require "action_view"
|
|
8
8
|
require "launchy"
|
9
9
|
require "readline"
|
10
10
|
require "shellwords"
|
11
|
+
require "nokogiri"
|
12
|
+
require "net/http"
|
13
|
+
require "uri"
|
11
14
|
require "pp"
|
12
15
|
require "app_config"
|
13
16
|
require "formatter"
|
@@ -28,28 +31,44 @@ end
|
|
28
31
|
# Init
|
29
32
|
|
30
33
|
PivotalTracker::Client.token = $config.api_key
|
31
|
-
$project = PivotalTracker::Project.find
|
34
|
+
$project = PivotalTracker::Project.find($config.project)
|
32
35
|
|
33
36
|
# For `shell` mode, to retain menu number history.
|
34
37
|
$menu_story_mapping = {}
|
35
38
|
|
36
|
-
# ----------------------------------------------------------
|
37
|
-
# Main app code
|
38
39
|
|
39
40
|
require "commander/import"
|
40
41
|
require "pivotal-tracker"
|
41
42
|
|
42
|
-
program :version, "1.
|
43
|
+
program :version, "1.8.1"
|
43
44
|
program :description, "Pivotal Tracker command line client."
|
44
45
|
|
45
46
|
# ----------------------------------------------------------
|
46
47
|
# Support functionality
|
47
48
|
|
49
|
+
require 'rubygems'
|
50
|
+
require 'active_resource'
|
51
|
+
|
52
|
+
# Defining this method because retrieving stories from PivotalTracker::Iteration doesn't work right.
|
53
|
+
def get_first_story opts={:project => nil, :group => "icebox"}
|
54
|
+
resource_uri = URI.parse(
|
55
|
+
"http://www.pivotaltracker.com/services/v3/projects/#{opts[:project].id}/iterations/#{opts[:group]}"
|
56
|
+
)
|
57
|
+
response = Net::HTTP.start(resource_uri.host, resource_uri.port) do |http|
|
58
|
+
http.get(resource_uri.path, {"X-TrackerToken" => $config.api_key})
|
59
|
+
end
|
60
|
+
|
61
|
+
xml = Nokogiri.XML(response.body)
|
62
|
+
target_story_id = xml.xpath("//story").xpath("./id").first.content.strip.to_i
|
63
|
+
|
64
|
+
opts[:project].stories.find target_story_id
|
65
|
+
end
|
66
|
+
|
48
67
|
def init_new_shell_loop
|
49
68
|
# Commander doesn't act right when you try to run the same command more than once,
|
50
69
|
# so we have to redefine them after we use them.
|
51
70
|
define_commands
|
52
|
-
|
71
|
+
|
53
72
|
# Don't let the Pivotal Tracker API client cache projects or stories.
|
54
73
|
$project = PivotalTracker::Project.find $config.project
|
55
74
|
end
|
@@ -58,7 +77,7 @@ end
|
|
58
77
|
# to `id` as passed in.
|
59
78
|
def normalize_story_id id
|
60
79
|
mapped = $menu_story_mapping[id]
|
61
|
-
|
80
|
+
|
62
81
|
if mapped != nil
|
63
82
|
mapped
|
64
83
|
else
|
@@ -81,12 +100,12 @@ def output_story_list opts={:project => nil, :states => [], :owned_by => nil, :m
|
|
81
100
|
requested_by = Formatter.requested_by(story.requested_by)
|
82
101
|
created_at = Formatter.time_ago(story.created_at)
|
83
102
|
story_type = Formatter.story_type(story.story_type)
|
84
|
-
|
103
|
+
|
85
104
|
menu_output = nil
|
86
105
|
if opts[:menu]
|
87
106
|
$menu_story_mapping[menu_number] = story.id
|
88
107
|
menu_output = "#{menu_number}. "
|
89
|
-
|
108
|
+
|
90
109
|
menu_number += 1
|
91
110
|
end
|
92
111
|
|
@@ -94,6 +113,9 @@ def output_story_list opts={:project => nil, :states => [], :owned_by => nil, :m
|
|
94
113
|
end
|
95
114
|
end
|
96
115
|
|
116
|
+
# ----------------------------------------------------------
|
117
|
+
# Main app code
|
118
|
+
|
97
119
|
# ----------------------------------------------------------
|
98
120
|
# Command-line interaction functionality
|
99
121
|
|
@@ -106,7 +128,7 @@ def define_commands
|
|
106
128
|
c.action do |args, options|
|
107
129
|
states = %w{unstarted started finished rejected}
|
108
130
|
owned_by = $config.user
|
109
|
-
|
131
|
+
|
110
132
|
output_story_list :project => $project, :states => states, :owned_by => owned_by, :menu => options.menu
|
111
133
|
end
|
112
134
|
end
|
@@ -118,7 +140,7 @@ def define_commands
|
|
118
140
|
c.action do |args, options|
|
119
141
|
states = %w{accepted}
|
120
142
|
owned_by = $config.user
|
121
|
-
|
143
|
+
|
122
144
|
output_story_list :project => $project, :states => states, :owned_by => owned_by
|
123
145
|
end
|
124
146
|
end
|
@@ -130,7 +152,7 @@ def define_commands
|
|
130
152
|
c.action do |args, options|
|
131
153
|
states = %w{started}
|
132
154
|
owned_by = $config.user
|
133
|
-
|
155
|
+
|
134
156
|
output_story_list :project => $project, :states => states, :owned_by => owned_by
|
135
157
|
end
|
136
158
|
end
|
@@ -142,7 +164,7 @@ def define_commands
|
|
142
164
|
c.action do |args, options|
|
143
165
|
states = %w{unstarted}
|
144
166
|
owned_by = $config.user
|
145
|
-
|
167
|
+
|
146
168
|
output_story_list :project => $project, :states => states, :owned_by => owned_by
|
147
169
|
end
|
148
170
|
end
|
@@ -159,7 +181,7 @@ def define_commands
|
|
159
181
|
end
|
160
182
|
|
161
183
|
command :show do |c|
|
162
|
-
c.syntax = "pivotal-slacker show story_id"
|
184
|
+
c.syntax = "pivotal-slacker show story_id"
|
163
185
|
c.description = "Show the details of a given Pivotal Tracker story."
|
164
186
|
c.example "Show a story with ID 123", "pivotal-slacker show 123"
|
165
187
|
c.action do |args, options|
|
@@ -178,7 +200,7 @@ def define_commands
|
|
178
200
|
puts ""
|
179
201
|
puts Formatter.description(story.description)
|
180
202
|
puts ""
|
181
|
-
|
203
|
+
|
182
204
|
notes = story.notes.all.sort { |a, b| a.noted_at <=> b.noted_at }
|
183
205
|
notes.each do |note|
|
184
206
|
author = Formatter.note_author(note.author)
|
@@ -186,12 +208,12 @@ def define_commands
|
|
186
208
|
note_text = Formatter.note_text(note.text)
|
187
209
|
puts "#{author} (#{noted_at}): #{note_text}"
|
188
210
|
end
|
189
|
-
|
211
|
+
|
190
212
|
# Extra padding line after notes.
|
191
213
|
if notes != nil and notes.size > 0
|
192
214
|
puts ""
|
193
215
|
end
|
194
|
-
|
216
|
+
|
195
217
|
end
|
196
218
|
end
|
197
219
|
|
@@ -201,7 +223,7 @@ def define_commands
|
|
201
223
|
c.example "Start a story with ID 123", "pivotal-slacker start 123"
|
202
224
|
c.action do |args, options|
|
203
225
|
story_id = normalize_story_id(args[0].to_i)
|
204
|
-
|
226
|
+
|
205
227
|
story = $project.stories.find(story_id)
|
206
228
|
result = story.update :current_state => "started", :owned_by => $config.user
|
207
229
|
|
@@ -215,7 +237,7 @@ def define_commands
|
|
215
237
|
end
|
216
238
|
end
|
217
239
|
end
|
218
|
-
|
240
|
+
|
219
241
|
command :estimate do |c|
|
220
242
|
c.syntax = "pivotal-slacker estimate story_id"
|
221
243
|
c.description = "Assign an estimate to a Pivotal Tracker story."
|
@@ -223,11 +245,11 @@ def define_commands
|
|
223
245
|
c.option "--points", "The estimate in points to assign to the story"
|
224
246
|
c.action do |args, options|
|
225
247
|
options.default :points => nil
|
226
|
-
|
248
|
+
|
227
249
|
raise "--points is required" if options.points == nil
|
228
|
-
|
250
|
+
|
229
251
|
story_id = normalize_story_id(args[0].to_i)
|
230
|
-
|
252
|
+
|
231
253
|
story = $project.stories.find(story_id)
|
232
254
|
result = story.update :estimate => options.points
|
233
255
|
|
@@ -242,10 +264,10 @@ def define_commands
|
|
242
264
|
c.action do |args, options|
|
243
265
|
story_id = normalize_story_id(args[0].to_i)
|
244
266
|
comment = args[1]
|
245
|
-
|
267
|
+
|
246
268
|
story = $project.stories.find(story_id)
|
247
269
|
story.notes.create :text => comment
|
248
|
-
|
270
|
+
|
249
271
|
comment = Formatter.note_text(comment)
|
250
272
|
puts "#{Formatter.story_action "Commented on", story.id, story.name}:"
|
251
273
|
puts "#{Formatter.note_author($config.user)}: #{comment}"
|
@@ -258,10 +280,10 @@ def define_commands
|
|
258
280
|
c.example "Finish a story with ID 123", "pivotal-slacker finish 123"
|
259
281
|
c.action do |args, options|
|
260
282
|
story_id = normalize_story_id(args[0].to_i)
|
261
|
-
|
283
|
+
|
262
284
|
story = $project.stories.find(story_id)
|
263
285
|
story.update :current_state => "finished"
|
264
|
-
|
286
|
+
|
265
287
|
puts Formatter.story_action(Formatter.state("finished"), story.id, story.name) + "."
|
266
288
|
end
|
267
289
|
end
|
@@ -273,29 +295,44 @@ def define_commands
|
|
273
295
|
c.option "--feature", "Designate the story as a feature"
|
274
296
|
c.option "--bug", "Designate the story as a bug"
|
275
297
|
c.option "--chore", "Designate the story as a chore"
|
298
|
+
c.option "--backlog", "Put this story at the top of the topmost 'Backlog' iteration"
|
276
299
|
c.option "--release", "Designate the story as a release"
|
277
300
|
c.option "--owner STRING", String, "Assigns the story to a user"
|
278
301
|
c.option "--name STRING", String, "Name of story"
|
279
302
|
c.option "--description STRING", String, "Description of story"
|
280
303
|
c.action do |args, options|
|
281
|
-
options.default
|
282
|
-
|
304
|
+
options.default(
|
305
|
+
:chore => false, :owner => $config.user, :name => nil, :description => nil
|
306
|
+
)
|
307
|
+
|
283
308
|
raise "--name is required" if options.name == nil
|
284
309
|
raise "--description is required" if options.description == nil
|
285
310
|
|
311
|
+
if not options.feature and not options.bug and not options.chore and not options.release
|
312
|
+
raise "Story type is required (e.g. --chore)"
|
313
|
+
end
|
314
|
+
|
286
315
|
story_type = "feature" if options.feature
|
287
316
|
story_type = "bug" if options.bug
|
288
317
|
story_type = "chore" if options.chore
|
289
318
|
story_type = "release" if options.release
|
290
319
|
|
320
|
+
iteration = nil
|
321
|
+
if options.backlog
|
322
|
+
iteration = "backlog"
|
323
|
+
end
|
324
|
+
|
291
325
|
puts ""
|
292
326
|
puts Formatter.story_name(options.name, :heading => true)
|
293
327
|
puts "#{Formatter.attr_descriptor('type is', Formatter.story_type(story_type))}"
|
294
328
|
puts "#{Formatter.attr_descriptor('requested by', Formatter.owner($config.user))}"
|
295
329
|
puts "#{Formatter.attr_descriptor('owned by', Formatter.owner(options.owner))}"
|
296
330
|
puts "#{Formatter.attr_descriptor('description is', Formatter.description(options.description))}"
|
331
|
+
if iteration
|
332
|
+
puts "#{Formatter.attr_descriptor('iteration is', Formatter.iteration(iteration))}"
|
333
|
+
end
|
297
334
|
puts ""
|
298
|
-
|
335
|
+
|
299
336
|
if agree "Really create? (y/n)"
|
300
337
|
story = $project.stories.create(
|
301
338
|
:name => options.name,
|
@@ -304,6 +341,13 @@ def define_commands
|
|
304
341
|
:requested_by => $config.user,
|
305
342
|
:owned_by => options.owner
|
306
343
|
)
|
344
|
+
|
345
|
+
# If asked to, put the story at the top of the backlog.
|
346
|
+
if options.backlog
|
347
|
+
first_backlog_story = get_first_story :project => $project, :group => "backlog"
|
348
|
+
story.move(:before, first_backlog_story)
|
349
|
+
end
|
350
|
+
|
307
351
|
puts Formatter.story_action("Created", story.id, story.name)
|
308
352
|
else
|
309
353
|
puts "Didn't create story."
|
@@ -318,7 +362,7 @@ command :shell do |c|
|
|
318
362
|
c.syntax = "pivotal-slacker shell"
|
319
363
|
c.description = %q{
|
320
364
|
The pivotal-slacker shell mode allows entry of commands with less effort.
|
321
|
-
|
365
|
+
|
322
366
|
In shell mode you can run commands as normal. You can also enter "menu numbers"
|
323
367
|
(listed to the left of stories in story listings) instead of story IDs when running
|
324
368
|
commands (e.g. `show 3` instead of `show 456123`).
|
@@ -330,32 +374,32 @@ command :shell do |c|
|
|
330
374
|
c.action do |args, options|
|
331
375
|
loop do
|
332
376
|
input = Readline::readline("pivotal-slacker> ")
|
333
|
-
|
377
|
+
|
334
378
|
break if input.nil? or input == "q" or input == "quit"
|
335
379
|
next if input.strip == ""
|
336
|
-
|
380
|
+
|
337
381
|
# Add input to Readinput history.
|
338
382
|
Readline::HISTORY.push(input)
|
339
383
|
|
340
384
|
# Turn output into an ARGV array.
|
341
|
-
argv = Shellwords.shellwords input
|
342
|
-
|
385
|
+
argv = Shellwords.shellwords input
|
386
|
+
|
343
387
|
# Shift the command name off the ARGV array; Commander doesn't want it in there.
|
344
388
|
command_name = argv.shift
|
345
389
|
command = command(command_name.to_sym)
|
346
|
-
|
390
|
+
|
347
391
|
if command.nil?
|
348
392
|
puts Formatter.err "No such command, \"#{command_name}\""
|
349
393
|
next
|
350
394
|
end
|
351
|
-
|
395
|
+
|
352
396
|
# Tell all commands to be in "menu" mode (print list numbers, save list number history, etc.)
|
353
397
|
# Only do this if the command supports --menu.
|
354
398
|
command.options.each do |option|
|
355
399
|
args = option[:args]
|
356
400
|
if (not args.nil?) and args.include? "--menu"
|
357
401
|
argv.push "--menu"
|
358
|
-
|
402
|
+
|
359
403
|
# Reset the menu mapping, since the command we're about to call will update them,
|
360
404
|
# and we want this mapping to not have any stale data in it.
|
361
405
|
menu_story_mapping = {}
|
@@ -367,8 +411,8 @@ command :shell do |c|
|
|
367
411
|
rescue Exception => e
|
368
412
|
puts e
|
369
413
|
end
|
370
|
-
|
414
|
+
|
371
415
|
init_new_shell_loop
|
372
|
-
end
|
416
|
+
end
|
373
417
|
end
|
374
418
|
end
|
data/lib/formatter.rb
CHANGED
@@ -52,7 +52,7 @@ class Formatter
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def self.story_type type
|
55
|
-
type.color '#cc6600'
|
55
|
+
"#{type}".color '#cc6600'
|
56
56
|
end
|
57
57
|
|
58
58
|
def self.attr_descriptor descriptor, attr
|
@@ -63,6 +63,10 @@ class Formatter
|
|
63
63
|
description
|
64
64
|
end
|
65
65
|
|
66
|
+
def self.iteration iteration
|
67
|
+
"#{iteration}".color('#cccc00')
|
68
|
+
end
|
69
|
+
|
66
70
|
# Format error messages.
|
67
71
|
def self.err errmsg
|
68
72
|
errmsg.background('#990000').color(:white)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pivotal-slacker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.8.
|
4
|
+
version: 1.8.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pivotal-tracker
|