jira-auto-tool 0.1.1
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +291 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Guardfile +105 -0
- data/LICENSE.txt +21 -0
- data/README.md +159 -0
- data/Rakefile +20 -0
- data/bin/jira-auto-tool +57 -0
- data/bin/jira-auto-tool.bat +2 -0
- data/bin/setup +8 -0
- data/bin/setup-dev-win.bat +3 -0
- data/cucumber.yml +7 -0
- data/features/align_sprint_time_in_dates.feature +33 -0
- data/features/assign_tickets_to_team_sprints.feature +73 -0
- data/features/cache_boards.feature +24 -0
- data/features/control_http_request_rate_limit.feature +17 -0
- data/features/create_sprints_using_existing_ones_as_reference.feature +79 -0
- data/features/list_boards.feature +12 -0
- data/features/list_project_fields.feature +89 -0
- data/features/list_sprint_prefixes.feature +77 -0
- data/features/quarterly_add_sprints_using_existing_ones_as_a_reference.feature +71 -0
- data/features/quarterly_create_sprints_until_specific_date.feature +75 -0
- data/features/quarterly_rename_sprints.feature +179 -0
- data/features/rename_sprints.feature +203 -0
- data/features/self_documented_command_line.feature +15 -0
- data/features/sprint_filtering.feature +111 -0
- data/features/step_definitions/execution_context_steps.rb +33 -0
- data/features/step_definitions/jira_board_steps.rb +102 -0
- data/features/step_definitions/jira_ticket_steps.rb +63 -0
- data/features/support/10.setup_cucumber.rb +10 -0
- data/features/support/env.rb +25 -0
- data/features/support/hooks.rb +25 -0
- data/features/support/setup_rspec.rb +14 -0
- data/features/support/setup_simplecov.rb +5 -0
- data/features/update_sprint_end_date_and_shift_following_ones.feature +52 -0
- data/lib/jira/auto/tool/board/cache.rb +67 -0
- data/lib/jira/auto/tool/board/unavailable_board.rb +36 -0
- data/lib/jira/auto/tool/board.rb +105 -0
- data/lib/jira/auto/tool/board_controller/options.rb +32 -0
- data/lib/jira/auto/tool/board_controller.rb +88 -0
- data/lib/jira/auto/tool/common_options.rb +37 -0
- data/lib/jira/auto/tool/config/options.rb +19 -0
- data/lib/jira/auto/tool/config.rb +64 -0
- data/lib/jira/auto/tool/fetch_custom_field_options.rb +47 -0
- data/lib/jira/auto/tool/field.rb +59 -0
- data/lib/jira/auto/tool/field_controller.rb +50 -0
- data/lib/jira/auto/tool/field_option.rb +35 -0
- data/lib/jira/auto/tool/get_createmeta_for_project.rb +24 -0
- data/lib/jira/auto/tool/helpers/environment_based_value.rb +96 -0
- data/lib/jira/auto/tool/helpers/option_parser.rb +16 -0
- data/lib/jira/auto/tool/helpers/overridable_time.rb +18 -0
- data/lib/jira/auto/tool/helpers/pagination.rb +50 -0
- data/lib/jira/auto/tool/jira_http_options.rb +20 -0
- data/lib/jira/auto/tool/next_sprint_creator.rb +60 -0
- data/lib/jira/auto/tool/performer/options.rb +76 -0
- data/lib/jira/auto/tool/performer/planning_increment_sprint_creator.rb +42 -0
- data/lib/jira/auto/tool/performer/prefix_sprint_updater.rb +42 -0
- data/lib/jira/auto/tool/performer/quarterly_sprint_renamer/next_name_generator.rb +60 -0
- data/lib/jira/auto/tool/performer/quarterly_sprint_renamer.rb +19 -0
- data/lib/jira/auto/tool/performer/sprint_end_date_updater.rb +55 -0
- data/lib/jira/auto/tool/performer/sprint_renamer/keep_same_name_generator.rb +19 -0
- data/lib/jira/auto/tool/performer/sprint_renamer/next_name_generator.rb +47 -0
- data/lib/jira/auto/tool/performer/sprint_renamer.rb +55 -0
- data/lib/jira/auto/tool/performer/sprint_time_in_dates_aligner.rb +52 -0
- data/lib/jira/auto/tool/project/options.rb +22 -0
- data/lib/jira/auto/tool/project/ticket_fields.rb +70 -0
- data/lib/jira/auto/tool/project.rb +40 -0
- data/lib/jira/auto/tool/rate_limited_jira_client.rb +50 -0
- data/lib/jira/auto/tool/request_builder/field_context_fetcher.rb +78 -0
- data/lib/jira/auto/tool/request_builder/field_option_fetcher.rb +54 -0
- data/lib/jira/auto/tool/request_builder/get.rb +29 -0
- data/lib/jira/auto/tool/request_builder/sprint_creator.rb +112 -0
- data/lib/jira/auto/tool/request_builder/sprint_state_updater.rb +60 -0
- data/lib/jira/auto/tool/request_builder.rb +89 -0
- data/lib/jira/auto/tool/setup_logging.rb +35 -0
- data/lib/jira/auto/tool/sprint/name.rb +105 -0
- data/lib/jira/auto/tool/sprint/prefix.rb +66 -0
- data/lib/jira/auto/tool/sprint.rb +183 -0
- data/lib/jira/auto/tool/sprint_controller/options.rb +61 -0
- data/lib/jira/auto/tool/sprint_controller.rb +152 -0
- data/lib/jira/auto/tool/sprint_state_controller.rb +58 -0
- data/lib/jira/auto/tool/team.rb +23 -0
- data/lib/jira/auto/tool/team_sprint_prefix_mapper/options.rb +27 -0
- data/lib/jira/auto/tool/team_sprint_prefix_mapper.rb +62 -0
- data/lib/jira/auto/tool/team_sprint_ticket_dispatcher.rb +76 -0
- data/lib/jira/auto/tool/ticket.rb +110 -0
- data/lib/jira/auto/tool/until_date.rb +68 -0
- data/lib/jira/auto/tool/version.rb +9 -0
- data/lib/jira/auto/tool.rb +216 -0
- data/sig/jira/sprint/tool.rbs +8 -0
- data/spec/jira/auto/tool/board/cache_spec.rb +179 -0
- data/spec/jira/auto/tool/board/unavailable_board_spec.rb +34 -0
- data/spec/jira/auto/tool/board_controller/options_spec.rb +52 -0
- data/spec/jira/auto/tool/board_controller_spec.rb +154 -0
- data/spec/jira/auto/tool/board_spec.rb +163 -0
- data/spec/jira/auto/tool/common_options_spec.rb +49 -0
- data/spec/jira/auto/tool/config_spec.rb +108 -0
- data/spec/jira/auto/tool/field_controller_spec.rb +121 -0
- data/spec/jira/auto/tool/field_option_spec.rb +42 -0
- data/spec/jira/auto/tool/field_spec.rb +99 -0
- data/spec/jira/auto/tool/helpers/environment_based_value_spec.rb +21 -0
- data/spec/jira/auto/tool/helpers/option_parser_spec.rb +21 -0
- data/spec/jira/auto/tool/helpers/overridable_time_spec.rb +43 -0
- data/spec/jira/auto/tool/helpers/pagination_spec.rb +72 -0
- data/spec/jira/auto/tool/jira_http_options_spec.rb +32 -0
- data/spec/jira/auto/tool/next_sprint_creator_spec.rb +85 -0
- data/spec/jira/auto/tool/performer/option_spec.rb +55 -0
- data/spec/jira/auto/tool/performer/planning_increment_sprint_creator_spec.rb +62 -0
- data/spec/jira/auto/tool/performer/prefix_sprint_updater_spec.rb +35 -0
- data/spec/jira/auto/tool/performer/quarterly_sprint_renamer/next_name_generator_spec.rb +175 -0
- data/spec/jira/auto/tool/performer/quarterly_sprint_renamer_spec.rb +239 -0
- data/spec/jira/auto/tool/performer/sprint_end_date_updater_spec.rb +90 -0
- data/spec/jira/auto/tool/performer/sprint_renamer/keep_same_name_generator_spec.rb +12 -0
- data/spec/jira/auto/tool/performer/sprint_renamer/next_name_generator_spec.rb +129 -0
- data/spec/jira/auto/tool/performer/sprint_renamer_spec.rb +240 -0
- data/spec/jira/auto/tool/performer/sprint_time_in_dates_aligner_spec.rb +132 -0
- data/spec/jira/auto/tool/project/ticket_fields_spec.rb +390 -0
- data/spec/jira/auto/tool/project_spec.rb +31 -0
- data/spec/jira/auto/tool/rate_limited_jira_client_spec.rb +82 -0
- data/spec/jira/auto/tool/request_builder/field_context_fetcher_spec.rb +54 -0
- data/spec/jira/auto/tool/request_builder/field_option_fetcher_spec.rb +64 -0
- data/spec/jira/auto/tool/request_builder/get_spec.rb +40 -0
- data/spec/jira/auto/tool/request_builder/sprint_creator_spec.rb +179 -0
- data/spec/jira/auto/tool/request_builder/sprint_state_updater_spec.rb +31 -0
- data/spec/jira/auto/tool/request_builder_spec.rb +73 -0
- data/spec/jira/auto/tool/sprint/name_spec.rb +101 -0
- data/spec/jira/auto/tool/sprint/prefix_spec.rb +207 -0
- data/spec/jira/auto/tool/sprint_controller_spec.rb +406 -0
- data/spec/jira/auto/tool/sprint_spec.rb +309 -0
- data/spec/jira/auto/tool/team_spec.rb +21 -0
- data/spec/jira/auto/tool/team_sprint_prefix_mapper_spec.rb +97 -0
- data/spec/jira/auto/tool/team_sprint_ticket_dispatcher_spec.rb +232 -0
- data/spec/jira/auto/tool/ticket_spec.rb +116 -0
- data/spec/jira/auto/tool/until_date_spec.rb +80 -0
- data/spec/jira/auto/tool_spec.rb +458 -0
- data/spec/spec_helper.rb +42 -0
- metadata +368 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
Feature: Sprint Filtering
|
2
|
+
In order to target specific sprints
|
3
|
+
As a user
|
4
|
+
I need the ability to filter sprints
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given a Jira Scrum board
|
8
|
+
And the board only has the following sprints:
|
9
|
+
| name | length | start_date | state |
|
10
|
+
| ART-16_CRM_24.4.1 | 2-week | 2024-12-01 11:00:00 UTC | closed |
|
11
|
+
| ART-16_E2E-Test_24.4.1 | 4-day | 2024-12-01 11:00:00 UTC | future |
|
12
|
+
| ART-32_Sys-Team_24.4.12 | 1-week | 2024-12-24 11:00:00 UTC | future |
|
13
|
+
| ART-32_Sys-Team_25.1.1 | 1-week | 2025-01-07 11:00:00 UTC | future |
|
14
|
+
| ART-16_E2E-Test_24.4.2 | 4-day | 2024-12-05 11:00:00 UTC | future |
|
15
|
+
| ART-32_Platform_24.4.7 | 3-week | 2024-10-07 11:00:00 UTC | future |
|
16
|
+
|
17
|
+
|
18
|
+
Scenario: No filter applied except ignoring closed sprints
|
19
|
+
When I successfully run `jira-auto-tool --sprint-list`
|
20
|
+
Then the output should match:
|
21
|
+
"""
|
22
|
+
\+-------+--\+
|
23
|
+
\| \s+ Matching Sprints \s+ \|
|
24
|
+
\+-------\+-------------------------\+----------------\+-------------------------\+-------------------------\+-----------------------\+---------------------------------+--\+-------------------\+
|
25
|
+
\| Id \| Name \| Length In Days \| Start Date \| End Date \| Board Name \| Board UI URL \s+ \| Board Project Key \|
|
26
|
+
\+-------\+-------------------------\+----------------\+-------------------------\+-------------------------\+-----------------------\+---------------------------------+--\+-------------------\+
|
27
|
+
"""
|
28
|
+
And the output should match:
|
29
|
+
"""
|
30
|
+
.+-------------------------.+
|
31
|
+
.+ Name .+
|
32
|
+
.+-------------------------.+
|
33
|
+
.+ ART-32_Platform_24.4.7 .+
|
34
|
+
.+ ART-16_E2E-Test_24.4.1 .+
|
35
|
+
.+ ART-16_E2E-Test_24.4.2 .+
|
36
|
+
.+ ART-32_Sys-Team_24.4.12 .+
|
37
|
+
.+ ART-32_Sys-Team_25.1.1 .+
|
38
|
+
.+-------------------------.+
|
39
|
+
"""
|
40
|
+
|
41
|
+
Scenario: No filtering (except closed sprints) and excluding the corresponding board information
|
42
|
+
When I successfully run `jira-auto-tool --sprint-list-without-board-info`
|
43
|
+
Then the stdout should contain:
|
44
|
+
"""
|
45
|
+
+------------------------------------------------------------------------------------------------------+
|
46
|
+
| Matching Sprints |
|
47
|
+
+-------+-------------------------+----------------+-------------------------+-------------------------+
|
48
|
+
| Id | Name | Length In Days | Start Date | End Date |
|
49
|
+
+-------+-------------------------+----------------+-------------------------+-------------------------+
|
50
|
+
"""
|
51
|
+
And the output should match:
|
52
|
+
"""
|
53
|
+
.+-------------------------.+
|
54
|
+
.+ Name .+
|
55
|
+
.+-------------------------.+
|
56
|
+
.+ ART-32_Platform_24.4.7 .+
|
57
|
+
.+ ART-16_E2E-Test_24.4.1 .+
|
58
|
+
.+ ART-16_E2E-Test_24.4.2 .+
|
59
|
+
.+ ART-32_Sys-Team_24.4.12 .+
|
60
|
+
.+ ART-32_Sys-Team_25.1.1 .+
|
61
|
+
.+-------------------------.+
|
62
|
+
"""
|
63
|
+
|
64
|
+
Scenario: Filter sprints with a string
|
65
|
+
Given the following environment variables are set:
|
66
|
+
| name | value |
|
67
|
+
| ART_SPRINT_REGEX | ART-16 |
|
68
|
+
When I successfully run `jira-auto-tool --sprint-list`
|
69
|
+
Then the output should match:
|
70
|
+
"""
|
71
|
+
\+---------+------------\+
|
72
|
+
\| \s+ Matching Sprints \s+ \|
|
73
|
+
\+-------\+---------+--------\+----------------\+-------------------------\+-------------------------\+-----------------------\+---------------------------------+--\+-------------------\+
|
74
|
+
\| Id \| Name \s+ \| Length In Days \| Start Date \| End Date \| Board Name \| Board UI URL \s+ \| Board Project Key \|
|
75
|
+
\+-------\+---------+--------\+----------------\+-------------------------\+-------------------------\+-----------------------\+---------------------------------+--\+-------------------\+
|
76
|
+
"""
|
77
|
+
And the output should match:
|
78
|
+
"""
|
79
|
+
.*------------------------.*
|
80
|
+
.* .*
|
81
|
+
.*------------------------.*
|
82
|
+
.* Name .*
|
83
|
+
.*------------------------.*
|
84
|
+
.* ART-16_E2E-Test_24.4.1 .*
|
85
|
+
.* ART-16_E2E-Test_24.4.2 .*
|
86
|
+
.*------------------------.*
|
87
|
+
"""
|
88
|
+
|
89
|
+
Scenario: Filter sprints with a regular expression
|
90
|
+
Given the following environment variables are set:
|
91
|
+
| name | value |
|
92
|
+
| ART_SPRINT_REGEX | Platform\|(4\.1)$ |
|
93
|
+
When I successfully run `jira-auto-tool --sprint-list`
|
94
|
+
Then the output should match:
|
95
|
+
"""
|
96
|
+
\+-----------+------\+
|
97
|
+
\| \s+ Matching Sprints \s+ \|
|
98
|
+
\+-------\+---------+-------\+----------------\+-------------------------\+-------------------------\+-----------------------\+---------------------------------+--\+-------------------\+
|
99
|
+
\| Id \| Name \s+ \| Length In Days \| Start Date \| End Date \| Board Name \| Board UI URL \s+ \| Board Project Key \|
|
100
|
+
\+-------\+---------+-------\+----------------\+-------------------------\+-------------------------\+-----------------------\+---------------------------------+--\+-------------------\+
|
101
|
+
"""
|
102
|
+
And the output should match:
|
103
|
+
"""
|
104
|
+
.+------------------------.+
|
105
|
+
.+ Name .+
|
106
|
+
.+------------------------.+
|
107
|
+
.+ ART-32_Platform_24.4.7 .+
|
108
|
+
.+ ART-16_E2E-Test_24.4.1 .+
|
109
|
+
.+------------------------.+
|
110
|
+
"""
|
111
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Given("the current date time is {string}") do |current_date_time|
|
4
|
+
set_environment_variable("JAT_CURRENT_DATE_TIME", current_date_time)
|
5
|
+
end
|
6
|
+
|
7
|
+
Given(/^the following environment variables are set:$/) do |table|
|
8
|
+
table.hashes.each do |env_var|
|
9
|
+
name = env_var.fetch("name")
|
10
|
+
value = env_var.fetch("value")
|
11
|
+
@jira_auto_tool.send("#{name.downcase}=", value)
|
12
|
+
set_environment_variable(name, value)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
BUFFER_TIME_IN_SECONDS = 10
|
17
|
+
Then(/^successfully running `(.*)` takes between (.*) and (.*) seconds$/) do |command_line, minimal_time, maximal_time|
|
18
|
+
start_time = Time.now
|
19
|
+
|
20
|
+
run_command_and_stop(command_line, fail_on_error: true, timeout: maximal_time.to_i + BUFFER_TIME_IN_SECONDS)
|
21
|
+
|
22
|
+
end_time = Time.now
|
23
|
+
|
24
|
+
expect(end_time - start_time).to be_between(minimal_time.to_i, maximal_time.to_i)
|
25
|
+
end
|
26
|
+
|
27
|
+
Given(/^I wait for over a day$/) do
|
28
|
+
in_over_a_day = (Time.now + 1.day + 2.minute).to_s
|
29
|
+
|
30
|
+
log.debug { "Waiting until #{in_over_a_day}" }
|
31
|
+
|
32
|
+
set_environment_variable("JAT_CURRENT_DATE_TIME", in_over_a_day)
|
33
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "time"
|
4
|
+
|
5
|
+
Given(/^a Jira Scrum board$/) do
|
6
|
+
expect(@board).not_to be_nil
|
7
|
+
end
|
8
|
+
|
9
|
+
Given(/^the board has no sprint$/) do
|
10
|
+
expect(@board.jira_board.sprints).to be_empty
|
11
|
+
end
|
12
|
+
|
13
|
+
Given(/^the board has only closed sprints$/) do
|
14
|
+
4.times do |sprint_index|
|
15
|
+
@jira_auto_tool.create_sprint(name: "sprint_24.1.#{sprint_index}", state: "closed",
|
16
|
+
start_date: Time.now + sprint_index.days, length_in_days: 14)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
DAYS_IN_A_WEEK = 7
|
21
|
+
|
22
|
+
def parse_length_in_days(length_string)
|
23
|
+
case length_string
|
24
|
+
when /(\d+)-week/
|
25
|
+
Regexp.last_match(1).to_i * DAYS_IN_A_WEEK
|
26
|
+
when /(\d+)-day/
|
27
|
+
Regexp.last_match(1).to_i
|
28
|
+
else
|
29
|
+
raise "#{length_string} is not an expected sprint length"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Given(/^an unclosed (.+) sprint named (.+) starting on (.+)$/) do |sprint_length, sprint_name, start_date_time|
|
34
|
+
@jira_auto_tool.create_sprint({ name: sprint_name, start_date: start_date_time,
|
35
|
+
length_in_days: parse_length_in_days(sprint_length), state: "future" })
|
36
|
+
end
|
37
|
+
|
38
|
+
Then(/^a sprint named (.*) should exist$/) do |expected_name|
|
39
|
+
unclosed_sprints = @jira_auto_tool.sprint_controller.unclosed_sprints
|
40
|
+
expect(unclosed_sprints.collect(&:name)).to include(expected_name)
|
41
|
+
@actual_sprint = unclosed_sprints.find { |sprint| sprint.name == expected_name }
|
42
|
+
end
|
43
|
+
|
44
|
+
And(/^it starts on (.*)$/) do |expected_start|
|
45
|
+
expect(@actual_sprint.start_date).to eq(Time.parse(expected_start).utc)
|
46
|
+
end
|
47
|
+
|
48
|
+
And(/^it ends on (.*)$/) do |expected_end|
|
49
|
+
expect(@actual_sprint.end_date).to eq(Time.parse(expected_end).utc)
|
50
|
+
end
|
51
|
+
|
52
|
+
And(/^its state is (.*)$/) do |expected_state|
|
53
|
+
expect(@actual_sprint.state).to eq(expected_state)
|
54
|
+
end
|
55
|
+
|
56
|
+
Given(/^the board only has the following sprints:$/) do |table|
|
57
|
+
table_value_keys = table.raw.first
|
58
|
+
|
59
|
+
table.hashes.each do |sprint_hash|
|
60
|
+
attributes = {}
|
61
|
+
|
62
|
+
table_value_keys.each do |key|
|
63
|
+
value = sprint_hash[key]
|
64
|
+
|
65
|
+
case key.intern
|
66
|
+
when :length
|
67
|
+
attributes[:length_in_days] = parse_length_in_days(value)
|
68
|
+
when :expecting_added_sprint, :comment
|
69
|
+
next
|
70
|
+
else
|
71
|
+
attributes[key.intern] = value
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
@jira_auto_tool.create_sprint(attributes)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
Then(/^afterwards the board only has the following sprints:$/) do |table|
|
80
|
+
expected_value_keys = table.raw.first
|
81
|
+
|
82
|
+
expected_sprints = table.hashes.collect(&:values).collect(&:flatten)
|
83
|
+
|
84
|
+
actual_sprints = @jira_auto_tool.sprint_controller.sprints.collect do |sprint|
|
85
|
+
expected_value_keys.collect do |key|
|
86
|
+
unavailable_date = key =~ /date/ && !sprint.send("#{key}?")
|
87
|
+
|
88
|
+
value = unavailable_date ? "" : sprint.send(key)
|
89
|
+
|
90
|
+
value.is_a?(Time) ? value.utc.to_s : value
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
expect(actual_sprints).to contain_exactly(*expected_sprints)
|
95
|
+
end
|
96
|
+
|
97
|
+
Then(/^the output contains (no )?requests that enumerate the list of boards$/) do |contains_or_not|
|
98
|
+
contains_predicate = contains_or_not.nil? || contains_or_not.empty? ? :to : :not_to
|
99
|
+
|
100
|
+
board_enumeration_pattern = /#{Regexp.escape("get: /rest/agile/1.0/board - []")}/
|
101
|
+
expect(last_command_started.output).send(contains_predicate, match(board_enumeration_pattern))
|
102
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
And(/^tickets on the board have an expected date field named "([^"]*)"$/) do |date_field_name|
|
4
|
+
@date_field = @jira_auto_tool.expected_start_date_field(date_field_name)
|
5
|
+
|
6
|
+
expect(@date_field).not_to be_nil
|
7
|
+
end
|
8
|
+
|
9
|
+
And(/^tickets on the board have a team field named "([^"]*)" with exactly those values:$/) do
|
10
|
+
|team_field_name, field_values|
|
11
|
+
|
12
|
+
expected_field_values = field_values.hashes.collect { |hashes| hashes["values"] }
|
13
|
+
|
14
|
+
@team_field = @jira_auto_tool.implementation_team_field(team_field_name)
|
15
|
+
|
16
|
+
expect(@team_field.values.collect(&:value)).to eq(expected_field_values)
|
17
|
+
end
|
18
|
+
|
19
|
+
Given(/^the following tickets exist:$/) do |ticket_table|
|
20
|
+
# Summary | Description | Implementation Team | Expected Start |
|
21
|
+
# table is a table.hashes.keys # => [:summary, :team, :expected_start]
|
22
|
+
ticket_table.hashes.each do |ticket_info|
|
23
|
+
log.debug { ticket_info.inspect }
|
24
|
+
|
25
|
+
jira_ticket = @jira_auto_tool.jira_client.Issue.build
|
26
|
+
|
27
|
+
jira_ticket.save!({ fields: {
|
28
|
+
project: { key: @jira_auto_tool.board.project_key },
|
29
|
+
summary: ticket_info[:summary],
|
30
|
+
description: ticket_info[:description],
|
31
|
+
issuetype: { name: "Task" },
|
32
|
+
@jira_auto_tool.implementation_team_field.id.intern =>
|
33
|
+
{ "value" => ticket_info[:implementation_team] },
|
34
|
+
@jira_auto_tool.expected_start_date_field.id.intern => ticket_info[:expected_start_date]
|
35
|
+
} })
|
36
|
+
|
37
|
+
log.debug { "created jira ticket: #{jira_ticket.key}" }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
Then(/^the tickets should have been assigned to sprints as follows:$/) do |ticket_expectation_table|
|
42
|
+
# table is a table.hashes.keys # => [:summary, :sprint]
|
43
|
+
ticket_expectations =
|
44
|
+
ticket_expectation_table.hashes.collect { |expectation| [expectation[:summary], expectation[:sprint]] }
|
45
|
+
|
46
|
+
actual_ticket_values = @jira_auto_tool.tickets.collect do |ticket|
|
47
|
+
sprint = ticket.sprint
|
48
|
+
|
49
|
+
log.debug { "ticket: #{ticket.summary} sprint: #{sprint.inspect}" }
|
50
|
+
|
51
|
+
[ticket.summary, sprint&.first&.fetch("name") || ""]
|
52
|
+
end
|
53
|
+
|
54
|
+
expect(actual_ticket_values.sort).to eq(ticket_expectations.sort)
|
55
|
+
end
|
56
|
+
|
57
|
+
Given(/^a Jira project$/) do
|
58
|
+
@project = @jira_auto_tool.project
|
59
|
+
end
|
60
|
+
|
61
|
+
And(/^JAT_TICKETS_FOR_TEAM_SPRINT_TICKET_DISPATCHER_JQL has been defined as an environment variable$/) do
|
62
|
+
expect(ENV).to have_key("JAT_TICKETS_FOR_TEAM_SPRINT_TICKET_DISPATCHER_JQL")
|
63
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module JiraSprintToolWorld
|
4
|
+
def log
|
5
|
+
@log ||= Logging.logger[self]
|
6
|
+
end
|
7
|
+
|
8
|
+
def remove_existing_sprints(jira_auto_tool)
|
9
|
+
sprints = jira_auto_tool.sprint_controller.sprints
|
10
|
+
|
11
|
+
log.debug { "Removing sprints #sprints = #{sprints.size}: #{sprints.map(&:name).join(", ")}" }
|
12
|
+
|
13
|
+
sprints.each(&:delete)
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove_existing_board_tickets(jira_auto_tool)
|
17
|
+
tickets = jira_auto_tool.jira_client.Issue.jql("project = #{jira_auto_tool.board.project_key}")
|
18
|
+
|
19
|
+
log.debug { "Removing tickets from board #{jira_auto_tool.board.name}: #tickets = #{tickets.size}" }
|
20
|
+
|
21
|
+
tickets.each(&:delete)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
World(JiraSprintToolWorld)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jira-ruby"
|
4
|
+
|
5
|
+
require "jira/auto/tool"
|
6
|
+
|
7
|
+
def ensure_the_aruba_sandbox_is_active
|
8
|
+
setup_aruba
|
9
|
+
ENV["HOME"] = expand_path(".")
|
10
|
+
cd(".")
|
11
|
+
|
12
|
+
expect(Dir.home).to eq(expand_path("."))
|
13
|
+
end
|
14
|
+
|
15
|
+
Before do
|
16
|
+
ensure_the_aruba_sandbox_is_active
|
17
|
+
|
18
|
+
@jira_auto_tool = Jira::Auto::Tool.new
|
19
|
+
@jira_client = @jira_auto_tool.jira_client
|
20
|
+
@board = @jira_auto_tool.board
|
21
|
+
|
22
|
+
remove_existing_sprints(@jira_auto_tool)
|
23
|
+
remove_existing_board_tickets(@jira_auto_tool)
|
24
|
+
Jira::Auto::Tool::Board::Cache.new(@jira_auto_tool).clear
|
25
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rspec"
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.expect_with :rspec do |expectations|
|
7
|
+
expectations.syntax = :expect
|
8
|
+
expectations.max_formatted_output_length = nil
|
9
|
+
end
|
10
|
+
|
11
|
+
config.mock_with :rspec do |mocks|
|
12
|
+
mocks.verify_partial_doubles = true
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
Feature: Rename sprints
|
2
|
+
In order to adjust the end date of a sprint to cope with specific periods like year end
|
3
|
+
As a team member
|
4
|
+
I need the ability to update the end date of a sprint and shift the following sprints
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given a Jira Scrum board
|
8
|
+
|
9
|
+
Scenario: Adjust the end of a sprint and shift the following ones so there are no gaps
|
10
|
+
Given the board only has the following sprints:
|
11
|
+
| comment | name | length | start_date | state |
|
12
|
+
| "no | Food_Supply_25.1.2 | 2-week | 2025-01-07 11:00:00 UTC | closed |
|
13
|
+
| change | Food_Supply_25.1.3 | 2-week | 2025-01-21 11:00:00 UTC | closed |
|
14
|
+
| since | Food_Supply_25.1.4 | 2-week | 2025-02-04 11:00:00 UTC | closed |
|
15
|
+
| closed" | Food_Supply_25.1.5 | 2-week | 2025-02-18 11:00:00 UTC | closed |
|
16
|
+
| | Food_Delivery_25.1.2 | 2-week | 2025-01-07 11:00:00 UTC | future |
|
17
|
+
| | Food_Delivery_25.1.3 | 2-week | 2025-01-21 11:00:00 UTC | future |
|
18
|
+
| | Food_Delivery_25.1.4 | 2-week | 2025-02-04 11:00:00 UTC | future |
|
19
|
+
| new end date | Food_Delivery_25.1.5 | 2-week | 2025-02-18 11:00:00 UTC | future |
|
20
|
+
| shifted | Food_Delivery_25.2.1 | 2-week | 2025-03-04 11:00:00 UTC | future |
|
21
|
+
| idem | Food_Delivery_25.2.2 | 2-week | 2025-03-18 11:00:00 UTC | future |
|
22
|
+
| idem | Food_Delivery_25.2.3 | 2-week | 2025-04-01 11:00:00 UTC | future |
|
23
|
+
| idem | Food_Delivery_25.2.4 | 2-week | 2025-04-15 11:00:00 UTC | future |
|
24
|
+
| idem | Food_Delivery_25.2.5 | 2-week | 2025-04-29 11:00:00 UTC | future |
|
25
|
+
| | Food_Restaurant_24.4.1 | 4-day | 2025-01-07 11:00:00 UTC | future |
|
26
|
+
| "last two | Food_Restaurant_25.1.5 | 4-day | 2025-01-18 11:00:00 UTC | future |
|
27
|
+
| updated" | Food_Restaurant_25.2.1 | 4-day | 2025-04-01 11:00:00 UTC | future |
|
28
|
+
| "no update | Food_Market_25.2.5 | 4-day | 2025-06-14 11:00:00 UTC | future |
|
29
|
+
| since | Food_Market_25.3.1 | 4-day | 2025-07-07 11:00:00 UTC | future |
|
30
|
+
| not matching" | Food_Market_25.3.2 | 4-day | 2025-07-21 11:00:00 UTC | future |
|
31
|
+
When I successfully run `jira-auto-tool --sprint-update-end-date=25.1.5,"2025-02-25 16:00:00 UTC"`
|
32
|
+
Then afterwards the board only has the following sprints:
|
33
|
+
| name | start_date | end_date | state |
|
34
|
+
| Food_Supply_25.1.2 | 2025-01-07 11:00:00 UTC | 2025-01-21 11:00:00 UTC | closed |
|
35
|
+
| Food_Supply_25.1.3 | 2025-01-21 11:00:00 UTC | 2025-02-04 11:00:00 UTC | closed |
|
36
|
+
| Food_Supply_25.1.4 | 2025-02-04 11:00:00 UTC | 2025-02-18 11:00:00 UTC | closed |
|
37
|
+
| Food_Supply_25.1.5 | 2025-02-18 11:00:00 UTC | 2025-03-04 11:00:00 UTC | closed |
|
38
|
+
| Food_Delivery_25.1.2 | 2025-01-07 11:00:00 UTC | 2025-01-21 11:00:00 UTC | future |
|
39
|
+
| Food_Delivery_25.1.3 | 2025-01-21 11:00:00 UTC | 2025-02-04 11:00:00 UTC | future |
|
40
|
+
| Food_Delivery_25.1.4 | 2025-02-04 11:00:00 UTC | 2025-02-18 11:00:00 UTC | future |
|
41
|
+
| Food_Delivery_25.1.5 | 2025-02-18 11:00:00 UTC | 2025-02-25 16:00:00 UTC | future |
|
42
|
+
| Food_Delivery_25.2.1 | 2025-02-25 16:00:00 UTC | 2025-03-11 16:00:00 UTC | future |
|
43
|
+
| Food_Delivery_25.2.2 | 2025-03-11 16:00:00 UTC | 2025-03-25 16:00:00 UTC | future |
|
44
|
+
| Food_Delivery_25.2.3 | 2025-03-25 16:00:00 UTC | 2025-04-08 16:00:00 UTC | future |
|
45
|
+
| Food_Delivery_25.2.4 | 2025-04-08 16:00:00 UTC | 2025-04-22 16:00:00 UTC | future |
|
46
|
+
| Food_Delivery_25.2.5 | 2025-04-22 16:00:00 UTC | 2025-05-06 16:00:00 UTC | future |
|
47
|
+
| Food_Restaurant_24.4.1 | 2025-01-07 11:00:00 UTC | 2025-01-11 11:00:00 UTC | future |
|
48
|
+
| Food_Restaurant_25.1.5 | 2025-01-18 11:00:00 UTC | 2025-02-25 16:00:00 UTC | future |
|
49
|
+
| Food_Restaurant_25.2.1 | 2025-02-25 16:00:00 UTC | 2025-03-01 16:00:00 UTC | future |
|
50
|
+
| Food_Market_25.2.5 | 2025-06-14 11:00:00 UTC | 2025-06-18 11:00:00 UTC | future |
|
51
|
+
| Food_Market_25.3.1 | 2025-07-07 11:00:00 UTC | 2025-07-11 11:00:00 UTC | future |
|
52
|
+
| Food_Market_25.3.2 | 2025-07-21 11:00:00 UTC | 2025-07-25 11:00:00 UTC | future |
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "fileutils"
|
5
|
+
require "jira/auto/tool/helpers/overridable_time"
|
6
|
+
|
7
|
+
module Jira
|
8
|
+
module Auto
|
9
|
+
class Tool
|
10
|
+
class Board
|
11
|
+
class Cache
|
12
|
+
attr_reader :tool
|
13
|
+
|
14
|
+
def initialize(tool)
|
15
|
+
@tool = tool
|
16
|
+
end
|
17
|
+
|
18
|
+
def boards
|
19
|
+
raise "This method should not be used since the cache is invalid" unless valid?
|
20
|
+
|
21
|
+
@boards ||= raw_content.fetch("boards").collect do |board_info|
|
22
|
+
Board.new(tool, tool.jira_client.Board.build(board_info))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def save(boards)
|
27
|
+
File.write(file_path, { "boards" => boards.collect { |board| board.jira_board.attrs } }.to_yaml)
|
28
|
+
|
29
|
+
boards
|
30
|
+
end
|
31
|
+
|
32
|
+
def clear
|
33
|
+
FileUtils.rm_f(file_path)
|
34
|
+
end
|
35
|
+
|
36
|
+
def valid?
|
37
|
+
File.exist?(file_path) && !expired?
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def expired?
|
43
|
+
log.debug { "expired? #{cached_at} < #{one_day_ago}" }
|
44
|
+
|
45
|
+
cached_at < one_day_ago
|
46
|
+
end
|
47
|
+
|
48
|
+
def one_day_ago
|
49
|
+
Helpers::OverridableTime.now - 1.day
|
50
|
+
end
|
51
|
+
|
52
|
+
def cached_at
|
53
|
+
File.mtime(file_path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def raw_content
|
57
|
+
YAML.safe_load_file(file_path) || {}
|
58
|
+
end
|
59
|
+
|
60
|
+
def file_path
|
61
|
+
File.join(tool.config.dir, "jira-auto-tool.cache.yml")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Jira
|
4
|
+
module Auto
|
5
|
+
class Tool
|
6
|
+
class Board
|
7
|
+
class UnavailableBoard < Board
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
attr_reader :id
|
11
|
+
|
12
|
+
def initialize(tool, id)
|
13
|
+
super(tool, JIRA::Resource::Board.new(id: id))
|
14
|
+
@id = id
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
"N/A"
|
19
|
+
end
|
20
|
+
|
21
|
+
def url
|
22
|
+
"N/A"
|
23
|
+
end
|
24
|
+
|
25
|
+
def ui_url
|
26
|
+
"N/A"
|
27
|
+
end
|
28
|
+
|
29
|
+
def <=>(other)
|
30
|
+
id <=> other.id
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jira/auto/tool/board/unavailable_board"
|
4
|
+
|
5
|
+
module Jira
|
6
|
+
module Auto
|
7
|
+
class Tool
|
8
|
+
class Board
|
9
|
+
include Comparable
|
10
|
+
|
11
|
+
attr_reader :tool, :jira_board
|
12
|
+
|
13
|
+
def self.find_by_id(tool, id)
|
14
|
+
cached_boards[id] ||=
|
15
|
+
begin
|
16
|
+
new(tool, JIRA::Resource::Board.find(tool.jira_client, id))
|
17
|
+
rescue JIRA::HTTPError => e
|
18
|
+
if e.code.to_i == 404
|
19
|
+
UnavailableBoard.new(tool, id)
|
20
|
+
else
|
21
|
+
raise e.class,
|
22
|
+
"#{e.class}: code = #{e.code.inspect}: #{self.class}.find_by_id(tool, #{id.inspect}): " \
|
23
|
+
"#{e.message}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.cached_boards
|
29
|
+
@cached_boards ||= {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(tool, jira_board)
|
33
|
+
@tool = tool
|
34
|
+
@jira_board = jira_board
|
35
|
+
end
|
36
|
+
|
37
|
+
def id
|
38
|
+
jira_board.id
|
39
|
+
end
|
40
|
+
|
41
|
+
def unavailable?
|
42
|
+
instance_of?(UnavailableBoard)
|
43
|
+
end
|
44
|
+
|
45
|
+
def <=>(other)
|
46
|
+
id <=> other.id
|
47
|
+
end
|
48
|
+
|
49
|
+
def name
|
50
|
+
jira_board.name
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.to_table_row_field_names
|
54
|
+
%i[name ui_url project_key]
|
55
|
+
end
|
56
|
+
|
57
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
58
|
+
inflect.acronym "UI"
|
59
|
+
inflect.acronym "URL"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.to_table_row_header
|
63
|
+
to_table_row_field_names.collect { |field| field.to_s.titleize }
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_table_row
|
67
|
+
self.class.to_table_row_field_names.collect { |field| send(field) }
|
68
|
+
end
|
69
|
+
|
70
|
+
PROJECT_INFORMATION_NOT_AVAILABLE = "N/A"
|
71
|
+
|
72
|
+
def project_key
|
73
|
+
if with_project_information?
|
74
|
+
jira_board.location.fetch("projectKey")
|
75
|
+
else
|
76
|
+
PROJECT_INFORMATION_NOT_AVAILABLE
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def with_project_information?
|
81
|
+
jira_board.respond_to?(:location)
|
82
|
+
end
|
83
|
+
|
84
|
+
def sprint_compatible?
|
85
|
+
jira_board.type =~ /^(scrum)$/
|
86
|
+
end
|
87
|
+
|
88
|
+
def url
|
89
|
+
jira_board.url
|
90
|
+
end
|
91
|
+
|
92
|
+
def ui_url
|
93
|
+
request_path =
|
94
|
+
if with_project_information?
|
95
|
+
"/jira/software/c/projects/#{project_key}/boards/#{id}"
|
96
|
+
else
|
97
|
+
"/secure/RapidBoard.jspa?rapidView=#{id}"
|
98
|
+
end
|
99
|
+
|
100
|
+
tool.jira_url(request_path)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|