pivotal-slacker 1.6.0 → 1.7.0
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 +244 -155
- data/lib/formatter.rb +5 -0
- metadata +2 -2
data/bin/pivotal-slacker
CHANGED
@@ -6,6 +6,8 @@ require "pivotal-tracker"
|
|
6
6
|
require "rainbow"
|
7
7
|
require "action_view"
|
8
8
|
require "launchy"
|
9
|
+
require "readline"
|
10
|
+
require "shellwords"
|
9
11
|
require "pp"
|
10
12
|
require "app_config"
|
11
13
|
require "formatter"
|
@@ -14,9 +16,9 @@ include ActionView::Helpers::DateHelper
|
|
14
16
|
# ----------------------------------------------------------
|
15
17
|
# Load config
|
16
18
|
|
17
|
-
config = nil
|
19
|
+
$config = nil
|
18
20
|
begin
|
19
|
-
config = AppConfig.load
|
21
|
+
$config = AppConfig.load
|
20
22
|
rescue Exception => e
|
21
23
|
puts e
|
22
24
|
exit 1
|
@@ -25,8 +27,11 @@ end
|
|
25
27
|
# ----------------------------------------------------------
|
26
28
|
# Init
|
27
29
|
|
28
|
-
PivotalTracker::Client.token = config.api_key
|
29
|
-
project = PivotalTracker::Project.find config.project
|
30
|
+
PivotalTracker::Client.token = $config.api_key
|
31
|
+
$project = PivotalTracker::Project.find $config.project
|
32
|
+
|
33
|
+
# For `shell` mode, to retain menu number history.
|
34
|
+
$menu_story_mapping = {}
|
30
35
|
|
31
36
|
# ----------------------------------------------------------
|
32
37
|
# Main app code
|
@@ -34,17 +39,32 @@ project = PivotalTracker::Project.find config.project
|
|
34
39
|
require "commander/import"
|
35
40
|
require "pivotal-tracker"
|
36
41
|
|
37
|
-
program :version, "1.
|
42
|
+
program :version, "1.6.0"
|
38
43
|
program :description, "Pivotal Tracker command line client."
|
39
44
|
|
40
45
|
# ----------------------------------------------------------
|
41
46
|
# Support functionality
|
42
47
|
|
43
|
-
|
48
|
+
# Supports looking up a story ID from $menu_story_mapping. If no mapping is found there, defaults
|
49
|
+
# to `id` as passed in.
|
50
|
+
def normalize_story_id id
|
51
|
+
mapped = $menu_story_mapping[id]
|
52
|
+
|
53
|
+
if mapped != nil
|
54
|
+
mapped
|
55
|
+
else
|
56
|
+
id
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Shared functionality for outputting lists of stories (e.g. for `mine`, `started`, etc.).
|
61
|
+
# Handles menuing functionality if opts[:menu] is `true`.
|
62
|
+
def output_story_list opts={:project => nil, :states => [], :owned_by => nil, :menu => false}
|
44
63
|
puts "Looking for stories owned by #{opts[:owned_by]} in states: #{opts[:states]}".color("#444444")
|
45
64
|
|
46
65
|
stories = opts[:project].stories.all(:owned_by => opts[:owned_by], :current_state => opts[:states])
|
47
66
|
stories.sort! { |a, b| a.created_at <=> b.created_at }
|
67
|
+
menu_number = 1 # Only used when --menu specified.
|
48
68
|
stories.each do |story|
|
49
69
|
id = Formatter.story_id(story.id)
|
50
70
|
name = Formatter.story_name(story.name)
|
@@ -52,195 +72,264 @@ def output_story_list opts={:project => nil, :states => [], :owned_by => nil}
|
|
52
72
|
requested_by = Formatter.requested_by(story.requested_by)
|
53
73
|
created_at = Formatter.time_ago(story.created_at)
|
54
74
|
story_type = Formatter.story_type(story.story_type)
|
75
|
+
|
76
|
+
menu_output = nil
|
77
|
+
if opts[:menu]
|
78
|
+
$menu_story_mapping[menu_number] = story.id
|
79
|
+
menu_output = "#{menu_number}. "
|
80
|
+
|
81
|
+
menu_number += 1
|
82
|
+
end
|
55
83
|
|
56
|
-
puts "[#{id}] #{name} ~ #{state} #{story_type}, from #{requested_by}, created #{created_at}"
|
84
|
+
puts "#{menu_output}[#{id}] #{name} ~ #{state} #{story_type}, from #{requested_by}, created #{created_at}"
|
57
85
|
end
|
58
86
|
end
|
59
87
|
|
60
88
|
# ----------------------------------------------------------
|
61
89
|
# Command-line interaction functionality
|
62
90
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
91
|
+
def define_commands
|
92
|
+
command :mine do |c|
|
93
|
+
c.syntax = "pivotal-slacker mine"
|
94
|
+
c.description = "List your stories in Pivotal Tracker."
|
95
|
+
c.example "List your open stories", "pivotal-slacker mine"
|
96
|
+
c.option "--menu", "Print an easily-navigable menu for stories. Only useful for `shell` mode"
|
97
|
+
c.action do |args, options|
|
98
|
+
states = %w{unstarted started finished rejected}
|
99
|
+
owned_by = $config.user
|
70
100
|
|
71
|
-
|
101
|
+
output_story_list :project => $project, :states => states, :owned_by => owned_by, :menu => options.menu
|
102
|
+
end
|
72
103
|
end
|
73
|
-
end
|
74
104
|
|
75
|
-
command :accepted do |c|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
105
|
+
command :accepted do |c|
|
106
|
+
c.syntax = "pivotal-slacker accepted"
|
107
|
+
c.description = "List your accepted stories in Pivotal Tracker."
|
108
|
+
c.example "List your accepted stories", "pivotal-slacker accepted"
|
109
|
+
c.action do |args, options|
|
110
|
+
states = %w{accepted}
|
111
|
+
owned_by = $config.user
|
82
112
|
|
83
|
-
|
113
|
+
output_story_list :project => $project, :states => states, :owned_by => owned_by
|
114
|
+
end
|
84
115
|
end
|
85
|
-
end
|
86
116
|
|
87
|
-
command :started do |c|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
117
|
+
command :started do |c|
|
118
|
+
c.syntax = "pivotal-slacker started"
|
119
|
+
c.description = "List your started stories in Pivotal Tracker."
|
120
|
+
c.example "List your started stories", "pivotal-slacker started"
|
121
|
+
c.action do |args, options|
|
122
|
+
states = %w{started}
|
123
|
+
owned_by = $config.user
|
94
124
|
|
95
|
-
|
125
|
+
output_story_list :project => $project, :states => states, :owned_by => owned_by
|
126
|
+
end
|
96
127
|
end
|
97
|
-
end
|
98
128
|
|
99
|
-
command :unstarted do |c|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
129
|
+
command :unstarted do |c|
|
130
|
+
c.syntax = "pivotal-slacker unstarted"
|
131
|
+
c.description = "List your unstarted stories in Pivotal Tracker."
|
132
|
+
c.example "List your unstarted stories", "pivotal-slacker unstarted"
|
133
|
+
c.action do |args, options|
|
134
|
+
states = %w{unstarted}
|
135
|
+
owned_by = $config.user
|
106
136
|
|
107
|
-
|
137
|
+
output_story_list :project => $project, :states => states, :owned_by => owned_by
|
138
|
+
end
|
108
139
|
end
|
109
|
-
end
|
110
140
|
|
111
|
-
command :open do |c|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
141
|
+
command :open do |c|
|
142
|
+
c.syntax = "pivotal-slacker open story_id"
|
143
|
+
c.description = "Open a specific Pivotal Tracker story in the browser."
|
144
|
+
c.example "Open a story with ID 123", "pivotal-slacker open 123"
|
145
|
+
c.action do |args, options|
|
146
|
+
story = $project.stories.find(args[0].to_i)
|
147
|
+
Launchy.open story.url
|
148
|
+
end
|
118
149
|
end
|
119
|
-
end
|
120
150
|
|
121
|
-
command :show do |c|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
151
|
+
command :show do |c|
|
152
|
+
c.syntax = "pivotal-slacker show story_id"
|
153
|
+
c.description = "Show the details of a given Pivotal Tracker story."
|
154
|
+
c.example "Show a story with ID 123", "pivotal-slacker show 123"
|
155
|
+
c.action do |args, options|
|
156
|
+
story_id = normalize_story_id(args[0].to_i)
|
157
|
+
story = $project.stories.find(story_id)
|
158
|
+
id = Formatter.story_id(story.id)
|
159
|
+
state = Formatter.state(story.current_state)
|
160
|
+
requested_by = Formatter.requested_by(story.requested_by)
|
161
|
+
created_at = Formatter.time_ago("created #{story.created_at}")
|
162
|
+
story_type = Formatter.story_type(story.story_type)
|
132
163
|
|
133
|
-
puts ""
|
134
|
-
puts "[#{id}] #{Formatter.story_name(story.name, :heading => true)}"
|
135
|
-
puts "#{state} #{story_type}, from #{requested_by}, created #{created_at}"
|
136
|
-
puts "☛ #{story.url.color('#0066CC').underline}"
|
137
|
-
puts ""
|
138
|
-
puts Formatter.description(story.description)
|
139
|
-
puts ""
|
140
|
-
|
141
|
-
notes = story.notes.all.sort { |a, b| a.noted_at <=> b.noted_at }
|
142
|
-
notes.each do |note|
|
143
|
-
author = Formatter.note_author(note.author)
|
144
|
-
noted_at = Formatter.time_ago(note.noted_at)
|
145
|
-
note_text = Formatter.note_text(note.text)
|
146
|
-
puts "#{author} (#{noted_at}): #{note_text}"
|
147
|
-
end
|
148
|
-
|
149
|
-
# Extra padding line after notes.
|
150
|
-
if notes != nil and notes.size > 0
|
151
164
|
puts ""
|
165
|
+
puts "[#{id}] #{Formatter.story_name(story.name, :heading => true)}"
|
166
|
+
puts "#{state} #{story_type}, from #{requested_by}, #{created_at}"
|
167
|
+
puts "☛ #{story.url.color('#0066CC').underline}"
|
168
|
+
puts ""
|
169
|
+
puts Formatter.description(story.description)
|
170
|
+
puts ""
|
171
|
+
|
172
|
+
notes = story.notes.all.sort { |a, b| a.noted_at <=> b.noted_at }
|
173
|
+
notes.each do |note|
|
174
|
+
author = Formatter.note_author(note.author)
|
175
|
+
noted_at = Formatter.time_ago(note.noted_at)
|
176
|
+
note_text = Formatter.note_text(note.text)
|
177
|
+
puts "#{author} (#{noted_at}): #{note_text}"
|
178
|
+
end
|
179
|
+
|
180
|
+
# Extra padding line after notes.
|
181
|
+
if notes != nil and notes.size > 0
|
182
|
+
puts ""
|
183
|
+
end
|
184
|
+
|
152
185
|
end
|
186
|
+
end
|
187
|
+
|
188
|
+
command :start do |c|
|
189
|
+
c.syntax = "pivotal-slacker start story_id"
|
190
|
+
c.description = "Mark a given Pivotal Tracker story as \"started\"."
|
191
|
+
c.example "Start a story with ID 123", "pivotal-slacker start 123"
|
192
|
+
c.action do |args, options|
|
193
|
+
story_id = normalize_story_id(args[0].to_i)
|
153
194
|
|
195
|
+
story = $project.stories.find(story_id)
|
196
|
+
story.update :current_state => "started"
|
197
|
+
|
198
|
+
puts Formatter.story_action(Formatter.state("started"), story.id, story.name) + "."
|
199
|
+
end
|
154
200
|
end
|
155
|
-
end
|
156
201
|
|
157
|
-
command :
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
202
|
+
command :comment do |c|
|
203
|
+
c.syntax = "pivotal-slacker comment story_id comment"
|
204
|
+
c.description = "Comment on a given Pivotal Tracker story."
|
205
|
+
c.example "Comment on story 123", "pivotal-slacker comment 123 \"Due to my skills I will destroy this task.\""
|
206
|
+
c.action do |args, options|
|
207
|
+
story_id = normalize_story_id(args[0].to_i)
|
208
|
+
comment = args[1]
|
163
209
|
|
164
|
-
|
165
|
-
|
210
|
+
story = $project.stories.find(story_id)
|
211
|
+
story.notes.create :text => comment
|
166
212
|
|
167
|
-
|
213
|
+
comment = Formatter.note_text(comment)
|
214
|
+
puts "#{Formatter.story_action "Commented on", story.id, story.name}:"
|
215
|
+
puts "#{Formatter.note_author($config.user)}: #{comment}"
|
216
|
+
end
|
168
217
|
end
|
169
|
-
end
|
170
218
|
|
171
|
-
command :
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
comment = args[1]
|
219
|
+
command :finish do |c|
|
220
|
+
c.syntax = "pivotal-slacker finish story_id"
|
221
|
+
c.description = "Mark a given Pivotal Tracker story as \"finished\"."
|
222
|
+
c.example "Finish a story with ID 123", "pivotal-slacker finish 123"
|
223
|
+
c.action do |args, options|
|
224
|
+
story_id = normalize_story_id(args[0].to_i)
|
178
225
|
|
179
|
-
|
180
|
-
|
226
|
+
story = $project.stories.find(story_id)
|
227
|
+
story.update :current_state => "finished"
|
181
228
|
|
182
|
-
|
183
|
-
|
184
|
-
puts "#{Formatter.note_author(config.user)}: #{comment}"
|
229
|
+
puts Formatter.story_action(Formatter.state("finished"), story.id, story.name) + "."
|
230
|
+
end
|
185
231
|
end
|
186
|
-
end
|
187
232
|
|
188
|
-
command :
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
233
|
+
command :create do |c|
|
234
|
+
c.syntax = "pivotal-slacker create [options]"
|
235
|
+
c.description = "Create a new task in Pivotal Tracker."
|
236
|
+
c.example "Create a new task", "pivotal-slacker create --chore --owner \"Jimmy Winkerbean\""
|
237
|
+
c.option "--feature", "Designate the story as a feature"
|
238
|
+
c.option "--bug", "Designate the story as a bug"
|
239
|
+
c.option "--chore", "Designate the story as a chore"
|
240
|
+
c.option "--release", "Designate the story as a release"
|
241
|
+
c.option "--owner STRING", String, "Assigns the story to a user"
|
242
|
+
c.option "--name STRING", String, "Name of story"
|
243
|
+
c.option "--description STRING", String, "Description of story"
|
244
|
+
c.action do |args, options|
|
245
|
+
options.default :chore => false, :owner => $config.user, :name => nil, :description => nil
|
194
246
|
|
195
|
-
|
196
|
-
|
247
|
+
raise "--name is required" if options.name == nil
|
248
|
+
raise "--description is required" if options.description == nil
|
249
|
+
|
250
|
+
story_type = "feature" if options.feature
|
251
|
+
story_type = "bug" if options.bug
|
252
|
+
story_type = "chore" if options.chore
|
253
|
+
story_type = "release" if options.release
|
254
|
+
|
255
|
+
puts ""
|
256
|
+
puts Formatter.story_name(options.name, :heading => true)
|
257
|
+
puts "#{Formatter.attr_descriptor('type is', Formatter.story_type(story_type))}"
|
258
|
+
puts "#{Formatter.attr_descriptor('requested by', Formatter.owner($config.user))}"
|
259
|
+
puts "#{Formatter.attr_descriptor('owned by', Formatter.owner(options.owner))}"
|
260
|
+
puts "#{Formatter.attr_descriptor('description is', Formatter.description(options.description))}"
|
261
|
+
puts ""
|
197
262
|
|
198
|
-
|
263
|
+
if agree "Really create? (y/n)"
|
264
|
+
story = $project.stories.create(
|
265
|
+
:name => options.name,
|
266
|
+
:story_type => story_type,
|
267
|
+
:description => options.description,
|
268
|
+
:requested_by => $config.user,
|
269
|
+
:owned_by => options.owner
|
270
|
+
)
|
271
|
+
puts Formatter.story_action("Created", story.id, story.name)
|
272
|
+
else
|
273
|
+
puts "Didn't create story."
|
274
|
+
end
|
275
|
+
end
|
199
276
|
end
|
200
277
|
end
|
201
278
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
c.
|
206
|
-
c.
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
279
|
+
define_commands
|
280
|
+
|
281
|
+
command :shell do |c|
|
282
|
+
c.syntax = "pivotal-slacker shell"
|
283
|
+
c.description = %q{
|
284
|
+
The pivotal-slacker shell mode allows entry of commands with less effort.
|
285
|
+
|
286
|
+
In shell mode you can run commands as normal. You can also enter "menu numbers"
|
287
|
+
(listed to the left of stories in story listings) instead of story IDs when running
|
288
|
+
commands (e.g. `show 3` instead of `show 456123`).
|
289
|
+
|
290
|
+
Readline support is available, so you can go back through the history of
|
291
|
+
commands entered in the current session.
|
292
|
+
}
|
293
|
+
c.example "Enter the pivotal-slacker shell mode", "pivotal-slacker shell"
|
213
294
|
c.action do |args, options|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
295
|
+
loop do
|
296
|
+
input = Readline::readline("pivotal-slacker> ")
|
297
|
+
break if input.nil? or input == "q" or input == "quit"
|
298
|
+
next if input.strip == ""
|
299
|
+
|
300
|
+
# Add input to Readinput history.
|
301
|
+
Readline::HISTORY.push(input)
|
302
|
+
|
303
|
+
# Turn output into an ARGV array.
|
304
|
+
argv = Shellwords.shellwords input
|
305
|
+
|
306
|
+
# Shift the command name off the ARGV array; Commander doesn't want it in there.
|
307
|
+
command_name = argv.shift
|
308
|
+
command = command(command_name.to_sym)
|
309
|
+
|
310
|
+
if command.nil?
|
311
|
+
puts Formatter.err "No such command, \"#{command_name}\""
|
312
|
+
next
|
313
|
+
end
|
314
|
+
|
315
|
+
# Tell all commands to be in "menu" mode (print list numbers, save list number history, etc.)
|
316
|
+
# Only do this if the command supports --menu.
|
317
|
+
command.options.each do |option|
|
318
|
+
args = option[:args]
|
319
|
+
if (not args.nil?) and args.include? "--menu"
|
320
|
+
argv.push "--menu"
|
321
|
+
|
322
|
+
# Reset the menu mapping, since the command we're about to call will update them,
|
323
|
+
# and we want this mapping to not have any stale data in it.
|
324
|
+
menu_story_mapping = {}
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
command.run *argv
|
329
|
+
|
330
|
+
# Commander doesn't act right when you try to run the same command more than once,
|
331
|
+
# so we have to redefine them after we use them.
|
332
|
+
define_commands
|
333
|
+
end
|
244
334
|
end
|
245
335
|
end
|
246
|
-
|
data/lib/formatter.rb
CHANGED
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.
|
4
|
+
version: 1.7.0
|
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-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pivotal-tracker
|