trollolo 0.0.9 → 0.0.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
![Burndown example](https://raw.githubusercontent.com/openSUSE/trollolo/master/examples/burndown-26.png)
|
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'
|