pivotal-to-trello 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1c6a9c20609a5c73bcb501e1dfb7348887428e9f
4
+ data.tar.gz: 4de6683169e6fdfe8f0c7b91ece7e7a2d250598c
5
+ SHA512:
6
+ metadata.gz: 77a57760785ef0508c3a9f4e787b6df6ebd143178e5ae1deabc56707bbfbf2023d9598bb5e65c851c0a05d9bc60f50de25a76770ab92c50c775558e672c20b1d
7
+ data.tar.gz: b8d99a1d6839f9c8792811d8317e251d830198ed71797dc2e3e512af41a134c9cb8b249a92d53161d2869d9d998f69c1ff1f7e6d8281993e4dff3245870e9223
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'highline'
4
+ gem 'ruby-trello', '>= 1.0.0 '
5
+ gem 'pivotal-tracker'
6
+
7
+ # Add dependencies to develop your gem here.
8
+ # Include everything needed to run rake, tests, features, etc.
9
+ group :development do
10
+ gem 'rspec'
11
+ gem 'rdoc'
12
+ gem 'bundler'
13
+ gem 'jeweler'
14
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 Dave Perrett
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,260 @@
1
+ pivotal-to-trello [![Build Status](https://travis-ci.org/recurser/pivotal-to-trello.png?branch=master)](https://travis-ci.org/recurser/pivotal-to-trello)
2
+ =================
3
+
4
+ This gem provides a command for exporting a [Pivotal Tracker](https://www.pivotaltracker.com/) project to [Trello](https://trello.com/).
5
+
6
+ Getting started
7
+ ---------------
8
+
9
+ 1. Install the gem:
10
+
11
+ > gem install pivotal-to-trello
12
+
13
+ 2. Run the importer:
14
+
15
+ > pivotal-to-trello import --trello-key TRELLO_API_KEY --trello-token TRELLO_TOKEN --pivotal-token PIVOTAL_TOKEN
16
+
17
+ See the [Obtaining API credentials](#obtaining-api-credentials) section for details on how to obtain these credentials.
18
+
19
+ The importer will ask you a series of questions to identify which Trello lists you want to import certain classes of stories into. It will then import the stories into Trello, along with any associated comments. It does not currently have the ability to import attachments.
20
+
21
+ For example :
22
+
23
+ > pivotal-to-trello import --trello-key TRELLO_API_KEY --trello-token TRELLO_TOKEN --pivotal-token PIVOTAL_TOKEN
24
+
25
+ Which Pivotal project would you like to export?
26
+ 1. Android App
27
+ 2. IOS App
28
+ 3. Tech Support
29
+ 4. Web App
30
+ Please select an option : 4
31
+
32
+ Which Trello board would you like to import into?
33
+ 1. Development
34
+ 2. Welcome Board
35
+ 3. Wish List
36
+ Please select an option : 1
37
+
38
+ Which Trello list would you like to put 'icebox' stories into?
39
+ 1. Accepted
40
+ 2. Backlog
41
+ 3. Bugs
42
+ 4. Delivered
43
+ 5. Finished
44
+ 6. Icebox
45
+ 7. In Progress
46
+ 8. Rejected
47
+ 9. Releases
48
+ 10. [don't import these stories]
49
+ Please select an option : 6
50
+
51
+ Which Trello list would you like to put 'current' stories into?
52
+ 1. Accepted
53
+ 2. Backlog
54
+ 3. Bugs
55
+ 4. Delivered
56
+ 5. Finished
57
+ 6. Icebox
58
+ 7. In Progress
59
+ 8. Rejected
60
+ 9. Releases
61
+ 10. [don't import these stories]
62
+ Please select an option : 7
63
+
64
+ Which Trello list would you like to put 'finished' stories into?
65
+ 1. Accepted
66
+ 2. Backlog
67
+ 3. Bugs
68
+ 4. Delivered
69
+ 5. Finished
70
+ 6. Icebox
71
+ 7. In Progress
72
+ 8. Rejected
73
+ 9. Releases
74
+ 10. [don't import these stories]
75
+ Please select an option : 5
76
+
77
+ Which Trello list would you like to put 'delivered' stories into?
78
+ 1. Accepted
79
+ 2. Backlog
80
+ 3. Bugs
81
+ 4. Delivered
82
+ 5. Finished
83
+ 6. Icebox
84
+ 7. In Progress
85
+ 8. Rejected
86
+ 9. Releases
87
+ 10. [don't import these stories]
88
+ Please select an option : 4
89
+
90
+ Which Trello list would you like to put 'accepted' stories into?
91
+ 1. Accepted
92
+ 2. Backlog
93
+ 3. Bugs
94
+ 4. Delivered
95
+ 5. Finished
96
+ 6. Icebox
97
+ 7. In Progress
98
+ 8. Rejected
99
+ 9. Releases
100
+ 10. [don't import these stories]
101
+ Please select an option : 10
102
+
103
+ Which Trello list would you like to put 'rejected' stories into?
104
+ 1. Accepted
105
+ 2. Backlog
106
+ 3. Bugs
107
+ 4. Delivered
108
+ 5. Finished
109
+ 6. Icebox
110
+ 7. In Progress
111
+ 8. Rejected
112
+ 9. Releases
113
+ 10. [don't import these stories]
114
+ Please select an option : 10
115
+
116
+ Which Trello list would you like to put 'backlog' bugs into?
117
+ 1. Accepted
118
+ 2. Backlog
119
+ 3. Bugs
120
+ 4. Delivered
121
+ 5. Finished
122
+ 6. Icebox
123
+ 7. In Progress
124
+ 8. Rejected
125
+ 9. Releases
126
+ 10. [don't import these stories]
127
+ Please select an option : 2
128
+
129
+ Which Trello list would you like to put 'backlog' chores into?
130
+ 1. Accepted
131
+ 2. Backlog
132
+ 3. Bugs
133
+ 4. Delivered
134
+ 5. Finished
135
+ 6. Icebox
136
+ 7. In Progress
137
+ 8. Rejected
138
+ 9. Releases
139
+ 10. [don't import these stories]
140
+ Please select an option : 2
141
+
142
+ Which Trello list would you like to put 'backlog' features into?
143
+ 1. Accepted
144
+ 2. Backlog
145
+ 3. Bugs
146
+ 4. Delivered
147
+ 5. Finished
148
+ 6. Icebox
149
+ 7. In Progress
150
+ 8. Rejected
151
+ 9. Releases
152
+ 10. [don't import these stories]
153
+ Please select an option : 2
154
+
155
+ Which Trello list would you like to put 'backlog' releases into?
156
+ 1. Accepted
157
+ 2. Backlog
158
+ 3. Bugs
159
+ 4. Delivered
160
+ 5. Finished
161
+ 6. Icebox
162
+ 7. In Progress
163
+ 8. Rejected
164
+ 9. Releases
165
+ 10. [don't import these stories]
166
+ Please select an option : 2
167
+
168
+ What color would you like to label bugs with?
169
+ 1. Blue
170
+ 2. Green
171
+ 3. Orange
172
+ 4. Purple
173
+ 5. Red
174
+ 6. Yellow
175
+ 7. [none]
176
+ Please select an option : 5
177
+
178
+ What color would you like to label features with?
179
+ 1. Blue
180
+ 2. Green
181
+ 3. Orange
182
+ 4. Purple
183
+ 5. Red
184
+ 6. Yellow
185
+ 7. [none]
186
+ Please select an option : 2
187
+
188
+ What color would you like to label chores with?
189
+ 1. Blue
190
+ 2. Green
191
+ 3. Orange
192
+ 4. Purple
193
+ 5. Red
194
+ 6. Yellow
195
+ 7. [none]
196
+ Please select an option : 6
197
+
198
+ What color would you like to label releases with?
199
+ 1. Blue
200
+ 2. Green
201
+ 3. Orange
202
+ 4. Purple
203
+ 5. Red
204
+ 6. Yellow
205
+ 7. [none]
206
+ Please select an option : 4
207
+
208
+ Beginning import...
209
+ Creating a card for chore 'My example chore'.
210
+ ...
211
+
212
+ Obtaining API credentials
213
+ -------------------------
214
+
215
+ You can get your Trello application key by logging into Trello, and then visiting [https://trello.com/1/appKey/generate](https://trello.com/1/appKey/generate)
216
+
217
+ Your 32-character application key will be listed in the first box.
218
+
219
+ To obtain your Trello member token, visit the following URL, substuting your Trello application key for *APP_KEY*:
220
+
221
+ [https://trello.com/1/authorize?key=APP_KEY&name=Pivotal%20To%20Trello&response_type=token&scope=read,write](https://trello.com/1/authorize?key=APP_KEY&name=Pivotal%20To%20Trello&response_type=token&scope=read,write)
222
+
223
+ Click the *Allow* button, and you will be presented with a 64-character token.
224
+
225
+ See the [Trello documentation](https://trello.com/docs/gettingstarted/index.html#getting-an-application-key) for more details.
226
+
227
+ The Pivotal Tracker token can be found at the bottom of your [Pivotal profile page](https://www.pivotaltracker.com/profile).
228
+
229
+ Change history
230
+ --------------
231
+
232
+ * **Version 0.1.0 (2014-01-13)** : Initial version.
233
+
234
+ Bug Reports
235
+ -----------
236
+
237
+ If you come across any problems, please [create a ticket](https://github.com/recurser/pivotal-to-trello/issues) and I'll try to get it fixed as soon as possible.
238
+
239
+ Contributing
240
+ ------------
241
+
242
+ Once you've made your changes:
243
+
244
+ 1. [Fork](http://help.github.com/fork-a-repo/) pivotal-to-trello
245
+ 2. Create a topic branch - `git checkout -b my_branch`
246
+ 3. Push to your branch - `git push origin my_branch`
247
+ 4. Create a [Pull Request](http://help.github.com/pull-requests/) from your branch
248
+ 5. That's it!
249
+
250
+
251
+ Author
252
+ ------
253
+
254
+ Dave Perrett :: hello@daveperrett.com :: [@daveperrett](http://twitter.com/daveperrett)
255
+
256
+
257
+ Copyright
258
+ ---------
259
+
260
+ Copyright (c) 2010 Dave Perrett. See [License](https://github.com/recurser/jquery-i18n/blob/master/LICENSE) for details.
data/Rakefile ADDED
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "pivotal-to-trello"
18
+ gem.license = "MIT"
19
+ gem.homepage = "http://github.com/recurser/pivotal-to-trello"
20
+ gem.summary = %Q{Pivotal Tracker to Trello exporter}
21
+ gem.description = %Q{Pulls stories from Pivotal Tracker and imports them into Trello}
22
+ gem.email = "hello@daveperrett.com"
23
+ gem.authors = ["Dave Perrett"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rspec/core'
29
+ require 'rspec/core/rake_task'
30
+ RSpec::Core::RakeTask.new(:spec) do |spec|
31
+ spec.pattern = FileList['spec/**/*_spec.rb']
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
35
+ spec.pattern = 'spec/**/*_spec.rb'
36
+ spec.rcov = true
37
+ end
38
+
39
+ task :default => :spec
40
+
41
+ require 'rdoc/task'
42
+ Rake::RDocTask.new do |rdoc|
43
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
44
+
45
+ rdoc.rdoc_dir = 'rdoc'
46
+ rdoc.title = "pivotal-to-trello #{version}"
47
+ rdoc.rdoc_files.include('README*')
48
+ rdoc.rdoc_files.include('lib/**/*.rb')
49
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ file = __FILE__
4
+ if File.lstat(file).symlink?
5
+ require 'pathname'
6
+ file = Pathname.new(file).realpath
7
+ end
8
+
9
+ require File.expand_path(File.dirname(file) + '/../lib/pivotal-to-trello')
10
+ PivotalToTrello::Runner.execute
@@ -0,0 +1,16 @@
1
+ unless defined? P2T_ROOT
2
+ $LOAD_PATH << File.expand_path(File.dirname(__FILE__))
3
+ P2T_ROOT = File.expand_path(File.dirname(__FILE__) + '/../')
4
+ P2T_LIB = File.join(P2T_ROOT, 'lib', 'pivotal_to_trello')
5
+ end
6
+
7
+ module PivotalToTrello
8
+ def self.version
9
+ File.exist?(File.join(P2T_ROOT, 'VERSION')) ? File.read(File.join(P2T_ROOT, 'VERSION')) : 'Unknown'
10
+ end
11
+
12
+ require 'pivotal_to_trello/core'
13
+ require 'pivotal_to_trello/pivotal_wrapper'
14
+ require 'pivotal_to_trello/runner'
15
+ require 'pivotal_to_trello/trello_wrapper'
16
+ end
@@ -0,0 +1,114 @@
1
+ require 'rubygems'
2
+ require 'highline/import'
3
+
4
+ module PivotalToTrello
5
+ # The core entry point of the gem, which handles the import process.
6
+ class Core
7
+
8
+ # Constructor
9
+ def initialize(options = OpenStruct.new)
10
+ @options = options
11
+ end
12
+
13
+ # Generates a film clip based on the given input audio file, and saves it
14
+ # to the given output path.
15
+ def import!
16
+ prompt_for_details
17
+
18
+ puts "\nBeginning import..."
19
+ pivotal.stories(options.pivotal_project_id).each do |story|
20
+ if story.current_state == 'accepted'
21
+ list_id = options.accepted_list_id
22
+ elsif story.current_state == 'rejected'
23
+ list_id = options.rejected_list_id
24
+ elsif story.current_state == 'finished'
25
+ list_id = options.finished_list_id
26
+ elsif story.current_state == 'delivered'
27
+ list_id = options.delivered_list_id
28
+ elsif story.current_state == 'started'
29
+ list_id = options.current_list_id
30
+ elsif story.current_state == 'unscheduled'
31
+ list_id = options.icebox_list_id
32
+ elsif story.current_state == 'unstarted' && story.story_type == 'feature'
33
+ list_id = options.feature_list_id
34
+ elsif story.current_state == 'unstarted' && story.story_type == 'chore'
35
+ list_id = options.chore_list_id
36
+ elsif story.current_state == 'unstarted' && story.story_type == 'bug'
37
+ list_id = options.bug_list_id
38
+ elsif story.current_state == 'unstarted' && story.story_type == 'release'
39
+ list_id = options.release_list_id
40
+ else
41
+ puts "Ignoring story #{story.id} - type is '#{story.story_type}', state is '#{story.current_state}'"
42
+ end
43
+
44
+ if story.story_type == 'bug' && options.bug_label
45
+ label = options.bug_label
46
+ elsif story.story_type == 'chore' && options.feature_label
47
+ label = options.feature_label
48
+ elsif story.story_type == 'feature' && options.chore_label
49
+ label = options.chore_label
50
+ elsif story.story_type == 'release' && options.release_label
51
+ label = options.release_label
52
+ end
53
+
54
+ if list_id
55
+ card = trello.create_card(list_id, story)
56
+ trello.add_label(card, label)
57
+ end
58
+ end
59
+ end
60
+
61
+ # Returns the options struct.
62
+ def options
63
+ @options
64
+ end
65
+
66
+ private
67
+
68
+ # Prompts the user for details about the imort/export.
69
+ def prompt_for_details
70
+ options.pivotal_project_id = prompt_selection('Which Pivotal project would you like to export?', pivotal.project_choices)
71
+ options.trello_board_id = prompt_selection('Which Trello board would you like to import into?', trello.board_choices)
72
+ options.icebox_list_id = prompt_selection("Which Trello list would you like to put 'icebox' stories into?", trello.list_choices(options.trello_board_id))
73
+ options.current_list_id = prompt_selection("Which Trello list would you like to put 'current' stories into?", trello.list_choices(options.trello_board_id))
74
+ options.finished_list_id = prompt_selection("Which Trello list would you like to put 'finished' stories into?", trello.list_choices(options.trello_board_id))
75
+ options.delivered_list_id = prompt_selection("Which Trello list would you like to put 'delivered' stories into?", trello.list_choices(options.trello_board_id))
76
+ options.accepted_list_id = prompt_selection("Which Trello list would you like to put 'accepted' stories into?", trello.list_choices(options.trello_board_id))
77
+ options.rejected_list_id = prompt_selection("Which Trello list would you like to put 'rejected' stories into?", trello.list_choices(options.trello_board_id))
78
+ options.bug_list_id = prompt_selection("Which Trello list would you like to put 'backlog' bugs into?", trello.list_choices(options.trello_board_id))
79
+ options.chore_list_id = prompt_selection("Which Trello list would you like to put 'backlog' chores into?", trello.list_choices(options.trello_board_id))
80
+ options.feature_list_id = prompt_selection("Which Trello list would you like to put 'backlog' features into?", trello.list_choices(options.trello_board_id))
81
+ options.release_list_id = prompt_selection("Which Trello list would you like to put 'backlog' releases into?", trello.list_choices(options.trello_board_id))
82
+ options.bug_label = prompt_selection('What color would you like to label bugs with?', trello.label_choices)
83
+ options.feature_label = prompt_selection('What color would you like to label features with?', trello.label_choices)
84
+ options.chore_label = prompt_selection('What color would you like to label chores with?', trello.label_choices)
85
+ options.release_label = prompt_selection('What color would you like to label releases with?', trello.label_choices)
86
+ end
87
+
88
+ # Prompts the user to select an option from the given list of choices.
89
+ def prompt_selection(question, choices)
90
+ say("\n#{question}")
91
+ choose do |menu|
92
+ menu.prompt = "Please select an option : "
93
+
94
+ choices.each do |key, value|
95
+ menu.choice value do
96
+ return key
97
+ end
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ # Returns an instance of the pivotal wrapper.
104
+ def pivotal
105
+ @pivotal ||= PivotalToTrello::PivotalWrapper.new(options.pivotal_token)
106
+ end
107
+
108
+ # Returns an instance of the trello wrapper.
109
+ def trello
110
+ @trello ||= PivotalToTrello::TrelloWrapper.new(options.trello_key, options.trello_token)
111
+ end
112
+
113
+ end
114
+ end
@@ -0,0 +1,34 @@
1
+ require 'pivotal-tracker'
2
+
3
+ module PivotalToTrello
4
+ # Interface to the Pivotal Tracker API.
5
+ class PivotalWrapper
6
+
7
+ # Constructor
8
+ def initialize(token)
9
+ ::PivotalTracker::Client.token = token
10
+ end
11
+
12
+ # Returns a hash of available projects keyed on project ID.
13
+ def project_choices
14
+ ::PivotalTracker::Project.all.inject({}) do |hash, project|
15
+ hash[project.id] = project.name
16
+ hash
17
+ end
18
+ end
19
+
20
+ # Returns all stories for the given project.
21
+ def stories(project_id)
22
+ project(project_id).stories.all
23
+ end
24
+
25
+ private
26
+
27
+ # Returns the Pivotal project that we're exporting.
28
+ def project(project_id)
29
+ @projects ||= {}
30
+ @projects[project_id] ||= ::PivotalTracker::Project.find(project_id)
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,108 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+
4
+ module PivotalToTrello
5
+ # Utility class to handle the different commands that the 'pivotal-to-trello'
6
+ # command offers.
7
+ class Runner
8
+ # Start running a pivotal-to-trello command from the passed-in arguments.
9
+ def self.execute
10
+ runner = new
11
+ options = runner.parse_options(ARGV)
12
+ runner.execute!(options)
13
+ end
14
+
15
+ # Dispatch central.
16
+ def execute!(options)
17
+ case options.action
18
+ when :import then import(options)
19
+ end
20
+ end
21
+
22
+ # Parses the options, and displays help messages if the options given are
23
+ # incorrect.
24
+ def parse_options(args)
25
+ options = OpenStruct.new
26
+
27
+ commands = {
28
+ 'import' => OptionParser.new do |opts|
29
+ opts.banner = 'Usage: pivotal-to-trello import [options]'
30
+ opts.separator ''
31
+ opts.separator 'All arguments except for -v and -h are required.'
32
+ opts.separator ''
33
+ opts.separator 'Options:'
34
+
35
+ opts.on('-k', '--trello-key KEY', 'Trello application key') do |trello_key|
36
+ options.trello_key = trello_key
37
+ end
38
+ opts.on('-t', '--trello-token TOKEN', 'Trello member token') do |trello_token|
39
+ options.trello_token = trello_token
40
+ end
41
+ opts.on('-p', '--pivotal-token TOKEN', 'Pivotal Tracker API token') do |pivotal_token|
42
+ options.pivotal_token = pivotal_token
43
+ end
44
+
45
+ # Miscellaneous.
46
+ opts.on_tail('-v', '--version', 'Show version information') { show_version }
47
+ opts.on_tail('-h', '--help', 'Show this message') do
48
+ STDOUT.write opts
49
+ exit
50
+ end
51
+
52
+ opts.on do
53
+ if options.trello_key && \
54
+ options.trello_token && \
55
+ options.pivotal_token
56
+ options.action = :import
57
+ else
58
+ # Output a help message unless the required options have been specified.
59
+ options.action = :error
60
+ STDOUT.write commands['import']
61
+ end
62
+ end
63
+ end,
64
+ }
65
+
66
+ global = OptionParser.new do |opts|
67
+ opts.banner = "Usage: pivotal-to-trello [#{commands.keys.join(', ')}] [options]"
68
+
69
+ opts.separator ''
70
+ opts.separator 'pivotal-to-trello is a library for importing stories from Pivotal Tracker into Trello.'
71
+ opts.separator ''
72
+ opts.separator "Use pivotal-to-trello [#{commands.keys.join(', ')}] -h to find out more about a specific command"
73
+ opts.separator ''
74
+ opts.separator 'Other options:'
75
+
76
+ opts.on_tail('-v', '--version', 'Show version information') do
77
+ show_version
78
+ exit
79
+ end
80
+
81
+ opts.on_tail('-h', '--help', 'Show this message') do
82
+ STDOUT.write opts
83
+ exit
84
+ end
85
+ end
86
+
87
+ global.order!
88
+ command = args.shift
89
+ commands[command].order! if commands[command]
90
+
91
+ # Output a help message unless a command has been specified.
92
+ STDOUT.write global unless options.action
93
+
94
+ options
95
+ end
96
+
97
+ # Generates a film clip for the given input file, and saves it to the given
98
+ # output path.
99
+ def import(options)
100
+ PivotalToTrello::Core.new(options).import!
101
+ end
102
+
103
+ # Display the current version of Ruby-Processing.
104
+ def show_version
105
+ STDOUT.write "pivotal-to-trello version #{PivotalToTrello.version}"
106
+ end
107
+ end # class Runner
108
+ end # module PivotalToTrello
@@ -0,0 +1,107 @@
1
+ require 'trello'
2
+
3
+ module PivotalToTrello
4
+ # Interface to the Trello API.
5
+ class TrelloWrapper
6
+
7
+ # Constructor
8
+ def initialize(key, token)
9
+ Trello.configure do |config|
10
+ config.developer_public_key = key
11
+ config.member_token = token
12
+ end
13
+ end
14
+
15
+ # Creates a card in the given list if one with the same name doesn't already exist.
16
+ def create_card(list_id, pivotal_story)
17
+ card = get_card(list_id, pivotal_story.name, pivotal_story.description)
18
+ card ||= begin
19
+ puts "Creating a card for #{pivotal_story.story_type} '#{pivotal_story.name}'."
20
+ card = Trello::Card.create(
21
+ :name => pivotal_story.name,
22
+ :desc => pivotal_story.description,
23
+ :list_id => list_id
24
+ )
25
+
26
+ pivotal_story.notes.all.each do |note|
27
+ card.add_comment("[#{note.author}] #{note.text.to_s.strip}") unless note.text.to_s.strip.empty?
28
+ end
29
+
30
+ card
31
+ end
32
+
33
+ key = card_hash(card.name, card.desc)
34
+ @cards ||= {}
35
+ @cards[list_id] ||= {}
36
+ @cards[list_id][key] = card
37
+ end
38
+
39
+ # Returns a hash of available boards, keyed on board ID.
40
+ def board_choices
41
+ Trello::Board.all.inject({}) do |hash, board|
42
+ hash[board.id] = board.name
43
+ hash
44
+ end
45
+ end
46
+
47
+ # Returns a hash of available lists for the given board, keyed on board ID.
48
+ def list_choices(board_id)
49
+ # Cache the list to improve performance.
50
+ @lists ||= {}
51
+ @lists[board_id] ||= begin
52
+ choices = Trello::Board.find(board_id).lists.inject({}) do |hash, list|
53
+ hash[list.id] = list.name
54
+ hash
55
+ end
56
+ choices = Hash[choices.sort_by { |_, v| v }]
57
+ choices[false] = "[don't import these stories]"
58
+ choices
59
+ end
60
+
61
+ @lists[board_id]
62
+ end
63
+
64
+ # Returns a list of all cards in the given list, keyed on name.
65
+ def cards_for_list(list_id)
66
+ @cards ||= {}
67
+ @cards[list_id] ||= Trello::List.find(list_id).cards.inject({}) do |hash, card|
68
+ hash[card_hash(card.name, card.desc)] = card
69
+ hash
70
+ end
71
+
72
+ @cards[list_id]
73
+ end
74
+
75
+ # Adds the given label to the card.
76
+ def add_label(card, label)
77
+ card.add_label(label) unless card.labels.collect { |label| label.color }.include?(label)
78
+ end
79
+
80
+ # Returns a list of colors that can be used to label cards.
81
+ def label_choices
82
+ {
83
+ 'blue' => 'Blue',
84
+ 'green' => 'Green',
85
+ 'orange' => 'Orange',
86
+ 'purple' => 'Purple',
87
+ 'red' => 'Red',
88
+ 'yellow' => 'Yellow',
89
+ false => '[none]'
90
+ }
91
+ end
92
+
93
+ private
94
+
95
+ # Returns a unique identifier for this list/name/description combination.
96
+ def card_hash(name, description)
97
+ Digest::SHA1.hexdigest("#{name}_#{description}")
98
+ end
99
+
100
+ # Returns a card with the given name and description if it exists in the given list, nil otherwise.
101
+ def get_card(list_id, name, description)
102
+ key = card_hash(name, description)
103
+ cards_for_list(list_id)[key] if !cards_for_list(list_id)[key].nil?
104
+ end
105
+
106
+ end
107
+ end
@@ -0,0 +1,76 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: pivotal-to-trello 0.1.0 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "pivotal-to-trello"
9
+ s.version = "0.1.0"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
13
+ s.authors = ["Dave Perrett"]
14
+ s.date = "2014-01-13"
15
+ s.description = "Pulls stories from Pivotal Tracker and imports them into Trello"
16
+ s.email = "hello@daveperrett.com"
17
+ s.executables = ["pivotal-to-trello"]
18
+ s.extra_rdoc_files = [
19
+ "LICENSE.txt",
20
+ "README.markdown"
21
+ ]
22
+ s.files = [
23
+ ".document",
24
+ "Gemfile",
25
+ "LICENSE.txt",
26
+ "README.markdown",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "bin/pivotal-to-trello",
30
+ "lib/pivotal-to-trello.rb",
31
+ "lib/pivotal_to_trello/core.rb",
32
+ "lib/pivotal_to_trello/pivotal_wrapper.rb",
33
+ "lib/pivotal_to_trello/runner.rb",
34
+ "lib/pivotal_to_trello/trello_wrapper.rb",
35
+ "pivotal-to-trello.gemspec",
36
+ "spec/pivotal_to_trello/core_spec.rb",
37
+ "spec/pivotal_to_trello/pivotal_wrapper_spec.rb",
38
+ "spec/pivotal_to_trello/trello_wrapper_spec.rb",
39
+ "spec/spec_helper.rb"
40
+ ]
41
+ s.homepage = "http://github.com/recurser/pivotal-to-trello"
42
+ s.licenses = ["MIT"]
43
+ s.rubygems_version = "2.2.1"
44
+ s.summary = "Pivotal Tracker to Trello exporter"
45
+
46
+ if s.respond_to? :specification_version then
47
+ s.specification_version = 4
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<highline>, [">= 0"])
51
+ s.add_runtime_dependency(%q<ruby-trello>, [">= 1.0.0"])
52
+ s.add_runtime_dependency(%q<pivotal-tracker>, [">= 0"])
53
+ s.add_development_dependency(%q<rspec>, [">= 0"])
54
+ s.add_development_dependency(%q<rdoc>, [">= 0"])
55
+ s.add_development_dependency(%q<bundler>, [">= 0"])
56
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
57
+ else
58
+ s.add_dependency(%q<highline>, [">= 0"])
59
+ s.add_dependency(%q<ruby-trello>, [">= 1.0.0"])
60
+ s.add_dependency(%q<pivotal-tracker>, [">= 0"])
61
+ s.add_dependency(%q<rspec>, [">= 0"])
62
+ s.add_dependency(%q<rdoc>, [">= 0"])
63
+ s.add_dependency(%q<bundler>, [">= 0"])
64
+ s.add_dependency(%q<jeweler>, [">= 0"])
65
+ end
66
+ else
67
+ s.add_dependency(%q<highline>, [">= 0"])
68
+ s.add_dependency(%q<ruby-trello>, [">= 1.0.0"])
69
+ s.add_dependency(%q<pivotal-tracker>, [">= 0"])
70
+ s.add_dependency(%q<rspec>, [">= 0"])
71
+ s.add_dependency(%q<rdoc>, [">= 0"])
72
+ s.add_dependency(%q<bundler>, [">= 0"])
73
+ s.add_dependency(%q<jeweler>, [">= 0"])
74
+ end
75
+ end
76
+
@@ -0,0 +1,101 @@
1
+ require File.expand_path(File.dirname(File.dirname(__FILE__)) + '/spec_helper')
2
+
3
+ describe 'Core' do
4
+ let(:core) { PivotalToTrello::Core.new(mock_options) }
5
+ let(:pivotal) { mock_pivotal_wrapper }
6
+ let(:trello) { mock_trello_wrapper }
7
+
8
+ before(:each) do
9
+ IO.any_instance.stub(:puts)
10
+ core.stub(:pivotal => pivotal)
11
+ core.stub(:trello => trello)
12
+
13
+ core.stub(:prompt_selection).with('Which Pivotal project would you like to export?', pivotal.project_choices).and_return('pivotal_project_id')
14
+ core.stub(:prompt_selection).with('Which Trello board would you like to import into?', trello.board_choices).and_return('trello_board_id')
15
+ core.stub(:prompt_selection).with("Which Trello list would you like to put 'icebox' stories into?", trello.list_choices).and_return('icebox_list_id')
16
+ core.stub(:prompt_selection).with("Which Trello list would you like to put 'current' stories into?", trello.list_choices).and_return('current_list_id')
17
+ core.stub(:prompt_selection).with("Which Trello list would you like to put 'finished' stories into?", trello.list_choices).and_return('finished_list_id')
18
+ core.stub(:prompt_selection).with("Which Trello list would you like to put 'delivered' stories into?", trello.list_choices).and_return('delivered_list_id')
19
+ core.stub(:prompt_selection).with("Which Trello list would you like to put 'accepted' stories into?", trello.list_choices).and_return('accepted_list_id')
20
+ core.stub(:prompt_selection).with("Which Trello list would you like to put 'rejected' stories into?", trello.list_choices).and_return('rejected_list_id')
21
+ core.stub(:prompt_selection).with("Which Trello list would you like to put 'backlog' bugs into?", trello.list_choices).and_return('bug_list_id')
22
+ core.stub(:prompt_selection).with("Which Trello list would you like to put 'backlog' chores into?", trello.list_choices).and_return('chore_list_id')
23
+ core.stub(:prompt_selection).with("Which Trello list would you like to put 'backlog' features into?", trello.list_choices).and_return('feature_list_id')
24
+ core.stub(:prompt_selection).with("Which Trello list would you like to put 'backlog' releases into?", trello.list_choices).and_return('release_list_id')
25
+ core.stub(:prompt_selection).with('What color would you like to label bugs with?', trello.label_choices).and_return('bug_label')
26
+ core.stub(:prompt_selection).with('What color would you like to label features with?', trello.label_choices).and_return('feature_label')
27
+ core.stub(:prompt_selection).with('What color would you like to label chores with?', trello.label_choices).and_return('chore_label')
28
+ core.stub(:prompt_selection).with('What color would you like to label releases with?', trello.label_choices).and_return('release_label')
29
+ end
30
+
31
+ context '#import!' do
32
+ it 'prompts the user for details' do
33
+ core.import!
34
+ core.options.should == mock_options
35
+ end
36
+
37
+ describe 'story handling' do
38
+ let(:card) { mock_trello_card }
39
+ let(:story) { mock_pivotal_story }
40
+
41
+ before(:each) do
42
+ pivotal.should_receive(:stories).and_return([story])
43
+ trello.stub(:add_label => true, :create_card => card)
44
+ end
45
+
46
+ it 'handles accepted stories' do
47
+ story.stub(:current_state => 'accepted')
48
+ trello.should_receive(:create_card).with(core.options.accepted_list_id, story).and_return(card)
49
+ core.import!
50
+ end
51
+
52
+ it 'handles rejected stories' do
53
+ story.stub(:current_state => 'rejected')
54
+ trello.should_receive(:create_card).with(core.options.rejected_list_id, story).and_return(card)
55
+ core.import!
56
+ end
57
+
58
+ it 'handles finished stories' do
59
+ story.stub(:current_state => 'finished')
60
+ trello.should_receive(:create_card).with(core.options.finished_list_id, story).and_return(card)
61
+ core.import!
62
+ end
63
+
64
+ it 'handles delivered stories' do
65
+ story.stub(:current_state => 'delivered')
66
+ trello.should_receive(:create_card).with(core.options.delivered_list_id, story).and_return(card)
67
+ core.import!
68
+ end
69
+
70
+ it 'handles unstarted features' do
71
+ story.stub(:current_state => 'unstarted', :story_type => 'feature')
72
+ trello.should_receive(:create_card).with(core.options.feature_list_id, story).and_return(card)
73
+ core.import!
74
+ end
75
+
76
+ it 'handles unstarted chores' do
77
+ story.stub(:current_state => 'unstarted', :story_type => 'chore')
78
+ trello.should_receive(:create_card).with(core.options.chore_list_id, story).and_return(card)
79
+ core.import!
80
+ end
81
+
82
+ it 'handles unstarted bugs' do
83
+ story.stub(:current_state => 'unstarted', :story_type => 'bug')
84
+ trello.should_receive(:create_card).with(core.options.bug_list_id, story).and_return(card)
85
+ core.import!
86
+ end
87
+
88
+ it 'handles unstarted releases' do
89
+ story.stub(:current_state => 'unstarted', :story_type => 'release')
90
+ trello.should_receive(:create_card).with(core.options.release_list_id, story).and_return(card)
91
+ core.import!
92
+ end
93
+
94
+ it 'labels stories' do
95
+ story.stub(:story_type => 'bug')
96
+ trello.should_receive(:add_label).with(card, core.options.bug_label)
97
+ core.import!
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,30 @@
1
+ require File.expand_path(File.dirname(File.dirname(__FILE__)) + '/spec_helper')
2
+
3
+ describe 'PivotalWrapper' do
4
+ let(:wrapper) { PivotalToTrello::PivotalWrapper.new('token') }
5
+
6
+ context '#initialize' do
7
+ it 'sets the pivotal token' do
8
+ ::PivotalTracker::Client.should_receive(:token=).with('token')
9
+ PivotalToTrello::PivotalWrapper.new('token')
10
+ end
11
+ end
12
+
13
+ context '#project_choices' do
14
+ it 'returns a hash of pivotal projects' do
15
+ project = OpenStruct.new(:id => 'id', :name => 'My Project')
16
+ ::PivotalTracker::Project.should_receive(:all).and_return([project])
17
+ wrapper.project_choices.should == { 'id' => 'My Project'}
18
+ end
19
+ end
20
+
21
+ context '#stories' do
22
+ it 'returns an array of pivotal stories' do
23
+ story = mock_pivotal_story
24
+ project = double(PivotalTracker::Project)
25
+ ::PivotalTracker::Project.should_receive(:find).with('project_id').and_return(project)
26
+ project.stub_chain(:stories, :all).and_return([story])
27
+ wrapper.stories('project_id').should == [story]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,114 @@
1
+ require File.expand_path(File.dirname(File.dirname(__FILE__)) + '/spec_helper')
2
+
3
+ describe 'TrelloWrapper' do
4
+ let(:wrapper) { PivotalToTrello::TrelloWrapper.new('key', 'token') }
5
+
6
+ before(:each) do
7
+ IO.any_instance.stub(:puts)
8
+ end
9
+
10
+ context '#initialize' do
11
+ it 'sets the auth credentials' do
12
+ config = double
13
+ Trello.should_receive(:configure).and_yield(config)
14
+ config.should_receive(:developer_public_key=).with('key')
15
+ config.should_receive(:member_token=).with('token')
16
+ PivotalToTrello::TrelloWrapper.new('key', 'token')
17
+ end
18
+ end
19
+
20
+ context '#create_card' do
21
+ let(:card) { mock_trello_card }
22
+
23
+ it 'creates a new card if none exists' do
24
+ story = mock_pivotal_story
25
+ wrapper.should_receive(:get_card).and_return(nil)
26
+ Trello::Card.should_receive(:create).with(
27
+ :name => story.name,
28
+ :desc => story.description,
29
+ :list_id => 'list_id'
30
+ ).and_return(card)
31
+ wrapper.create_card('list_id', story).should == card
32
+ end
33
+
34
+ it 'does not create a new card if one exists with the same name' do
35
+ story = mock_pivotal_story(:name => 'My Card')
36
+ Trello::List.stub_chain(:find, :cards).and_return([card])
37
+ Trello::Card.should_not_receive(:create)
38
+ wrapper.create_card('list_id', story).should == card
39
+ end
40
+
41
+ it 'creates a new card if one exists with a different name' do
42
+ story = mock_pivotal_story(:name => 'My Foo')
43
+ Trello::List.stub_chain(:find, :cards).and_return([card])
44
+ Trello::Card.should_receive(:create).with(
45
+ :name => story.name,
46
+ :desc => story.description,
47
+ :list_id => 'list_id'
48
+ ).and_return(card)
49
+ wrapper.create_card('list_id', story).should == card
50
+ end
51
+
52
+ it 'adds comments' do
53
+ note = OpenStruct.new(:text => 'My Note', :author => 'John Smith')
54
+ story = mock_pivotal_story
55
+ story.stub_chain(:notes, :all).and_return([note])
56
+ wrapper.should_receive(:get_card).and_return(nil)
57
+ Trello::Card.should_receive(:create).and_return(card)
58
+ card.should_receive(:add_comment).with('[John Smith] My Note')
59
+ wrapper.create_card('list_id', story).should == card
60
+ end
61
+ end
62
+
63
+ context '#board_choices' do
64
+ it 'returns a hash of Trello boards' do
65
+ board = OpenStruct.new(:id => 'id', :name => 'My Board')
66
+ Trello::Board.should_receive(:all).and_return([board])
67
+ wrapper.board_choices.should == { 'id' => 'My Board'}
68
+ end
69
+ end
70
+
71
+ context '#list_choices' do
72
+ it 'returns a hash of Trello lists' do
73
+ board = double(Trello::Board)
74
+ list = OpenStruct.new(:id => 'id', :name => 'My List')
75
+ Trello::Board.should_receive(:find).with('board_id').and_return(board)
76
+ board.should_receive(:lists).and_return([list])
77
+ wrapper.list_choices('board_id').should == {
78
+ 'id' => 'My List',
79
+ false => "[don't import these stories]",
80
+ }
81
+ end
82
+ end
83
+
84
+ context '#cards_for_list' do
85
+ it 'returns a hash of Trello lists' do
86
+ list = double(Trello::List)
87
+ card = OpenStruct.new(:name => 'My Card', :desc => 'My Description')
88
+ Trello::List.should_receive(:find).with('list_id').and_return(list)
89
+ list.should_receive(:cards).and_return([card])
90
+ expected = {'193060beddd00d64259bdc1271d6c5a330e92e7d' => card}
91
+ wrapper.cards_for_list('list_id').should == expected
92
+ # Test caching.
93
+ Trello::List.should_not_receive(:find)
94
+ wrapper.cards_for_list('list_id').should == expected
95
+ end
96
+ end
97
+
98
+ context '#add_label' do
99
+ it 'adds a label if it does not already exist' do
100
+ card = mock_trello_card
101
+ card.stub(:labels => [])
102
+ card.should_receive(:add_label).with('red')
103
+ wrapper.add_label(card, 'red')
104
+ end
105
+
106
+ it 'does not add a label if it already exists' do
107
+ card = mock_trello_card
108
+ card.stub(:labels => [OpenStruct.new(:color => 'red')])
109
+ card.should_not_receive(:add_label)
110
+ wrapper.add_label(card, 'red')
111
+ end
112
+ end
113
+
114
+ end
@@ -0,0 +1,70 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'pivotal-to-trello'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+ end
12
+
13
+ def mock_pivotal_wrapper
14
+ pivotal = double(PivotalToTrello::PivotalWrapper)
15
+ pivotal.stub(:project_choices => {})
16
+ pivotal.stub(:stories => [])
17
+ pivotal
18
+ end
19
+
20
+ def mock_trello_wrapper
21
+ trello = double(PivotalToTrello::TrelloWrapper)
22
+ trello.stub(:board_choices => {})
23
+ trello.stub(:list_choices => {})
24
+ trello.stub(:label_choices => {})
25
+ trello
26
+ end
27
+
28
+ def mock_pivotal_story(options = {})
29
+ options = {
30
+ :name => 'My Story',
31
+ :description => 'My Description',
32
+ :current_state => 'unstarted',
33
+ :story_type => 'feature',
34
+ }.merge(options)
35
+ story = double(PivotalTracker::Story)
36
+ story.stub_chain(:notes, :all).and_return([])
37
+ options.each { |k, v| story.stub(k => v) }
38
+ story
39
+ end
40
+
41
+ def mock_trello_card(options = {})
42
+ options = {
43
+ :name => 'My Card',
44
+ :desc => 'My Description',
45
+ }.merge(options)
46
+ card = double(Trello::Card)
47
+ options.each { |k, v| card.stub(k => v) }
48
+ card
49
+ end
50
+
51
+ def mock_options
52
+ OpenStruct.new({
53
+ :pivotal_project_id => 'pivotal_project_id',
54
+ :trello_board_id => 'trello_board_id',
55
+ :icebox_list_id => 'icebox_list_id',
56
+ :current_list_id => 'current_list_id',
57
+ :finished_list_id => 'finished_list_id',
58
+ :delivered_list_id => 'delivered_list_id',
59
+ :accepted_list_id => 'accepted_list_id',
60
+ :rejected_list_id => 'rejected_list_id',
61
+ :bug_list_id => 'bug_list_id',
62
+ :chore_list_id => 'chore_list_id',
63
+ :feature_list_id => 'feature_list_id',
64
+ :release_list_id => 'release_list_id',
65
+ :bug_label => 'bug_label',
66
+ :feature_label => 'feature_label',
67
+ :chore_label => 'chore_label',
68
+ :release_label => 'release_label',
69
+ })
70
+ end
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pivotal-to-trello
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Dave Perrett
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-13 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: highline
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ruby-trello
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: pivotal-tracker
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rdoc
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: jeweler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Pulls stories from Pivotal Tracker and imports them into Trello
112
+ email: hello@daveperrett.com
113
+ executables:
114
+ - pivotal-to-trello
115
+ extensions: []
116
+ extra_rdoc_files:
117
+ - LICENSE.txt
118
+ - README.markdown
119
+ files:
120
+ - ".document"
121
+ - Gemfile
122
+ - LICENSE.txt
123
+ - README.markdown
124
+ - Rakefile
125
+ - VERSION
126
+ - bin/pivotal-to-trello
127
+ - lib/pivotal-to-trello.rb
128
+ - lib/pivotal_to_trello/core.rb
129
+ - lib/pivotal_to_trello/pivotal_wrapper.rb
130
+ - lib/pivotal_to_trello/runner.rb
131
+ - lib/pivotal_to_trello/trello_wrapper.rb
132
+ - pivotal-to-trello.gemspec
133
+ - spec/pivotal_to_trello/core_spec.rb
134
+ - spec/pivotal_to_trello/pivotal_wrapper_spec.rb
135
+ - spec/pivotal_to_trello/trello_wrapper_spec.rb
136
+ - spec/spec_helper.rb
137
+ homepage: http://github.com/recurser/pivotal-to-trello
138
+ licenses:
139
+ - MIT
140
+ metadata: {}
141
+ post_install_message:
142
+ rdoc_options: []
143
+ require_paths:
144
+ - lib
145
+ required_ruby_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ required_rubygems_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 2.2.1
158
+ signing_key:
159
+ specification_version: 4
160
+ summary: Pivotal Tracker to Trello exporter
161
+ test_files: []