tpope-pickler 0.0.5 → 0.0.6
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/README.rdoc +4 -0
- data/lib/pickler/feature.rb +13 -4
- data/lib/pickler/runner.rb +119 -20
- data/lib/pickler/tracker/iteration.rb +4 -0
- data/lib/pickler/tracker/note.rb +4 -0
- data/lib/pickler/tracker/story.rb +48 -10
- data/lib/pickler.rb +45 -35
- data/pickler.gemspec +3 -1
- metadata +20 -2
data/README.rdoc
CHANGED
@@ -2,6 +2,10 @@
|
|
2
2
|
|
3
3
|
Synchronize user stories in Pivotal Tracker with Cucumber features.
|
4
4
|
|
5
|
+
If you aren't using Cucumber, you can still use pickler as a Pivotal Tracker
|
6
|
+
command line client, provided you humor it with a features/ directory
|
7
|
+
containing a tracker.yml file.
|
8
|
+
|
5
9
|
== Getting started
|
6
10
|
|
7
11
|
gem install tpope-pickler --source=http://gems.github.com
|
data/lib/pickler/feature.rb
CHANGED
@@ -13,6 +13,9 @@ class Pickler
|
|
13
13
|
@story = identifier
|
14
14
|
@id = @story.id
|
15
15
|
|
16
|
+
when Integer
|
17
|
+
@id = identifier
|
18
|
+
|
16
19
|
when /^#{URL_REGEX}$/, /^(\d+)$/
|
17
20
|
@id = $1.to_i
|
18
21
|
|
@@ -54,7 +57,9 @@ class Pickler
|
|
54
57
|
|
55
58
|
def start(default = nil)
|
56
59
|
story.transition!("started") if story.startable?
|
57
|
-
|
60
|
+
if filename || default
|
61
|
+
pull(default)
|
62
|
+
end
|
58
63
|
end
|
59
64
|
|
60
65
|
def push
|
@@ -64,9 +69,13 @@ class Pickler
|
|
64
69
|
end
|
65
70
|
|
66
71
|
def finish
|
67
|
-
|
68
|
-
|
69
|
-
|
72
|
+
if filename
|
73
|
+
story.finish
|
74
|
+
story.to_s = local_body
|
75
|
+
story.save
|
76
|
+
else
|
77
|
+
story.finish!
|
78
|
+
end
|
70
79
|
end
|
71
80
|
|
72
81
|
def id
|
data/lib/pickler/runner.rb
CHANGED
@@ -118,20 +118,48 @@ class Pickler
|
|
118
118
|
end
|
119
119
|
end
|
120
120
|
|
121
|
+
def colorize(code, string)
|
122
|
+
if color?
|
123
|
+
"\e[#{code}m#{string}\e[00m"
|
124
|
+
else
|
125
|
+
string.to_s
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
121
129
|
def puts_summary(story)
|
122
130
|
summary = "%6d " % story.id
|
123
131
|
type = story.estimate || TYPE_SYMBOLS[story.story_type]
|
124
132
|
state = STATE_SYMBOLS[story.current_state]
|
125
|
-
|
126
|
-
|
127
|
-
summary << "\e[01;3#{TYPE_COLORS[story.story_type]}m#{type}\e[00m "
|
128
|
-
else
|
129
|
-
summary << "#{state} #{type} "
|
130
|
-
end
|
133
|
+
summary << colorize("3#{STATE_COLORS[story.current_state]}", state) << ' '
|
134
|
+
summary << colorize("01;3#{TYPE_COLORS[story.story_type]}", type) << ' '
|
131
135
|
summary << story.name
|
132
136
|
puts summary
|
133
137
|
end
|
134
138
|
|
139
|
+
def puts_full(story)
|
140
|
+
puts colorize("01;3#{TYPE_COLORS[story.story_type]}", story.name)
|
141
|
+
puts "Type: #{story.story_type}".rstrip
|
142
|
+
if story.story_type == "release"
|
143
|
+
puts "Deadline: #{story.deadline}".rstrip
|
144
|
+
else
|
145
|
+
puts "Estimate: #{story.estimate}".rstrip
|
146
|
+
end
|
147
|
+
puts "State: #{story.current_state}".rstrip
|
148
|
+
puts "Labels: #{story.labels.join(', ')}".rstrip
|
149
|
+
puts "Requester: #{story.requested_by}".rstrip
|
150
|
+
puts "Owner: #{story.owned_by}".rstrip
|
151
|
+
puts "URL: #{story.url}".rstrip
|
152
|
+
puts unless story.description.blank?
|
153
|
+
story.description_lines.each do |line|
|
154
|
+
puts " #{line}".rstrip
|
155
|
+
end
|
156
|
+
story.notes.each do |note|
|
157
|
+
puts
|
158
|
+
puts " #{colorize('01', note.author)} (#{note.date})"
|
159
|
+
puts *note.lines(72).map {|l| " #{l}".rstrip}
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
135
163
|
def paginated_output
|
136
164
|
stdout = $stdout
|
137
165
|
if @tty && pager = pickler.config["pager"]
|
@@ -144,6 +172,7 @@ class Pickler
|
|
144
172
|
else
|
145
173
|
yield
|
146
174
|
end
|
175
|
+
rescue Errno::EPIPE
|
147
176
|
ensure
|
148
177
|
$stdout = stdout
|
149
178
|
end
|
@@ -177,9 +206,9 @@ class Pickler
|
|
177
206
|
when 0
|
178
207
|
puts "#{pickler.project_id} #{pickler.project.name}"
|
179
208
|
when 1
|
180
|
-
story = pickler.
|
209
|
+
story = pickler.story(args.first)
|
181
210
|
paginated_output do
|
182
|
-
|
211
|
+
puts_full story
|
183
212
|
end
|
184
213
|
else
|
185
214
|
too_many
|
@@ -200,17 +229,28 @@ class Pickler
|
|
200
229
|
end
|
201
230
|
end
|
202
231
|
[:requester, :owner, :mywork].each do |o|
|
203
|
-
on "--#{o}
|
204
|
-
modifications[o] = value
|
232
|
+
on "--#{o}[=USERNAME]" do |value|
|
233
|
+
modifications[o] = value || pickler.real_name
|
205
234
|
end
|
206
235
|
end
|
207
236
|
on "--[no-]includedone", "include accepted stories" do |value|
|
208
237
|
modifications[:includedone] = value
|
238
|
+
@iterations ||= []
|
239
|
+
@iterations << :done?
|
240
|
+
end
|
241
|
+
|
242
|
+
on "-b", "--backlog", "filter results to future iterations" do |c|
|
243
|
+
@iterations ||= []
|
244
|
+
@iterations << :backlog?
|
209
245
|
end
|
210
246
|
|
211
|
-
attr_writer :current
|
212
247
|
on "-c", "--current", "filter results to current iteration" do |b|
|
213
|
-
|
248
|
+
@iterations ||= []
|
249
|
+
@iterations << :current?
|
250
|
+
end
|
251
|
+
|
252
|
+
on "--[no-]full", "show full story, not a summary line" do |b|
|
253
|
+
@full = b
|
214
254
|
end
|
215
255
|
|
216
256
|
process do |*argv|
|
@@ -221,10 +261,19 @@ class Pickler
|
|
221
261
|
else
|
222
262
|
stories = pickler.project.stories(*argv)
|
223
263
|
end
|
224
|
-
|
264
|
+
if @iterations && @iterations != [:done?]
|
265
|
+
stories.reject! {|s| !@iterations.any? {|i| s.send(i)}}
|
266
|
+
end
|
225
267
|
paginated_output do
|
268
|
+
first = true
|
226
269
|
stories.each do |story|
|
227
|
-
|
270
|
+
if @full
|
271
|
+
puts unless first
|
272
|
+
puts_full story
|
273
|
+
else
|
274
|
+
puts_summary story
|
275
|
+
end
|
276
|
+
first = false
|
228
277
|
end
|
229
278
|
end
|
230
279
|
end
|
@@ -237,6 +286,13 @@ class Pickler
|
|
237
286
|
Upload the given story or all features with a tracker url in a comment on the
|
238
287
|
first line.
|
239
288
|
EOF
|
289
|
+
|
290
|
+
process do |*args|
|
291
|
+
args.replace(pickler.local_features) if args.empty?
|
292
|
+
args.each do |arg|
|
293
|
+
pickler.feature(arg).push
|
294
|
+
end
|
295
|
+
end
|
240
296
|
end
|
241
297
|
|
242
298
|
command :pull do
|
@@ -247,6 +303,13 @@ Download the given story or all well formed stories to the features/ directory.
|
|
247
303
|
Previously unseen stories will be given a numeric filename that you are
|
248
304
|
encouraged to change.
|
249
305
|
EOF
|
306
|
+
|
307
|
+
process do |*args|
|
308
|
+
args.replace(pickler.scenario_features) if args.empty?
|
309
|
+
args.each do |arg|
|
310
|
+
pickler.feature(arg).pull
|
311
|
+
end
|
312
|
+
end
|
250
313
|
end
|
251
314
|
|
252
315
|
command :start do
|
@@ -254,12 +317,11 @@ encouraged to change.
|
|
254
317
|
summary "Pull a story and mark it started"
|
255
318
|
description <<-EOF
|
256
319
|
Pull a given story and change its state to started. If basename is given
|
257
|
-
and no local file exists, features/basename.feature will be created
|
258
|
-
of features/id.feature.
|
320
|
+
and no local file exists, features/basename.feature will be created.
|
259
321
|
EOF
|
260
322
|
|
261
|
-
process do |
|
262
|
-
pickler.start(
|
323
|
+
process do |story, *args|
|
324
|
+
pickler.feature(story).start(args.first)
|
263
325
|
end
|
264
326
|
end
|
265
327
|
|
@@ -267,8 +329,8 @@ of features/id.feature.
|
|
267
329
|
banner_arguments "<story>"
|
268
330
|
summary "Push a story and mark it finished"
|
269
331
|
|
270
|
-
process do |
|
271
|
-
|
332
|
+
process do |story|
|
333
|
+
pickler.feature(story).finish
|
272
334
|
end
|
273
335
|
end
|
274
336
|
|
@@ -288,6 +350,34 @@ of features/id.feature.
|
|
288
350
|
end
|
289
351
|
end
|
290
352
|
|
353
|
+
command :unstart do
|
354
|
+
banner_arguments "[story] ..."
|
355
|
+
summary "Mark stories unstarted"
|
356
|
+
on "--all-started", "unstart all started stories" do
|
357
|
+
@all = true
|
358
|
+
end
|
359
|
+
process do |*args|
|
360
|
+
if @all
|
361
|
+
pickler.project.stories(:state => "started").each do |story|
|
362
|
+
story.transition!('unstarted')
|
363
|
+
end
|
364
|
+
end
|
365
|
+
args.each do |arg|
|
366
|
+
pickler.story(arg).transition!('unstarted')
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
command :unschedule do
|
372
|
+
banner_arguments "[story] ..."
|
373
|
+
summary "Move stories to icebox"
|
374
|
+
process do |*args|
|
375
|
+
args.each do |arg|
|
376
|
+
pickler.story(arg).transition!('unscheduled')
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
291
381
|
command :browse do
|
292
382
|
banner_arguments "[story]"
|
293
383
|
summary "Open a story in the web browser"
|
@@ -324,6 +414,15 @@ Requires launchy (gem install launchy).
|
|
324
414
|
end
|
325
415
|
end
|
326
416
|
|
417
|
+
command :comment do
|
418
|
+
banner_arguments "<story> <paragraph> ..."
|
419
|
+
summary "Post a comment to a story"
|
420
|
+
|
421
|
+
process do |story, *paragraphs|
|
422
|
+
pickler.story(story).comment!(paragraphs.join("\n\n"))
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
327
426
|
def initialize(argv)
|
328
427
|
@argv = argv
|
329
428
|
end
|
data/lib/pickler/tracker/note.rb
CHANGED
@@ -18,6 +18,10 @@ class Pickler
|
|
18
18
|
"#<#{self.class.inspect}:#{id.inspect}, story_id: #{story.id.inspect}, date: #{date.inspect}, author: #{author.inspect}, text: #{text.inspect}>"
|
19
19
|
end
|
20
20
|
|
21
|
+
def lines(width = 79)
|
22
|
+
text.scan(/(?:.{0,#{width}}|\S+?)(?:\s|$)/).map! {|line| line.strip}[0..-2]
|
23
|
+
end
|
24
|
+
|
21
25
|
end
|
22
26
|
end
|
23
27
|
end
|
@@ -5,22 +5,27 @@ class Pickler
|
|
5
5
|
TYPES = %w(bug feature chore release)
|
6
6
|
STATES = %w(unscheduled unstarted started finished delivered rejected accepted)
|
7
7
|
|
8
|
-
attr_reader :project, :iteration
|
9
|
-
reader :url
|
8
|
+
attr_reader :project, :iteration, :labels
|
9
|
+
reader :url
|
10
10
|
date_reader :created_at, :accepted_at, :deadline
|
11
11
|
accessor :current_state, :name, :description, :owned_by, :requested_by, :story_type
|
12
12
|
|
13
13
|
def initialize(project, attributes = {})
|
14
14
|
@project = project
|
15
|
-
@iteration = Iteration.new(project, attributes["iteration"]) if attributes["iteration"]
|
16
15
|
super(attributes)
|
16
|
+
@iteration = Iteration.new(project, @attributes["iteration"]) if @attributes["iteration"]
|
17
|
+
@labels = normalize_labels(@attributes["labels"])
|
18
|
+
end
|
19
|
+
|
20
|
+
def labels=(value)
|
21
|
+
@labels = normalize_labels(value)
|
17
22
|
end
|
18
23
|
|
19
24
|
def transition!(state)
|
20
25
|
raise Pickler::Tracker::Error, "Invalid state #{state}", caller unless STATES.include?(state)
|
21
26
|
self.current_state = state
|
22
27
|
if id
|
23
|
-
xml = "<story><
|
28
|
+
xml = "<story><current_state>#{state}</current_state></story>"
|
24
29
|
error = tracker.request_xml(:put, resource_url, xml).fetch("errors",{})["error"] || true
|
25
30
|
else
|
26
31
|
error = save
|
@@ -28,10 +33,33 @@ class Pickler
|
|
28
33
|
raise Pickler::Tracker::Error, Array(error).join("\n"), caller unless error == true
|
29
34
|
end
|
30
35
|
|
36
|
+
def finish
|
37
|
+
case story_type
|
38
|
+
when "bug", "feature"
|
39
|
+
self.current_state = "finished" unless complete?
|
40
|
+
when "chore", "release"
|
41
|
+
self.current_state = "accepted"
|
42
|
+
end
|
43
|
+
current_state
|
44
|
+
end
|
45
|
+
|
46
|
+
def finish!
|
47
|
+
transition!(finish)
|
48
|
+
end
|
49
|
+
|
50
|
+
def backlog?(as_of = Date.today)
|
51
|
+
iteration && iteration.start >= as_of
|
52
|
+
end
|
53
|
+
|
31
54
|
def current?(as_of = Date.today)
|
32
55
|
iteration && iteration.include?(as_of)
|
33
56
|
end
|
34
57
|
|
58
|
+
# In a previous iteration
|
59
|
+
def done?(as_of = Date.today)
|
60
|
+
iteration && iteration.finish <= as_of
|
61
|
+
end
|
62
|
+
|
35
63
|
def complete?
|
36
64
|
%w(finished delivered accepted).include?(current_state)
|
37
65
|
end
|
@@ -87,22 +115,27 @@ class Pickler
|
|
87
115
|
end
|
88
116
|
|
89
117
|
def comment!(body)
|
90
|
-
raise ArgumentError if body.strip.empty? || body.size > 5000
|
91
118
|
response = tracker.request_xml(:post, "#{resource_url}/notes",{:text => body}.to_xml(:dasherize => false, :root => 'note'))
|
92
|
-
|
119
|
+
if response["note"]
|
120
|
+
Note.new(self, response["note"])
|
121
|
+
else
|
122
|
+
raise Pickler::Tracker::Error, Array(response["errors"]["error"]).join("\n"), caller
|
123
|
+
end
|
93
124
|
end
|
94
125
|
|
95
|
-
def to_xml
|
126
|
+
def to_xml(force_labels = true)
|
96
127
|
hash = @attributes.reject do |k,v|
|
97
128
|
!%w(current_state deadline description estimate name owned_by requested_by story_type).include?(k)
|
98
129
|
end
|
99
|
-
|
130
|
+
if force_labels || !id || normalize_labels(@attributes["labels"]) != labels
|
131
|
+
hash["labels"] = labels.join(", ")
|
132
|
+
end
|
100
133
|
hash.to_xml(:dasherize => false, :root => "story")
|
101
134
|
end
|
102
135
|
|
103
136
|
def destroy
|
104
137
|
if id
|
105
|
-
response = tracker.request_xml(:delete, "/projects/#{project.id}/stories/#{id}",
|
138
|
+
response = tracker.request_xml(:delete, "/projects/#{project.id}/stories/#{id}", "")
|
106
139
|
raise Error, response["message"], caller if response["success"] != "true"
|
107
140
|
@attributes["id"] = nil
|
108
141
|
self
|
@@ -114,7 +147,7 @@ class Pickler
|
|
114
147
|
end
|
115
148
|
|
116
149
|
def save
|
117
|
-
response = tracker.request_xml(id ? :put : :post, resource_url, to_xml)
|
150
|
+
response = tracker.request_xml(id ? :put : :post, resource_url, to_xml(false))
|
118
151
|
if response["success"] == "true"
|
119
152
|
initialize(project, response["story"])
|
120
153
|
true
|
@@ -123,6 +156,11 @@ class Pickler
|
|
123
156
|
end
|
124
157
|
end
|
125
158
|
|
159
|
+
private
|
160
|
+
def normalize_labels(value)
|
161
|
+
Array(value).join(", ").strip.split(/\s*,\s*/)
|
162
|
+
end
|
163
|
+
|
126
164
|
end
|
127
165
|
end
|
128
166
|
end
|
data/lib/pickler.rb
CHANGED
@@ -47,6 +47,41 @@ class Pickler
|
|
47
47
|
self.class.config.merge(@config)
|
48
48
|
end
|
49
49
|
|
50
|
+
def real_name
|
51
|
+
config["real_name"] || (require 'etc'; Etc.getpwuid.gecos.split(',').first)
|
52
|
+
end
|
53
|
+
|
54
|
+
def new_story(attributes = {}, &block)
|
55
|
+
attributes = attributes.inject('requested_by' => real_name) do |h,(k,v)|
|
56
|
+
h.update(k.to_s => v)
|
57
|
+
end
|
58
|
+
project.new_story(attributes, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def stories(*args)
|
62
|
+
project.stories(*args)
|
63
|
+
end
|
64
|
+
|
65
|
+
def name
|
66
|
+
project.name
|
67
|
+
end
|
68
|
+
|
69
|
+
def iteration_length
|
70
|
+
project.iteration_length
|
71
|
+
end
|
72
|
+
|
73
|
+
def point_scale
|
74
|
+
project.point_scale
|
75
|
+
end
|
76
|
+
|
77
|
+
def week_start_day
|
78
|
+
project.week_start_day
|
79
|
+
end
|
80
|
+
|
81
|
+
def deliver_all_finished_stories
|
82
|
+
project.deliver_all_finished_stories
|
83
|
+
end
|
84
|
+
|
50
85
|
def parser
|
51
86
|
require 'cucumber'
|
52
87
|
require "cucumber/treetop_parser/feature_#@lang"
|
@@ -75,51 +110,26 @@ class Pickler
|
|
75
110
|
Cucumber.language['scenario']
|
76
111
|
end
|
77
112
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
113
|
+
def local_features
|
114
|
+
Dir[features_path('**','*.feature')].map {|f|feature(f)}.select {|f|f.id}
|
115
|
+
end
|
116
|
+
|
117
|
+
def scenario_features
|
118
|
+
project.stories(scenario_word, :includedone => true).reject do |s|
|
119
|
+
%(unscheduled unstarted).include?(s.current_state)
|
120
|
+
end.select do |s|
|
121
|
+
s.to_s =~ /^\s*#{Regexp.escape(scenario_word)}:/ && parser.parse(s.to_s)
|
83
122
|
end
|
84
123
|
end
|
85
124
|
|
86
125
|
def feature(string)
|
87
|
-
Feature.new(self,string)
|
126
|
+
string.kind_of?(Feature) ? string : Feature.new(self,string)
|
88
127
|
end
|
89
128
|
|
90
129
|
def story(string)
|
91
130
|
feature(string).story
|
92
131
|
end
|
93
132
|
|
94
|
-
def pull(*args)
|
95
|
-
if args.empty?
|
96
|
-
args = project.stories(scenario_word, :includedone => true).reject do |s|
|
97
|
-
%(unscheduled unstarted).include?(s.current_state)
|
98
|
-
end.select do |s|
|
99
|
-
s.to_s =~ /^\s*#{Regexp.escape(scenario_word)}:/ && parser.parse(s.to_s)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
args.each do |arg|
|
103
|
-
feature(arg).pull
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def start(arg, default = nil)
|
108
|
-
feature(arg).start(default)
|
109
|
-
end
|
110
|
-
|
111
|
-
def push(*args)
|
112
|
-
features(*args).each do |feature|
|
113
|
-
feature.push
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def finish(*args)
|
118
|
-
features(*args).each do |feature|
|
119
|
-
feature.finish
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
133
|
protected
|
124
134
|
|
125
135
|
end
|
data/pickler.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "pickler"
|
3
|
-
s.version = "0.0.
|
3
|
+
s.version = "0.0.6"
|
4
4
|
|
5
5
|
s.summary = "PIvotal traCKer Liaison to cucumbER"
|
6
6
|
s.description = "Synchronize between Cucumber and Pivotal Tracker"
|
@@ -25,4 +25,6 @@ Gem::Specification.new do |s|
|
|
25
25
|
]
|
26
26
|
s.add_dependency("activesupport", [">= 2.0.0"])
|
27
27
|
s.add_dependency("cucumber", [">= 0.1.9"])
|
28
|
+
s.add_dependency("builder")
|
29
|
+
s.add_dependency("xml-simple")
|
28
30
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tpope-pickler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tim Pope
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-
|
12
|
+
date: 2008-12-15 00:00:00 -08:00
|
13
13
|
default_executable: pickler
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -30,6 +30,24 @@ dependencies:
|
|
30
30
|
- !ruby/object:Gem::Version
|
31
31
|
version: 0.1.9
|
32
32
|
version:
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: builder
|
35
|
+
version_requirement:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: "0"
|
41
|
+
version:
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: xml-simple
|
44
|
+
version_requirement:
|
45
|
+
version_requirements: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
33
51
|
description: Synchronize between Cucumber and Pivotal Tracker
|
34
52
|
email: ruby@tpope.info
|
35
53
|
executables:
|