trollolo 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +21 -2
- data/.rubocop_todo.yml +105 -270
- data/CHANGELOG.md +10 -0
- data/CONTRIBUTING.md +6 -4
- data/Gemfile +1 -1
- data/README.md +10 -1
- data/Rakefile +4 -4
- data/bin/trollolo +2 -2
- data/lib/backup.rb +21 -23
- data/lib/burndown_chart.rb +46 -49
- data/lib/burndown_data.rb +21 -21
- data/lib/card.rb +23 -32
- data/lib/checklist.rb +13 -1
- data/lib/cli.rb +105 -110
- data/lib/column.rb +11 -10
- data/lib/empty_column.rb +2 -2
- data/lib/scrum/backlog_mover.rb +3 -3
- data/lib/scrum/card_type_detection.rb +1 -1
- data/lib/scrum/creator.rb +7 -7
- data/lib/scrum/prioritizer.rb +1 -1
- data/lib/scrum/sprint_board.rb +4 -4
- data/lib/scrum/sprint_cleaner.rb +3 -3
- data/lib/scrum/sprint_planning_board.rb +2 -4
- data/lib/scrum_board.rb +3 -3
- data/lib/settings.rb +29 -27
- data/lib/trello_service.rb +1 -1
- data/lib/trello_wrapper.rb +9 -9
- data/lib/version.rb +1 -1
- data/spec/data/card.json +19 -0
- data/spec/data/trollolorc +3 -0
- data/spec/integration/command_line_spec.rb +14 -14
- data/spec/integration/create_burndown_spec.rb +25 -25
- data/spec/integration/integration_spec_helper.rb +1 -1
- data/spec/integration/wrapper/credentials_input_wrapper +7 -11
- data/spec/integration/wrapper/empty_config_trollolo_wrapper +2 -2
- data/spec/integration/wrapper/trollolo_wrapper +2 -2
- data/spec/unit/backup_spec.rb +14 -14
- data/spec/unit/burndown_chart_spec.rb +176 -184
- data/spec/unit/burndown_data_spec.rb +21 -23
- data/spec/unit/card_spec.rb +38 -24
- data/spec/unit/cli_spec.rb +57 -57
- data/spec/unit/empty_column_spec.rb +1 -1
- data/spec/unit/retrieve_data_spec.rb +13 -13
- data/spec/unit/scrum/backlog_mover_spec.rb +8 -8
- data/spec/unit/scrum/card_type_detection_spec.rb +12 -12
- data/spec/unit/scrum/creator_spec.rb +8 -8
- data/spec/unit/scrum/prioritizer_spec.rb +19 -19
- data/spec/unit/scrum/priority_name_spec.rb +16 -16
- data/spec/unit/scrum/sprint_board_spec.rb +3 -3
- data/spec/unit/scrum/sprint_cleaner_spec.rb +15 -15
- data/spec/unit/scrum/sprint_planning_board_spec.rb +2 -2
- data/spec/unit/scrum_board_spec.rb +56 -56
- data/spec/unit/settings_spec.rb +23 -23
- data/spec/unit/spec_helper.rb +3 -3
- data/spec/unit/support/test_data_operations.rb +4 -0
- data/spec/unit/support/update_webmock_data +9 -11
- data/spec/unit/support/vcr.rb +3 -3
- data/spec/unit/support/webmocks.rb +21 -12
- data/spec/unit/trello_wrapper_spec.rb +40 -29
- data/trollolo.gemspec +3 -3
- metadata +8 -5
data/lib/column.rb
CHANGED
@@ -15,21 +15,22 @@
|
|
15
15
|
# To contact SUSE about this file by physical or electronic mail,
|
16
16
|
# you may find current contact information at www.suse.com
|
17
17
|
class Column
|
18
|
-
def initialize(board_data, list_id)
|
18
|
+
def initialize(board_data, list_id, settings = nil)
|
19
19
|
@board_data = board_data
|
20
|
-
@
|
20
|
+
@settings = settings
|
21
|
+
@list_data = @board_data['lists'].find{|l| l['id'] == list_id}
|
21
22
|
end
|
22
23
|
|
23
24
|
def name
|
24
|
-
@list_data[
|
25
|
+
@list_data['name']
|
25
26
|
end
|
26
27
|
|
27
28
|
def estimated_cards
|
28
|
-
cards.select
|
29
|
+
cards.select(&:estimated?)
|
29
30
|
end
|
30
31
|
|
31
32
|
def sum
|
32
|
-
estimated_cards.map
|
33
|
+
estimated_cards.map(&:story_points).sum
|
33
34
|
end
|
34
35
|
|
35
36
|
def tasks
|
@@ -41,11 +42,11 @@ class Column
|
|
41
42
|
end
|
42
43
|
|
43
44
|
def extra_cards
|
44
|
-
cards.select
|
45
|
+
cards.select(&:extra?)
|
45
46
|
end
|
46
47
|
|
47
48
|
def unplanned_cards
|
48
|
-
cards.select
|
49
|
+
cards.select(&:unplanned?)
|
49
50
|
end
|
50
51
|
|
51
52
|
def committed_cards
|
@@ -53,13 +54,13 @@ class Column
|
|
53
54
|
end
|
54
55
|
|
55
56
|
def fast_lane_cards
|
56
|
-
cards.select
|
57
|
+
cards.select(&:fast_lane?)
|
57
58
|
end
|
58
59
|
|
59
60
|
def cards
|
60
61
|
return @cards if @cards
|
61
62
|
|
62
|
-
cards = @board_data[
|
63
|
-
@cards = cards.map{|c| Card.new(@board_data, c[
|
63
|
+
cards = @board_data['cards'].select{|c| c['idList'] == @list_data['id']}
|
64
|
+
@cards = cards.map{|c| Card.new(@board_data, c['id'], @settings)}
|
64
65
|
end
|
65
66
|
end
|
data/lib/empty_column.rb
CHANGED
data/lib/scrum/backlog_mover.rb
CHANGED
@@ -12,10 +12,10 @@ module Scrum
|
|
12
12
|
|
13
13
|
def setup(planning_board_id, sprint_board_id)
|
14
14
|
@sprint_board = sprint_board(sprint_board_id)
|
15
|
-
|
15
|
+
raise "sprint board is missing #{@sprint_board.backlog_list_name} list" unless @sprint_board.backlog_list
|
16
16
|
|
17
17
|
@planning_board = planning_board(planning_board_id)
|
18
|
-
|
18
|
+
raise 'backlog list not found on planning board' unless @planning_board.backlog_list
|
19
19
|
|
20
20
|
@waterline_card = @planning_board.waterline_card
|
21
21
|
@seabed_card = @planning_board.seabed_card
|
@@ -27,7 +27,7 @@ module Scrum
|
|
27
27
|
break
|
28
28
|
elsif @waterline_card && card == @waterline_card
|
29
29
|
@sprint_board.place_waterline(@waterline_card)
|
30
|
-
puts
|
30
|
+
puts 'under the waterline'
|
31
31
|
else
|
32
32
|
move_sprint_card(card) unless @planning_board.sticky?(card)
|
33
33
|
end
|
data/lib/scrum/creator.rb
CHANGED
@@ -13,17 +13,17 @@ module Scrum
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def create_sprint_board
|
16
|
-
board = Trello::Board.create(name: @scrum.board_names[
|
17
|
-
Trello::List.create(board_id: board.id, name: @scrum.list_names[
|
18
|
-
Trello::List.create(board_id: board.id, name: @scrum.list_names[
|
19
|
-
Trello::List.create(board_id: board.id, name: @scrum.list_names[
|
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
20
|
create_labels(board.id)
|
21
21
|
end
|
22
22
|
|
23
23
|
def create_planning_board
|
24
|
-
board = Trello::Board.create(name: @scrum.board_names[
|
25
|
-
Trello::List.create(board_id: board.id, name: @scrum.list_names[
|
26
|
-
Trello::List.create(board_id: board.id, name: @scrum.list_names[
|
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
27
|
create_labels(board.id)
|
28
28
|
end
|
29
29
|
end
|
data/lib/scrum/prioritizer.rb
CHANGED
@@ -4,7 +4,7 @@ module Scrum
|
|
4
4
|
|
5
5
|
def prioritize(board_id, list_name = nil)
|
6
6
|
@board = planning_board(board_id, list_name)
|
7
|
-
|
7
|
+
raise "list named '#{@board.backlog_list_name}' not found on board" unless @board.backlog_list
|
8
8
|
update_priorities
|
9
9
|
end
|
10
10
|
|
data/lib/scrum/sprint_board.rb
CHANGED
@@ -14,7 +14,7 @@ module Scrum
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def backlog_list_name
|
17
|
-
@settings.list_names[
|
17
|
+
@settings.list_names['sprint_backlog']
|
18
18
|
end
|
19
19
|
|
20
20
|
def doing_list
|
@@ -74,15 +74,15 @@ module Scrum
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def waterline_label_name
|
77
|
-
@settings.label_names[
|
77
|
+
@settings.label_names['waterline']
|
78
78
|
end
|
79
79
|
|
80
80
|
def qa_list_name
|
81
|
-
@settings.list_names[
|
81
|
+
@settings.list_names['sprint_qa']
|
82
82
|
end
|
83
83
|
|
84
84
|
def doing_list_name
|
85
|
-
@settings.list_names[
|
85
|
+
@settings.list_names['sprint_doing']
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
data/lib/scrum/sprint_cleaner.rb
CHANGED
@@ -4,9 +4,9 @@ module Scrum
|
|
4
4
|
|
5
5
|
def cleanup(board_id, target_board_id)
|
6
6
|
@board = sprint_board(board_id)
|
7
|
-
|
7
|
+
raise "backlog list '#{@board.backlog_list_name}' not found on sprint board" unless @board.backlog_list
|
8
8
|
@target_board = Trello::Board.find(target_board_id)
|
9
|
-
|
9
|
+
raise "ready list '#{@settings.scrum.list_names['planning_ready']}' not found on planning board" unless target_list
|
10
10
|
|
11
11
|
gen_burndown
|
12
12
|
|
@@ -18,7 +18,7 @@ module Scrum
|
|
18
18
|
private
|
19
19
|
|
20
20
|
def target_list
|
21
|
-
@target_list ||= @target_board.lists.find { |l| l.name == @settings.scrum.list_names[
|
21
|
+
@target_list ||= @target_board.lists.find { |l| l.name == @settings.scrum.list_names['planning_ready'] }
|
22
22
|
end
|
23
23
|
|
24
24
|
def waterline_label(card)
|
@@ -6,15 +6,13 @@ module Scrum
|
|
6
6
|
|
7
7
|
def initialize(settings)
|
8
8
|
@settings = settings
|
9
|
-
@backlog_list_name = settings.list_names[
|
9
|
+
@backlog_list_name = settings.list_names['planning_backlog']
|
10
10
|
end
|
11
11
|
|
12
12
|
attr_accessor :backlog_list
|
13
13
|
|
14
14
|
def setup(id, list_name = nil)
|
15
|
-
if list_name
|
16
|
-
@backlog_list_name = list_name
|
17
|
-
end
|
15
|
+
@backlog_list_name = list_name if list_name
|
18
16
|
@board, @backlog_list = TrelloService.find_list(id, @backlog_list_name)
|
19
17
|
self
|
20
18
|
end
|
data/lib/scrum_board.rb
CHANGED
@@ -3,13 +3,13 @@ class ScrumBoard
|
|
3
3
|
class DoneColumnNotFoundError < StandardError; end
|
4
4
|
class AcceptedColumnNotFoundError < StandardError; end
|
5
5
|
|
6
|
-
def initialize(board_data, settings)
|
6
|
+
def initialize(board_data, settings = nil)
|
7
7
|
@settings = settings
|
8
8
|
@board_data = board_data
|
9
9
|
end
|
10
10
|
|
11
11
|
def columns
|
12
|
-
@columns ||= @board_data[
|
12
|
+
@columns ||= @board_data['lists'].map{|x| Column.new(@board_data, x['id'], @settings)}
|
13
13
|
end
|
14
14
|
|
15
15
|
def done_column
|
@@ -138,7 +138,7 @@ class ScrumBoard
|
|
138
138
|
end
|
139
139
|
|
140
140
|
def id
|
141
|
-
@board_data[
|
141
|
+
@board_data['id']
|
142
142
|
end
|
143
143
|
|
144
144
|
def cards
|
data/lib/settings.rb
CHANGED
@@ -19,23 +19,25 @@ class Settings
|
|
19
19
|
|
20
20
|
attr_accessor :developer_public_key, :member_token, :board_aliases, :verbose,
|
21
21
|
:raw, :not_done_columns, :todo_column, :accepted_column_name_regex,
|
22
|
-
:done_column_name_regex, :todo_column_name_regex, :scrum
|
22
|
+
:done_column_name_regex, :todo_column_name_regex, :scrum,
|
23
|
+
:no_task_checklists
|
23
24
|
|
24
|
-
def initialize
|
25
|
+
def initialize(config_file_path)
|
25
26
|
@config_file_path = config_file_path
|
26
27
|
if File.exists? config_file_path
|
27
28
|
@config = YAML.load_file(config_file_path)
|
28
29
|
|
29
30
|
if @config
|
30
|
-
@developer_public_key = @config[
|
31
|
-
@member_token = @config[
|
32
|
-
@board_aliases = @config[
|
33
|
-
@scrum = OpenStruct.new(@config[
|
34
|
-
@not_done_columns = @config[
|
35
|
-
@
|
36
|
-
@
|
37
|
-
@
|
38
|
-
@
|
31
|
+
@developer_public_key = @config['developer_public_key']
|
32
|
+
@member_token = @config['member_token']
|
33
|
+
@board_aliases = @config['board_aliases'] || {}
|
34
|
+
@scrum = OpenStruct.new(@config['scrum'] || scrum_defaults)
|
35
|
+
@not_done_columns = @config['not_done_columns'].freeze || ['Sprint Backlog', 'Doing']
|
36
|
+
@no_task_checklists = @config['no_task_checklists'].freeze || ['Feedback']
|
37
|
+
@todo_column = @config['todo_column'].freeze
|
38
|
+
@done_column_name_regex = @config['done_column_name_regex'].freeze || /\ADone/
|
39
|
+
@accepted_column_name_regex = @config['accepted_column_name_regex'].freeze || /\AAccepted/
|
40
|
+
@todo_column_name_regex = @config['todo_column_name_regex'].freeze || /\ATo Do\Z/
|
39
41
|
else
|
40
42
|
raise "Couldn't read config data from '#{config_file_path}'"
|
41
43
|
end
|
@@ -47,10 +49,10 @@ class Settings
|
|
47
49
|
|
48
50
|
def save_config
|
49
51
|
@config = {}
|
50
|
-
@config[
|
51
|
-
@config[
|
52
|
+
@config['developer_public_key'] = @developer_public_key
|
53
|
+
@config['member_token'] = @member_token
|
52
54
|
|
53
|
-
File.open(@config_file_path,
|
55
|
+
File.open(@config_file_path, 'w') do |f|
|
54
56
|
f.write(@config.to_yaml)
|
55
57
|
end
|
56
58
|
end
|
@@ -63,21 +65,21 @@ class Settings
|
|
63
65
|
|
64
66
|
def scrum_defaults
|
65
67
|
{
|
66
|
-
|
67
|
-
|
68
|
-
|
68
|
+
'board_names' => {
|
69
|
+
'planning' => 'Planning Board',
|
70
|
+
'sprint' => 'Sprint Board'
|
69
71
|
},
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
},
|
74
|
-
"list_names" => {
|
75
|
-
"sprint_backlog" => "Sprint Backlog",
|
76
|
-
"sprint_qa" => "QA",
|
77
|
-
"sprint_doing" => "Doing",
|
78
|
-
"planning_backlog" => "Backlog",
|
79
|
-
"planning_ready" => "Ready for Estimation"
|
72
|
+
'label_names' => {
|
73
|
+
'sticky' => 'Sticky',
|
74
|
+
'waterline' => 'Under waterline'
|
80
75
|
},
|
76
|
+
'list_names' => {
|
77
|
+
'sprint_backlog' => 'Sprint Backlog',
|
78
|
+
'sprint_qa' => 'QA',
|
79
|
+
'sprint_doing' => 'Doing',
|
80
|
+
'planning_backlog' => 'Backlog',
|
81
|
+
'planning_ready' => 'Ready for Estimation'
|
82
|
+
}
|
81
83
|
}.freeze
|
82
84
|
end
|
83
85
|
end
|
data/lib/trello_service.rb
CHANGED
data/lib/trello_wrapper.rb
CHANGED
@@ -42,23 +42,23 @@ class TrelloWrapper < TrelloService
|
|
42
42
|
|
43
43
|
def add_attachment(card_id, filename)
|
44
44
|
card = Trello::Card.find(card_id)
|
45
|
-
card.add_attachment(File.open(filename,
|
45
|
+
card.add_attachment(File.open(filename, 'rb'))
|
46
46
|
end
|
47
47
|
|
48
48
|
def make_cover(card_id, image_name)
|
49
49
|
attachment_id = attachment_id_by_name(card_id, image_name)
|
50
|
-
raise("Error: The attachment with the name '#{image_name}' was not found")
|
50
|
+
raise("Error: The attachment with the name '#{image_name}' was not found") unless attachment_id
|
51
|
+
make_cover_with_id(card_id, attachment_id)
|
52
|
+
end
|
53
|
+
|
54
|
+
def make_cover_with_id(card_id, attachment_id)
|
51
55
|
client.put("/cards/#{card_id}/idAttachmentCover?value=#{attachment_id}")
|
52
56
|
end
|
53
57
|
|
54
58
|
def attachment_id_by_name(card_id, image_name)
|
55
59
|
json = JSON.parse(client.get("/cards/#{card_id}/attachments?fields=name"))
|
56
|
-
attachment = json.find{ |e| e[
|
57
|
-
if attachment
|
58
|
-
attachment["id"]
|
59
|
-
else
|
60
|
-
nil
|
61
|
-
end
|
60
|
+
attachment = json.find{ |e| e['name'] == image_name }
|
61
|
+
attachment['id'] if attachment
|
62
62
|
end
|
63
63
|
|
64
64
|
def get_description(card_id)
|
@@ -79,7 +79,7 @@ class TrelloWrapper < TrelloService
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def get_board(board_id)
|
82
|
-
raise TrolloloError
|
82
|
+
raise TrolloloError, 'Board id cannot be blank' if board_id.blank?
|
83
83
|
|
84
84
|
client.get("/boards/#{board_id}?lists=open&cards=open&card_checklists=all")
|
85
85
|
end
|
data/lib/version.rb
CHANGED
data/spec/data/card.json
CHANGED
@@ -49,6 +49,25 @@
|
|
49
49
|
"idChecklists": [
|
50
50
|
"5319c04d592a9182153db831"
|
51
51
|
],
|
52
|
+
"checklists": [
|
53
|
+
{ "id": "5319c052d86da6cb6fa2a9e0",
|
54
|
+
"name": "Tasks",
|
55
|
+
"checkItems": [
|
56
|
+
{ "name": "Task 1", "state": "complete"},
|
57
|
+
{ "name": "Task 2", "state": "incomplete"},
|
58
|
+
{ "name": "Task 3", "state": "incomplete"}
|
59
|
+
]
|
60
|
+
},
|
61
|
+
{
|
62
|
+
"id": "5319c0562fef3bc511c79279",
|
63
|
+
"name": "Definition of done",
|
64
|
+
"checkItems": [
|
65
|
+
{ "name": "DoD 1", "state": "complete"},
|
66
|
+
{ "name": "DoD 2", "state": "complete"},
|
67
|
+
{ "name": "DoD 3", "state": "inmplete"}
|
68
|
+
]
|
69
|
+
}
|
70
|
+
],
|
52
71
|
"idMembers": [
|
53
72
|
|
54
73
|
],
|
data/spec/data/trollolorc
CHANGED
@@ -15,36 +15,36 @@ def credentials_input_wrapper
|
|
15
15
|
File.expand_path('../wrapper/credentials_input_wrapper', __FILE__)
|
16
16
|
end
|
17
17
|
|
18
|
-
describe
|
18
|
+
describe 'command line' do
|
19
19
|
|
20
|
-
it
|
21
|
-
result = run_command(args: [
|
20
|
+
it 'processes help option' do
|
21
|
+
result = run_command(args: ['-h'])
|
22
22
|
expect(result).to exit_with_success(/Commands:/)
|
23
|
-
expect(result.stdout).to match(
|
24
|
-
expect(result.stdout).to match(
|
23
|
+
expect(result.stdout).to match('trollolo help')
|
24
|
+
expect(result.stdout).to match('Options:')
|
25
25
|
end
|
26
26
|
|
27
|
-
it
|
28
|
-
result = run_command(cmd: trollolo_cmd, args: [
|
27
|
+
it 'throws error on invalid command' do
|
28
|
+
result = run_command(cmd: trollolo_cmd, args: ['invalid_command'])
|
29
29
|
expect(result).to exit_with_error(1, "Could not find command \"invalid_command\".\n")
|
30
30
|
end
|
31
31
|
|
32
|
-
it
|
33
|
-
expect(run_command(cmd: credentials_input_wrapper, args: [
|
32
|
+
it 'asks for authorization data' do
|
33
|
+
expect(run_command(cmd: credentials_input_wrapper, args: ['get-cards', '--board-id=myboardid'])).to exit_with_success('')
|
34
34
|
end
|
35
35
|
|
36
|
-
describe
|
36
|
+
describe 'burndown chart' do
|
37
37
|
use_given_filesystem
|
38
38
|
|
39
|
-
it
|
39
|
+
it 'inits burndown directory' do
|
40
40
|
path = given_directory
|
41
|
-
result = run_command(cmd: trollolo_cmd, args: [
|
41
|
+
result = run_command(cmd: trollolo_cmd, args: ['burndown-init', '-o', path.to_s, '--board-id=myboardid'])
|
42
42
|
expect(result).to exit_with_success(/Preparing/)
|
43
43
|
end
|
44
44
|
|
45
|
-
it
|
45
|
+
it 'fails, if burndown data is not found' do
|
46
46
|
path = given_directory
|
47
|
-
result = run_command(cmd: trollolo_cmd, args: [
|
47
|
+
result = run_command(cmd: trollolo_cmd, args: ['burndown', '-o', path.to_s])
|
48
48
|
expect(result).to exit_with_error(1, /burndown-data-01.yaml' not found/)
|
49
49
|
end
|
50
50
|
end
|