trollolo 0.0.9 → 0.0.10
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/Gemfile +1 -1
- data/README.md +97 -0
- data/lib/card.rb +0 -14
- data/lib/cli.rb +66 -27
- data/lib/empty_column.rb +5 -0
- data/lib/scrum/backlog_mover.rb +47 -0
- data/lib/scrum/card_type_detection.rb +23 -0
- data/lib/scrum/creator.rb +30 -0
- data/lib/scrum/prioritizer.rb +24 -0
- data/lib/scrum/priority_name.rb +15 -0
- data/lib/scrum/scrum_boards.rb +11 -0
- data/lib/scrum/sprint_board.rb +82 -0
- data/lib/scrum/sprint_cleaner.rb +41 -0
- data/lib/scrum/sprint_planning_board.rb +26 -0
- data/lib/scrum.rb +13 -0
- data/lib/scrum_board.rb +17 -7
- data/lib/settings.rb +36 -12
- data/lib/trello_service.rb +22 -0
- data/lib/trello_wrapper.rb +9 -23
- data/lib/trollolo.rb +3 -2
- data/lib/version.rb +1 -1
- data/man/trollolo.1.md +22 -1
- data/spec/data/board.json +3 -4
- data/spec/data/full-board-with-accepted.json +1817 -0
- data/spec/data/full-board.json +3 -27
- data/spec/data/trollolorc +13 -0
- data/spec/data/trollolorc_with_board_aliases +9 -0
- data/spec/data/vcr/creator_custom_config.yml +824 -0
- data/spec/data/vcr/creator_default_config.yml +824 -0
- data/spec/data/vcr/move_backlog.yml +2375 -0
- data/spec/data/vcr/move_backlog_missing_backlog.yml +155 -0
- data/spec/data/vcr/move_backlog_missing_waterbed.yml +364 -0
- data/spec/data/vcr/prioritize_backlog_list.yml +2335 -0
- data/spec/data/vcr/prioritize_no_backlog_list.yml +190 -0
- data/spec/data/vcr/sprint_board.yml +624 -0
- data/spec/data/vcr/sprint_board_no_waterline.yml +556 -0
- data/spec/data/vcr/sprint_cleanup.yml +1239 -7500
- data/spec/data/vcr/sprint_planning_board.yml +239 -0
- data/spec/integration/create_burndown_spec.rb +1 -1
- data/spec/unit/card_spec.rb +0 -41
- data/spec/unit/cli_spec.rb +74 -1
- data/spec/unit/empty_column_spec.rb +9 -0
- data/spec/unit/scrum/backlog_mover_spec.rb +26 -0
- data/spec/unit/scrum/card_type_detection_spec.rb +35 -0
- data/spec/unit/scrum/creator_spec.rb +23 -0
- data/spec/unit/scrum/prioritizer_spec.rb +45 -0
- data/spec/unit/scrum/priority_name_spec.rb +35 -0
- data/spec/unit/scrum/sprint_board_spec.rb +22 -0
- data/spec/unit/scrum/sprint_cleaner_spec.rb +26 -0
- data/spec/unit/scrum/sprint_planning_board_spec.rb +14 -0
- data/spec/unit/scrum_board_spec.rb +90 -0
- data/spec/unit/settings_spec.rb +42 -6
- data/spec/unit/spec_helper.rb +3 -2
- data/spec/unit/support/update_webmock_data +3 -1
- data/spec/unit/support/vcr.rb +8 -8
- data/spec/unit/support/webmocks.rb +9 -0
- data/spec/unit/trello_wrapper_spec.rb +20 -0
- data/trollolo.gemspec +2 -1
- metadata +52 -9
- data/lib/prioritizer.rb +0 -34
- data/lib/sprint_cleanup.rb +0 -54
- data/spec/unit/prioritizer_spec.rb +0 -47
- data/spec/unit/sprint_cleanup_spec.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: edecf120a728ec76d245cb2e64669fd8c075e34c
|
4
|
+
data.tar.gz: ada5a9fc6e5691786b28297662fdf8f7f5d42f83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9365f00bb685300806a0663cc84d075fcb92cea09eba9e04acfd328aa21a82e8ebe694db6c3d0ef731b3f6971a3d0698af7bc70cdf538116a9adad841716eba
|
7
|
+
data.tar.gz: 170c8a25882e6ee7f70fdfa634a7e7b75ac7e4f2d573b830556f8d3428afbe88b5dcb1d468302489fadb0cdda4478070d3021ab73517a29c476eed55214f0a5c
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
# Trollolo Changelog
|
2
2
|
|
3
|
+
## Version 0.0.10
|
4
|
+
|
5
|
+
* Rename `sprint-cleanup` to `cleanup-sprint`.
|
6
|
+
* Configure board, list and label names to trollolorc. The commands
|
7
|
+
`cleanup-sprint`, `move-backlog`, `set-priorities` and `setup-scrum` will use
|
8
|
+
these names. You will still need to provide board IDs, or their aliases, as
|
9
|
+
several boards can share the same name.
|
10
|
+
* Add `setup-scrum` command to create all necessary elements of our scrumb board
|
11
|
+
as configured in trollolorc or using the defaults. A sample configuration can
|
12
|
+
be found in `spec/data/trollolorc`. Fixes #57
|
13
|
+
* Add option for backlog list name in `set-priorities`.
|
14
|
+
* Add `move-backlog` command for moving a backlog from a planning to a sprint board
|
15
|
+
* Handle boards which have an "Accepted" column in addition to a "Done" column
|
16
|
+
|
3
17
|
## Version 0.0.9
|
4
18
|
|
5
19
|
* Add `sprint-cleanup` command to move cards back from the sprint board to the
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -57,6 +57,24 @@ To create a member token for the `set-priority` command use this URL instead:
|
|
57
57
|
|
58
58
|
The board id is the cryptic string in the URL of your board.
|
59
59
|
|
60
|
+
The `.trollolorc` file can also be used to set aliases for board ids. When set,
|
61
|
+
you will be able to use the alias instead of the board-id in the various
|
62
|
+
commands. E.g.
|
63
|
+
|
64
|
+
With the following configuration
|
65
|
+
|
66
|
+
```
|
67
|
+
board_aliases:
|
68
|
+
MyTrelloBoard: 53186e8391ef8671265ebf9e
|
69
|
+
|
70
|
+
```
|
71
|
+
|
72
|
+
You can issue the command:
|
73
|
+
|
74
|
+
```
|
75
|
+
trollolo get-cards --board-id=MyTrelloBoard
|
76
|
+
```
|
77
|
+
|
60
78
|
## Creating burndown charts
|
61
79
|
|
62
80
|
Trollolo implements a simple work flow for creating burndown charts from the
|
@@ -129,3 +147,82 @@ Some more info can be found in the command line help with `trollolo help` and
|
|
129
147
|
### Example
|
130
148
|
|
131
149
|

|
150
|
+
|
151
|
+
## Other SCRUM commands
|
152
|
+
|
153
|
+
Trollolo supports SCRUM on Trello by using one board for the sprint and another one for planning.
|
154
|
+
On these boards several lists are used to organize stories.
|
155
|
+
|
156
|
+
The `setup-scrum` command creates the necessary elements. The names are taken from the configuration.
|
157
|
+
If you change the names in Trello, you need to update theconfiguration in `trollolorc`.
|
158
|
+
|
159
|
+
At the end of a sprint, after the review meeting, remaining cards can be moved back to the planning
|
160
|
+
board with `cleanup-sprint`.
|
161
|
+
Once the sprint backlog is ready, priorities can be added to the card titles with `prioritize`.
|
162
|
+
Move the planning backlog to the sprint board with `move-backlog`.
|
163
|
+
|
164
|
+
### Labels
|
165
|
+
|
166
|
+
* `sticky`: used to mark cards which are not moved, like the goal card
|
167
|
+
* `waterline`: for cards which are under the waterline
|
168
|
+
|
169
|
+
### Lists
|
170
|
+
|
171
|
+
* `sprint_backlog`: this list contains the stories of the current sprint
|
172
|
+
* `sprint_qa`: any cards in the current sprint which need QA
|
173
|
+
* `sprint_doing`: cards currently being worked on
|
174
|
+
* `planning_backlog`: used to plan the next sprint, these cards will be prioritized
|
175
|
+
* `planning_ready`: contains cards which are not yet estimated and therefore not in the backlog
|
176
|
+
|
177
|
+
### Default Configuration
|
178
|
+
|
179
|
+
These are the default names, add this to `trollolorc` and change as necessary.
|
180
|
+
|
181
|
+
scrum:
|
182
|
+
board_names:
|
183
|
+
planning: Planning Board
|
184
|
+
sprint: Sprint Board
|
185
|
+
label_names:
|
186
|
+
sticky: Sticky
|
187
|
+
waterline: Waterline
|
188
|
+
list_names:
|
189
|
+
sprint_backlog: Sprint Backlog
|
190
|
+
sprint_qa: QA
|
191
|
+
sprint_doing: Doing
|
192
|
+
planning_backlog: Backlog
|
193
|
+
planning_ready: Ready for Estimation
|
194
|
+
|
195
|
+
The board names are not used to find the boards on trello.
|
196
|
+
Since several boards can share the same name, they are only used when creating the SCRUM setup.
|
197
|
+
|
198
|
+
### Examples
|
199
|
+
|
200
|
+
Create the boards and lists:
|
201
|
+
|
202
|
+
trollolo setup-scrum
|
203
|
+
|
204
|
+
Lookup the ID of the created boards and use them as arguments:
|
205
|
+
|
206
|
+
# https://trello.com/b/123abC/sprint-board
|
207
|
+
# https://trello.com/b/GHi456/planning-board
|
208
|
+
|
209
|
+
trollolo cleanup-sprint --board-id=123abC --target-board-id=GHi456
|
210
|
+
trollolo set-priorities --board-id=GHi456
|
211
|
+
trollolo move-backlog --planning-board-id=GHi456 --sprint-board-id=123abC
|
212
|
+
|
213
|
+
You can use aliases, as described in the configuration section, instead of IDs.
|
214
|
+
|
215
|
+
## Updating VCR specs
|
216
|
+
|
217
|
+
Some specs use VCR to reply with stored Trello API replies. The specs are annotated with `vcr:` and `vcr_record:`. To
|
218
|
+
re-record the stored replies, set `vcr_record:` to true and replace `dummy_settings` with `real_settings`.
|
219
|
+
|
220
|
+
describe Scrum::BacklogMover do
|
221
|
+
subject { described_class.new(real_settings) }
|
222
|
+
|
223
|
+
it "fails without moving if backlog list is missing waterline or seabed", vcr: "move_backlog_missing_waterbed", vcr_record: true do
|
224
|
+
expect { subject.move("neUHHzDo", "NzGCbEeN") }.to raise_error
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
The VCR support methods will replace your Trello token and key with dummy values, before saving the replies.
|
data/lib/card.rb
CHANGED
@@ -18,7 +18,6 @@
|
|
18
18
|
class Card
|
19
19
|
# Assuming we have card titles as follows '(8) This is the card name'
|
20
20
|
ESTIMATED_REGEX = /\(([\d.]+)\)/
|
21
|
-
PRIORITY_REGEX = /^(?:\([\d.]+\) )?P(\d+): /
|
22
21
|
SPRINT_NUMBER_REGEX = /\ASprint (\d+)/
|
23
22
|
|
24
23
|
def initialize(board_data, card_id)
|
@@ -43,19 +42,6 @@ class Card
|
|
43
42
|
name.match(ESTIMATED_REGEX).captures.first.to_f
|
44
43
|
end
|
45
44
|
|
46
|
-
def priority
|
47
|
-
return unless m = name.match(PRIORITY_REGEX)
|
48
|
-
m.captures.first.to_i
|
49
|
-
end
|
50
|
-
|
51
|
-
def priority=(n)
|
52
|
-
if priority
|
53
|
-
@card_data["name"].sub!(/P\d+: /, "P#{n}: ")
|
54
|
-
else
|
55
|
-
@card_data["name"] = "P#{n}: #{name}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
45
|
def done_tasks
|
60
46
|
count = 0
|
61
47
|
@card_data["checklists"].each do |checklist|
|
data/lib/cli.rb
CHANGED
@@ -74,7 +74,7 @@ EOT
|
|
74
74
|
require_trello_credentials
|
75
75
|
|
76
76
|
trello = TrelloWrapper.new(@@settings)
|
77
|
-
board = trello.board(options["board-id"])
|
77
|
+
board = trello.board(board_id(options["board-id"]))
|
78
78
|
lists = board.columns
|
79
79
|
|
80
80
|
if @@settings.raw
|
@@ -93,7 +93,7 @@ EOT
|
|
93
93
|
require_trello_credentials
|
94
94
|
|
95
95
|
trello = TrelloWrapper.new(@@settings)
|
96
|
-
board = trello.board(options["board-id"])
|
96
|
+
board = trello.board(board_id(options["board-id"]))
|
97
97
|
cards = board.cards
|
98
98
|
|
99
99
|
if @@settings.raw
|
@@ -118,7 +118,7 @@ EOT
|
|
118
118
|
require_trello_credentials
|
119
119
|
|
120
120
|
trello = TrelloWrapper.new(@@settings)
|
121
|
-
board = trello.board(options["board-id"])
|
121
|
+
board = trello.board(board_id(options["board-id"]))
|
122
122
|
board.cards.each do |card|
|
123
123
|
card.checklists.each do |checklist|
|
124
124
|
puts checklist.name
|
@@ -160,7 +160,7 @@ EOT
|
|
160
160
|
|
161
161
|
chart = BurndownChart.new @@settings
|
162
162
|
puts "Preparing directory..."
|
163
|
-
chart.setup(options[:output],options["board-id"])
|
163
|
+
chart.setup(options[:output],board_id(options["board-id"]))
|
164
164
|
end
|
165
165
|
|
166
166
|
desc "burndown", "Update burndown chart"
|
@@ -203,7 +203,7 @@ EOT
|
|
203
203
|
require_trello_credentials
|
204
204
|
|
205
205
|
b = Backup.new @@settings
|
206
|
-
b.backup(options["board-id"])
|
206
|
+
b.backup(board_id(options["board-id"]))
|
207
207
|
end
|
208
208
|
|
209
209
|
desc "list-backups", "List all backups"
|
@@ -219,7 +219,7 @@ EOT
|
|
219
219
|
option "show-descriptions", :desc => "Show descriptions of cards", :required => false, :type => :boolean
|
220
220
|
def show_backup
|
221
221
|
b = Backup.new @@settings
|
222
|
-
b.show(options["board-id"], options)
|
222
|
+
b.show(board_id(options["board-id"]), options)
|
223
223
|
end
|
224
224
|
|
225
225
|
desc "organization", "Show organization info"
|
@@ -293,21 +293,6 @@ EOT
|
|
293
293
|
trello.make_cover(options["card-id"], filename)
|
294
294
|
end
|
295
295
|
|
296
|
-
desc "set-priorities", "Set priority text into card titles"
|
297
|
-
long_desc <<EOT
|
298
|
-
Add 'P<n>: ' to the beginning of every cards title, replace where
|
299
|
-
already present. n is the current position of the list on the card.
|
300
|
-
EOT
|
301
|
-
option "board-id", :desc => "Id of the board", :required => true
|
302
|
-
option "list-name", :desc => "Name of the list", :required => true
|
303
|
-
def set_priorities
|
304
|
-
process_global_options options
|
305
|
-
require_trello_credentials
|
306
|
-
|
307
|
-
p = Prioritizer.new(@@settings)
|
308
|
-
p.prioritize(options["board-id"], options["list-name"])
|
309
|
-
end
|
310
|
-
|
311
296
|
desc "list-member-boards", "List name and id of all boards"
|
312
297
|
option "member-id", :desc => "Id of the member", :required => true
|
313
298
|
def list_member_boards
|
@@ -322,19 +307,66 @@ EOT
|
|
322
307
|
}
|
323
308
|
end
|
324
309
|
|
325
|
-
desc "
|
310
|
+
desc "setup-scrum", "Create necessary elements of our SCRUM setup"
|
311
|
+
long_desc <<-EOT
|
312
|
+
Will create board, lists and labels with names as configured in trollolorc,
|
313
|
+
or use the defaults.
|
314
|
+
EOT
|
315
|
+
def setup_scrum
|
316
|
+
process_global_options options
|
317
|
+
require_trello_credentials
|
318
|
+
|
319
|
+
c = Scrum::Creator.new(@@settings)
|
320
|
+
c.create
|
321
|
+
end
|
322
|
+
|
323
|
+
desc "set-priorities", "Set priority text into card titles"
|
324
|
+
long_desc <<EOT
|
325
|
+
Add 'P<n>: ' to the beginning of every cards title in the 'Backlog' list,
|
326
|
+
replace where already present. n is the current position of the list on
|
327
|
+
the card.
|
328
|
+
EOT
|
329
|
+
option "board-id", :desc => "Id of the board", :required => true
|
330
|
+
option "backlog-list-name", :desc => "Name of backlog list", :required => false
|
331
|
+
def set_priorities
|
332
|
+
process_global_options options
|
333
|
+
require_trello_credentials
|
334
|
+
|
335
|
+
p = Scrum::Prioritizer.new(@@settings)
|
336
|
+
p.prioritize(board_id(options["board-id"]), options["backlog-list-name"])
|
337
|
+
end
|
338
|
+
|
339
|
+
desc "cleanup-sprint", "Move remaining cards to the planning board"
|
326
340
|
long_desc <<EOT
|
327
|
-
After the sprint, move remaining cards from 'Sprint Backlog'
|
328
|
-
back to the planning board into the 'Ready' list.
|
341
|
+
After the sprint, move remaining cards from 'Sprint Backlog', 'Doing'
|
342
|
+
and 'QA' lists back to the planning board into the 'Ready' list.
|
329
343
|
EOT
|
330
344
|
option "board-id", :desc => "Id of the board", :required => true
|
331
345
|
option "target-board-id", :desc => "Id of the target board", :required => true
|
332
|
-
def
|
346
|
+
def cleanup_sprint
|
347
|
+
process_global_options options
|
348
|
+
require_trello_credentials
|
349
|
+
|
350
|
+
s = Scrum::SprintCleaner.new(@@settings)
|
351
|
+
s.cleanup(board_id(options["board-id"]),
|
352
|
+
board_id(options["target-board-id"]))
|
353
|
+
end
|
354
|
+
|
355
|
+
desc "move-backlog", "Move the planning backlog to the sprint board"
|
356
|
+
long_desc <<-EOT
|
357
|
+
Two separate boards are used, a planning board and a sprint board for the
|
358
|
+
current sprint.
|
359
|
+
After each planning meeting the cards are moved from the planning boards
|
360
|
+
'Backlog' list to the sprint boards 'Sprint Backlog' list.
|
361
|
+
EOT
|
362
|
+
option "planning-board-id", desc: "Id of the planning board", required: true
|
363
|
+
option "sprint-board-id", desc: "Id of the sprint board", required: true
|
364
|
+
def move_backlog
|
333
365
|
process_global_options options
|
334
366
|
require_trello_credentials
|
335
367
|
|
336
|
-
|
337
|
-
|
368
|
+
m = Scrum::BacklogMover.new(@@settings)
|
369
|
+
m.move(board_id(options["planning-board-id"]), board_id(options["sprint-board-id"]))
|
338
370
|
end
|
339
371
|
|
340
372
|
private
|
@@ -368,4 +400,11 @@ EOT
|
|
368
400
|
exit 1
|
369
401
|
end
|
370
402
|
end
|
403
|
+
|
404
|
+
# Returns the board_id using id_or_alias. If id_or_alias matches a mapping
|
405
|
+
# from trollolorc then the mapped id is returned or else the id_or_alias
|
406
|
+
# is returned.
|
407
|
+
def board_id(id_or_alias)
|
408
|
+
@@settings.board_aliases[id_or_alias] || id_or_alias
|
409
|
+
end
|
371
410
|
end
|
data/lib/empty_column.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Scrum
|
2
|
+
class BacklogMover < TrelloService
|
3
|
+
include ScrumBoards
|
4
|
+
|
5
|
+
def move(planning_board_id, sprint_board_id)
|
6
|
+
setup(planning_board_id, sprint_board_id)
|
7
|
+
inspect_backlog
|
8
|
+
@sprint_board.place_seabed(@seabed_card)
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def setup(planning_board_id, sprint_board_id)
|
14
|
+
@sprint_board = sprint_board(sprint_board_id)
|
15
|
+
fail "sprint board is missing #{@sprint_board.backlog_list_name} list" unless @sprint_board.backlog_list
|
16
|
+
|
17
|
+
@planning_board = planning_board(planning_board_id)
|
18
|
+
fail "backlog list not found on planning board" unless @planning_board.backlog_list
|
19
|
+
|
20
|
+
@waterline_card = @planning_board.waterline_card
|
21
|
+
fail "backlog list on planning board is missing waterline or seabed card" unless @waterline_card
|
22
|
+
|
23
|
+
@seabed_card = @planning_board.seabed_card
|
24
|
+
fail "backlog list on planning board is missing waterline or seabed card" unless @seabed_card
|
25
|
+
end
|
26
|
+
|
27
|
+
def inspect_backlog
|
28
|
+
@planning_board.backlog_cards.each do |card|
|
29
|
+
if card == @seabed_card
|
30
|
+
break
|
31
|
+
|
32
|
+
elsif card == @waterline_card
|
33
|
+
@sprint_board.place_waterline(@waterline_card)
|
34
|
+
puts "under the waterline"
|
35
|
+
|
36
|
+
else
|
37
|
+
move_sprint_card(card) unless @planning_board.sticky?(card)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def move_sprint_card(card)
|
43
|
+
puts %(moving card "#{card.name}")
|
44
|
+
@sprint_board.receive(card)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Scrum
|
2
|
+
module CardTypeDetection
|
3
|
+
def sticky?(card)
|
4
|
+
card.labels.any? { |l| l.name == @settings.label_names["sticky"] }
|
5
|
+
end
|
6
|
+
|
7
|
+
def waterline?(card)
|
8
|
+
card.name =~ /w.?a.?t.?e.?r.?l.?i.?n.?e/i
|
9
|
+
end
|
10
|
+
|
11
|
+
def seabed?(card)
|
12
|
+
card.name =~ /s.?e.?a.?b.?e.?d/i
|
13
|
+
end
|
14
|
+
|
15
|
+
def waterline_card
|
16
|
+
@backlog_list.cards.find { |card| waterline?(card) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def seabed_card
|
20
|
+
@backlog_list.cards.find { |card| seabed?(card) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Scrum
|
2
|
+
class Creator < TrelloService
|
3
|
+
def create
|
4
|
+
@scrum = @settings.scrum
|
5
|
+
create_sprint_board
|
6
|
+
create_planning_board
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def create_labels(board_id)
|
12
|
+
@scrum.label_names.each { |_, name| Trello::Label.create(name: name, board_id: board_id) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def create_sprint_board
|
16
|
+
board = Trello::Board.create(name: @scrum.board_names["sprint"])
|
17
|
+
Trello::List.create(board_id: board.id, name: @scrum.list_names["sprint_backlog"])
|
18
|
+
Trello::List.create(board_id: board.id, name: @scrum.list_names["sprint_qa"])
|
19
|
+
Trello::List.create(board_id: board.id, name: @scrum.list_names["sprint_doing"])
|
20
|
+
create_labels(board.id)
|
21
|
+
end
|
22
|
+
|
23
|
+
def create_planning_board
|
24
|
+
board = Trello::Board.create(name: @scrum.board_names["planning"])
|
25
|
+
Trello::List.create(board_id: board.id, name: @scrum.list_names["planning_backlog"])
|
26
|
+
Trello::List.create(board_id: board.id, name: @scrum.list_names["planning_ready"])
|
27
|
+
create_labels(board.id)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Scrum
|
2
|
+
class Prioritizer < TrelloService
|
3
|
+
include ScrumBoards
|
4
|
+
|
5
|
+
def prioritize(board_id, list_name = nil)
|
6
|
+
@board = planning_board(board_id, list_name)
|
7
|
+
fail "list named '#{@board.backlog_list_name}' not found on board" unless @board.backlog_list
|
8
|
+
update_priorities
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def update_priorities
|
14
|
+
n = 1
|
15
|
+
@board.backlog_cards.each do |card|
|
16
|
+
next if @board.sticky?(card) || @board.waterline?(card)
|
17
|
+
puts %(set priority to #{n} for "#{card.name}")
|
18
|
+
card.name = PriorityName.build(card.name, n)
|
19
|
+
card.save
|
20
|
+
n += 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Scrum
|
2
|
+
class PriorityName
|
3
|
+
PRIORITY_REGEX = /^(?:\([\d.]+\) )?P(\d+): /
|
4
|
+
|
5
|
+
def self.priority(name)
|
6
|
+
return unless m = name.match(PRIORITY_REGEX)
|
7
|
+
m.captures.first.to_i
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.build(name, n)
|
11
|
+
return name.sub(/P\d+: /, "P#{n}: ") if priority(name)
|
12
|
+
"P#{n}: #{name}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Scrum
|
2
|
+
module ScrumBoards
|
3
|
+
def sprint_board(board_id)
|
4
|
+
Scrum::SprintBoard.new(@settings.scrum).setup(board_id)
|
5
|
+
end
|
6
|
+
|
7
|
+
def planning_board(board_id, list_name = nil)
|
8
|
+
Scrum::SprintPlanningBoard.new(@settings.scrum).setup(board_id, list_name)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Scrum
|
2
|
+
class SprintBoard
|
3
|
+
include CardTypeDetection
|
4
|
+
|
5
|
+
def initialize(settings)
|
6
|
+
@under_waterline = false
|
7
|
+
@settings = settings
|
8
|
+
end
|
9
|
+
attr_reader :backlog_list
|
10
|
+
|
11
|
+
def setup(id)
|
12
|
+
@board, @backlog_list = TrelloService.find_list(id, backlog_list_name)
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def backlog_list_name
|
17
|
+
@settings.list_names["sprint_backlog"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def doing_list
|
21
|
+
@doing_list ||= @board.lists.find { |l| l.name == doing_list_name }
|
22
|
+
end
|
23
|
+
|
24
|
+
def qa_list
|
25
|
+
@qa_list ||= @board.lists.find { |l| l.name == qa_list_name }
|
26
|
+
end
|
27
|
+
|
28
|
+
def receive(card)
|
29
|
+
card.move_to_board(@board, @backlog_list)
|
30
|
+
add_waterline_label(card) if @under_waterline
|
31
|
+
end
|
32
|
+
|
33
|
+
def place_waterline(planning_waterline_card)
|
34
|
+
place_card_at_bottom(waterline_card, planning_waterline_card)
|
35
|
+
@under_waterline = true
|
36
|
+
end
|
37
|
+
|
38
|
+
def place_seabed(planning_seabed_card)
|
39
|
+
place_card_at_bottom(seabed_card, planning_seabed_card)
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_waterline_label(labels)
|
43
|
+
labels.find do |label|
|
44
|
+
label.name == waterline_label_name ||
|
45
|
+
label.name =~ /waterline/i
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def under_waterline_label
|
52
|
+
@label ||= find_waterline_label(@board.labels)
|
53
|
+
@label ||= Trello::Label.create(name: waterline_label_name)
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_waterline_label(original_card)
|
57
|
+
new_card = @backlog_list.cards.find { |card| card.name == original_card.name }
|
58
|
+
new_card.card_labels << under_waterline_label
|
59
|
+
end
|
60
|
+
|
61
|
+
def place_card_at_bottom(existing_card, planning_card)
|
62
|
+
if existing_card
|
63
|
+
existing_card.pos = 'bottom'
|
64
|
+
existing_card.save
|
65
|
+
else
|
66
|
+
planning_card.move_to_board(@board, @backlog_list)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def waterline_label_name
|
71
|
+
@settings.label_names["waterline"]
|
72
|
+
end
|
73
|
+
|
74
|
+
def qa_list_name
|
75
|
+
@settings.list_names["sprint_qa"]
|
76
|
+
end
|
77
|
+
|
78
|
+
def doing_list_name
|
79
|
+
@settings.list_names["sprint_doing"]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Scrum
|
2
|
+
class SprintCleaner < TrelloService
|
3
|
+
include ScrumBoards
|
4
|
+
|
5
|
+
def cleanup(board_id, target_board_id)
|
6
|
+
@board = sprint_board(board_id)
|
7
|
+
fail "backlog list '#{@board.backlog_list_name}' not found on sprint board" unless @board.backlog_list
|
8
|
+
@target_board = Trello::Board.find(target_board_id)
|
9
|
+
fail "ready list '#{@settings.scrum.list_names["planning_ready"]}' not found on planning board" unless target_list
|
10
|
+
|
11
|
+
move_cards(@board.backlog_list)
|
12
|
+
move_cards(@board.doing_list) if @board.doing_list
|
13
|
+
move_cards(@board.qa_list) if @board.qa_list
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def target_list
|
19
|
+
@target_list ||= @target_board.lists.find { |l| l.name == @settings.scrum.list_names["planning_ready"] }
|
20
|
+
end
|
21
|
+
|
22
|
+
def waterline_label(card)
|
23
|
+
@board.find_waterline_label(card.labels)
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove_waterline_label(card)
|
27
|
+
label = waterline_label(card)
|
28
|
+
card.remove_label(label) if label
|
29
|
+
end
|
30
|
+
|
31
|
+
def move_cards(source_list)
|
32
|
+
source_list.cards.each do |card|
|
33
|
+
next if @board.sticky?(card)
|
34
|
+
puts %(moving card "#{card.name}" to list "#{target_list.name}")
|
35
|
+
card.members.each { |member| card.remove_member(member) }
|
36
|
+
remove_waterline_label(card)
|
37
|
+
card.move_to_board(@target_board, target_list)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Scrum
|
2
|
+
class SprintPlanningBoard
|
3
|
+
include CardTypeDetection
|
4
|
+
|
5
|
+
attr_reader :backlog_list_name
|
6
|
+
|
7
|
+
def initialize(settings)
|
8
|
+
@settings = settings
|
9
|
+
@backlog_list_name = settings.list_names["planning_backlog"]
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_accessor :backlog_list
|
13
|
+
|
14
|
+
def setup(id, list_name = nil)
|
15
|
+
if list_name
|
16
|
+
@backlog_list_name = list_name
|
17
|
+
end
|
18
|
+
@board, @backlog_list = TrelloService.find_list(id, @backlog_list_name)
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def backlog_cards
|
23
|
+
@backlog_list.cards
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/scrum.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Scrum
|
2
|
+
end
|
3
|
+
|
4
|
+
require_relative 'scrum/card_type_detection'
|
5
|
+
require_relative 'scrum/sprint_board'
|
6
|
+
require_relative 'scrum/sprint_planning_board'
|
7
|
+
require_relative 'scrum/scrum_boards'
|
8
|
+
|
9
|
+
require_relative 'scrum/priority_name'
|
10
|
+
require_relative 'scrum/prioritizer'
|
11
|
+
require_relative 'scrum/sprint_cleaner'
|
12
|
+
require_relative 'scrum/backlog_mover'
|
13
|
+
require_relative 'scrum/creator'
|