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.
Files changed (139) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +291 -0
  4. data/CHANGELOG.md +5 -0
  5. data/CODE_OF_CONDUCT.md +132 -0
  6. data/Guardfile +105 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +159 -0
  9. data/Rakefile +20 -0
  10. data/bin/jira-auto-tool +57 -0
  11. data/bin/jira-auto-tool.bat +2 -0
  12. data/bin/setup +8 -0
  13. data/bin/setup-dev-win.bat +3 -0
  14. data/cucumber.yml +7 -0
  15. data/features/align_sprint_time_in_dates.feature +33 -0
  16. data/features/assign_tickets_to_team_sprints.feature +73 -0
  17. data/features/cache_boards.feature +24 -0
  18. data/features/control_http_request_rate_limit.feature +17 -0
  19. data/features/create_sprints_using_existing_ones_as_reference.feature +79 -0
  20. data/features/list_boards.feature +12 -0
  21. data/features/list_project_fields.feature +89 -0
  22. data/features/list_sprint_prefixes.feature +77 -0
  23. data/features/quarterly_add_sprints_using_existing_ones_as_a_reference.feature +71 -0
  24. data/features/quarterly_create_sprints_until_specific_date.feature +75 -0
  25. data/features/quarterly_rename_sprints.feature +179 -0
  26. data/features/rename_sprints.feature +203 -0
  27. data/features/self_documented_command_line.feature +15 -0
  28. data/features/sprint_filtering.feature +111 -0
  29. data/features/step_definitions/execution_context_steps.rb +33 -0
  30. data/features/step_definitions/jira_board_steps.rb +102 -0
  31. data/features/step_definitions/jira_ticket_steps.rb +63 -0
  32. data/features/support/10.setup_cucumber.rb +10 -0
  33. data/features/support/env.rb +25 -0
  34. data/features/support/hooks.rb +25 -0
  35. data/features/support/setup_rspec.rb +14 -0
  36. data/features/support/setup_simplecov.rb +5 -0
  37. data/features/update_sprint_end_date_and_shift_following_ones.feature +52 -0
  38. data/lib/jira/auto/tool/board/cache.rb +67 -0
  39. data/lib/jira/auto/tool/board/unavailable_board.rb +36 -0
  40. data/lib/jira/auto/tool/board.rb +105 -0
  41. data/lib/jira/auto/tool/board_controller/options.rb +32 -0
  42. data/lib/jira/auto/tool/board_controller.rb +88 -0
  43. data/lib/jira/auto/tool/common_options.rb +37 -0
  44. data/lib/jira/auto/tool/config/options.rb +19 -0
  45. data/lib/jira/auto/tool/config.rb +64 -0
  46. data/lib/jira/auto/tool/fetch_custom_field_options.rb +47 -0
  47. data/lib/jira/auto/tool/field.rb +59 -0
  48. data/lib/jira/auto/tool/field_controller.rb +50 -0
  49. data/lib/jira/auto/tool/field_option.rb +35 -0
  50. data/lib/jira/auto/tool/get_createmeta_for_project.rb +24 -0
  51. data/lib/jira/auto/tool/helpers/environment_based_value.rb +96 -0
  52. data/lib/jira/auto/tool/helpers/option_parser.rb +16 -0
  53. data/lib/jira/auto/tool/helpers/overridable_time.rb +18 -0
  54. data/lib/jira/auto/tool/helpers/pagination.rb +50 -0
  55. data/lib/jira/auto/tool/jira_http_options.rb +20 -0
  56. data/lib/jira/auto/tool/next_sprint_creator.rb +60 -0
  57. data/lib/jira/auto/tool/performer/options.rb +76 -0
  58. data/lib/jira/auto/tool/performer/planning_increment_sprint_creator.rb +42 -0
  59. data/lib/jira/auto/tool/performer/prefix_sprint_updater.rb +42 -0
  60. data/lib/jira/auto/tool/performer/quarterly_sprint_renamer/next_name_generator.rb +60 -0
  61. data/lib/jira/auto/tool/performer/quarterly_sprint_renamer.rb +19 -0
  62. data/lib/jira/auto/tool/performer/sprint_end_date_updater.rb +55 -0
  63. data/lib/jira/auto/tool/performer/sprint_renamer/keep_same_name_generator.rb +19 -0
  64. data/lib/jira/auto/tool/performer/sprint_renamer/next_name_generator.rb +47 -0
  65. data/lib/jira/auto/tool/performer/sprint_renamer.rb +55 -0
  66. data/lib/jira/auto/tool/performer/sprint_time_in_dates_aligner.rb +52 -0
  67. data/lib/jira/auto/tool/project/options.rb +22 -0
  68. data/lib/jira/auto/tool/project/ticket_fields.rb +70 -0
  69. data/lib/jira/auto/tool/project.rb +40 -0
  70. data/lib/jira/auto/tool/rate_limited_jira_client.rb +50 -0
  71. data/lib/jira/auto/tool/request_builder/field_context_fetcher.rb +78 -0
  72. data/lib/jira/auto/tool/request_builder/field_option_fetcher.rb +54 -0
  73. data/lib/jira/auto/tool/request_builder/get.rb +29 -0
  74. data/lib/jira/auto/tool/request_builder/sprint_creator.rb +112 -0
  75. data/lib/jira/auto/tool/request_builder/sprint_state_updater.rb +60 -0
  76. data/lib/jira/auto/tool/request_builder.rb +89 -0
  77. data/lib/jira/auto/tool/setup_logging.rb +35 -0
  78. data/lib/jira/auto/tool/sprint/name.rb +105 -0
  79. data/lib/jira/auto/tool/sprint/prefix.rb +66 -0
  80. data/lib/jira/auto/tool/sprint.rb +183 -0
  81. data/lib/jira/auto/tool/sprint_controller/options.rb +61 -0
  82. data/lib/jira/auto/tool/sprint_controller.rb +152 -0
  83. data/lib/jira/auto/tool/sprint_state_controller.rb +58 -0
  84. data/lib/jira/auto/tool/team.rb +23 -0
  85. data/lib/jira/auto/tool/team_sprint_prefix_mapper/options.rb +27 -0
  86. data/lib/jira/auto/tool/team_sprint_prefix_mapper.rb +62 -0
  87. data/lib/jira/auto/tool/team_sprint_ticket_dispatcher.rb +76 -0
  88. data/lib/jira/auto/tool/ticket.rb +110 -0
  89. data/lib/jira/auto/tool/until_date.rb +68 -0
  90. data/lib/jira/auto/tool/version.rb +9 -0
  91. data/lib/jira/auto/tool.rb +216 -0
  92. data/sig/jira/sprint/tool.rbs +8 -0
  93. data/spec/jira/auto/tool/board/cache_spec.rb +179 -0
  94. data/spec/jira/auto/tool/board/unavailable_board_spec.rb +34 -0
  95. data/spec/jira/auto/tool/board_controller/options_spec.rb +52 -0
  96. data/spec/jira/auto/tool/board_controller_spec.rb +154 -0
  97. data/spec/jira/auto/tool/board_spec.rb +163 -0
  98. data/spec/jira/auto/tool/common_options_spec.rb +49 -0
  99. data/spec/jira/auto/tool/config_spec.rb +108 -0
  100. data/spec/jira/auto/tool/field_controller_spec.rb +121 -0
  101. data/spec/jira/auto/tool/field_option_spec.rb +42 -0
  102. data/spec/jira/auto/tool/field_spec.rb +99 -0
  103. data/spec/jira/auto/tool/helpers/environment_based_value_spec.rb +21 -0
  104. data/spec/jira/auto/tool/helpers/option_parser_spec.rb +21 -0
  105. data/spec/jira/auto/tool/helpers/overridable_time_spec.rb +43 -0
  106. data/spec/jira/auto/tool/helpers/pagination_spec.rb +72 -0
  107. data/spec/jira/auto/tool/jira_http_options_spec.rb +32 -0
  108. data/spec/jira/auto/tool/next_sprint_creator_spec.rb +85 -0
  109. data/spec/jira/auto/tool/performer/option_spec.rb +55 -0
  110. data/spec/jira/auto/tool/performer/planning_increment_sprint_creator_spec.rb +62 -0
  111. data/spec/jira/auto/tool/performer/prefix_sprint_updater_spec.rb +35 -0
  112. data/spec/jira/auto/tool/performer/quarterly_sprint_renamer/next_name_generator_spec.rb +175 -0
  113. data/spec/jira/auto/tool/performer/quarterly_sprint_renamer_spec.rb +239 -0
  114. data/spec/jira/auto/tool/performer/sprint_end_date_updater_spec.rb +90 -0
  115. data/spec/jira/auto/tool/performer/sprint_renamer/keep_same_name_generator_spec.rb +12 -0
  116. data/spec/jira/auto/tool/performer/sprint_renamer/next_name_generator_spec.rb +129 -0
  117. data/spec/jira/auto/tool/performer/sprint_renamer_spec.rb +240 -0
  118. data/spec/jira/auto/tool/performer/sprint_time_in_dates_aligner_spec.rb +132 -0
  119. data/spec/jira/auto/tool/project/ticket_fields_spec.rb +390 -0
  120. data/spec/jira/auto/tool/project_spec.rb +31 -0
  121. data/spec/jira/auto/tool/rate_limited_jira_client_spec.rb +82 -0
  122. data/spec/jira/auto/tool/request_builder/field_context_fetcher_spec.rb +54 -0
  123. data/spec/jira/auto/tool/request_builder/field_option_fetcher_spec.rb +64 -0
  124. data/spec/jira/auto/tool/request_builder/get_spec.rb +40 -0
  125. data/spec/jira/auto/tool/request_builder/sprint_creator_spec.rb +179 -0
  126. data/spec/jira/auto/tool/request_builder/sprint_state_updater_spec.rb +31 -0
  127. data/spec/jira/auto/tool/request_builder_spec.rb +73 -0
  128. data/spec/jira/auto/tool/sprint/name_spec.rb +101 -0
  129. data/spec/jira/auto/tool/sprint/prefix_spec.rb +207 -0
  130. data/spec/jira/auto/tool/sprint_controller_spec.rb +406 -0
  131. data/spec/jira/auto/tool/sprint_spec.rb +309 -0
  132. data/spec/jira/auto/tool/team_spec.rb +21 -0
  133. data/spec/jira/auto/tool/team_sprint_prefix_mapper_spec.rb +97 -0
  134. data/spec/jira/auto/tool/team_sprint_ticket_dispatcher_spec.rb +232 -0
  135. data/spec/jira/auto/tool/ticket_spec.rb +116 -0
  136. data/spec/jira/auto/tool/until_date_spec.rb +80 -0
  137. data/spec/jira/auto/tool_spec.rb +458 -0
  138. data/spec/spec_helper.rb +42 -0
  139. metadata +368 -0
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+ require "jira-ruby"
5
+
6
+ require_relative "sprint_state_controller"
7
+ require_relative "sprint/name"
8
+
9
+ module Jira
10
+ module Auto
11
+ class Tool
12
+ class NextSprintCreator
13
+ def self.create_sprint_following(sprint)
14
+ new(sprint).create
15
+ end
16
+
17
+ attr_reader :sprint
18
+
19
+ def initialize(sprint)
20
+ log.debug { "Creating next sprint for #{sprint}" }
21
+ @sprint = sprint
22
+ end
23
+
24
+ def next_sprint_length_in_days
25
+ sprint.length_in_days
26
+ end
27
+
28
+ def create
29
+ RequestBuilder::SprintCreator.create_sprint(sprint.tool, sprint.origin_board_id, {
30
+ name: next_sprint_name,
31
+ start_date: next_sprint_start_date.utc.to_s,
32
+ length_in_days: next_sprint_length_in_days
33
+ })
34
+ end
35
+
36
+ INDEX_FIRST_SPRINT_IN_QUARTER = 1
37
+
38
+ def next_sprint_name
39
+ index_in_quarter =
40
+ if same_quarter?
41
+ sprint.index_in_quarter + 1
42
+ else
43
+ INDEX_FIRST_SPRINT_IN_QUARTER
44
+ end
45
+
46
+ Sprint::Name.new(sprint.name_prefix, next_sprint_start_date.year, next_sprint_start_date.quarter,
47
+ index_in_quarter).to_s
48
+ end
49
+
50
+ def same_quarter?
51
+ sprint.start_date.quarter == next_sprint_start_date.quarter
52
+ end
53
+
54
+ def next_sprint_start_date
55
+ sprint.end_date
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse/time"
4
+ require "jira/auto/tool/helpers/option_parser"
5
+ require "jira/auto/tool/performer/quarterly_sprint_renamer"
6
+ require "jira/auto/tool/performer/sprint_renamer"
7
+ require "jira/auto/tool/performer/sprint_time_in_dates_aligner"
8
+ require "jira/auto/tool/performer/sprint_end_date_updater"
9
+ require "jira/auto/tool/performer/planning_increment_sprint_creator"
10
+
11
+ module Jira
12
+ module Auto
13
+ class Tool
14
+ class Performer
15
+ module Options
16
+ def self.add(tool, parser)
17
+ parser.section_header "Sprint"
18
+ add_sprint_add(parser, tool)
19
+ add_sprint_align_time_in_dates(parser, tool)
20
+ add_quarterly_sprint_rename(parser, tool)
21
+ add_sprint_rename(parser, tool)
22
+ add_sprint_update_end_date(parser, tool)
23
+ end
24
+
25
+ def self.add_quarterly_sprint_rename(parser, tool)
26
+ parser.on("--quarterly-sprint-rename=FROM_STRING,TO_STRING", "--qsr", Array,
27
+ "Rename sprints starting with FROM_STRING to TO_STRING. The following sprints in the same " \
28
+ "planning increment will also be renamed. ") do |from_string, to_string|
29
+ QuarterlySprintRenamer.new(tool, from_string, to_string).run
30
+ end
31
+ end
32
+
33
+ SR_SHORT_OPTION_NAME = "--sr"
34
+ def self.add_sprint_rename(parser, tool)
35
+ parser.on("--sprint-rename=FROM_STRING,TO_STRING", SR_SHORT_OPTION_NAME, Array,
36
+ "Rename sprints starting with FROM_STRING to TO_STRING. The following sprints in the same " \
37
+ "prefix are also all going to be renamed " \
38
+ "irrespective of their original planning interval " \
39
+ "(e.g., #{SR_SHORT_OPTION_NAME}=25.3.6,25.4.1).") do |from_string, to_string|
40
+ SprintRenamer.new(tool, from_string, to_string).run
41
+ end
42
+ end
43
+
44
+ def self.add_sprint_update_end_date(parser, tool)
45
+ parser.on("--sprint-update-end-date=REGEX,NEW_END_DATE", "--sued", Array,
46
+ "Update the end of the sprint matching REGEX to NEW_END_DATE. " \
47
+ "The following sprints are shifted " \
48
+ "while keeping their original length planning increment will " \
49
+ "also be renamed. ") do |sprint_name_regex, new_end_date|
50
+ SprintEndDateUpdater.new(tool, sprint_name_regex, new_end_date).run
51
+ end
52
+ end
53
+
54
+ SATID_SHORT_OPTION_NAME = "--satid"
55
+ def self.add_sprint_align_time_in_dates(parser, tool)
56
+ parser.on("--sprint-align-time-in-dates=TIME", SATID_SHORT_OPTION_NAME, Time,
57
+ "Update the start and end dates of sprints to all have the specified time " \
58
+ "(e.g., #{SATID_SHORT_OPTION_NAME}=\"14:15 UTC\").") do |time|
59
+ SprintTimeInDatesAligner.new(tool, time).run
60
+ end
61
+ end
62
+
63
+ SA_SHORT_OPTION_NAME = "--sa"
64
+ def self.add_sprint_add(parser, tool)
65
+ parser.on("--sprint-add=YY.PI.START,COUNT", SA_SHORT_OPTION_NAME, Array,
66
+ "Add COUNT sprints for each sprint prefix/team sprints using " \
67
+ "the specified YY.PI.START (e.g., #{SA_SHORT_OPTION_NAME}=25.3.1) and " \
68
+ "the existing sprints as templates.") do |sprint_suffix, iteration_count|
69
+ PlanningIncrementSprintCreator.new(tool, sprint_suffix, Integer(iteration_count)).run
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/performer/prefix_sprint_updater"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Performer
9
+ class PlanningIncrementSprintCreator < PrefixSprintUpdater
10
+ attr_reader :sprint_suffix, :iteration_count
11
+
12
+ def initialize(tool, sprint_suffix, iteration_count)
13
+ super(tool)
14
+
15
+ @sprint_suffix = sprint_suffix
16
+ @iteration_count = iteration_count
17
+ end
18
+
19
+ def act_on_sprints_for_sprint_prefix(sprint_prefix)
20
+ last_sprint = sprint_prefix.last_sprint
21
+ parsed_new_name = Sprint::Name.new_with(sprint_prefix.name, sprint_suffix)
22
+
23
+ iteration_count.times do |_iteration|
24
+ last_sprint = create_sprint_for(last_sprint, parsed_new_name.to_s)
25
+
26
+ sprint_prefix << last_sprint
27
+
28
+ parsed_new_name = parsed_new_name.next_in_planning_interval
29
+ end
30
+ end
31
+
32
+ def create_sprint_for(last_sprint, new_name)
33
+ RequestBuilder::SprintCreator.create_sprint(
34
+ tool, last_sprint.origin_board_id,
35
+ name: new_name, start_date: last_sprint.end_date, length_in_days: last_sprint.length_in_days
36
+ )
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Performer
9
+ class PrefixSprintUpdater
10
+ attr_reader :tool
11
+
12
+ def initialize(tool)
13
+ @tool = tool
14
+ end
15
+
16
+ def sprint_prefixes
17
+ tool.unclosed_sprint_prefixes
18
+ end
19
+
20
+ def run
21
+ sprint_prefixes.each do |sprint_prefix|
22
+ @first_sprint_already_identified = false
23
+ act_on_sprints_for_sprint_prefix(sprint_prefix)
24
+ end
25
+ end
26
+
27
+ def act_on_sprints_for_sprint_prefix(sprint_prefix)
28
+ raise NotImplementedError
29
+ end
30
+
31
+ def first_sprint_to_act_on?(sprint_name)
32
+ if @first_sprint_already_identified
33
+ false
34
+ else
35
+ @first_sprint_already_identified = (sprint_name =~ from_string_regex)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/performer/quarterly_sprint_renamer"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Performer
9
+ class QuarterlySprintRenamer
10
+ class NextNameGenerator
11
+ attr_reader :original_name_of_first_renamed_sprint, :name_of_first_renamed_sprint
12
+
13
+ def initialize(original_name_of_first_renamed_sprint, name_of_first_renamed_sprint)
14
+ @original_name_of_first_renamed_sprint = Sprint::Name.parse(original_name_of_first_renamed_sprint)
15
+ @name_of_first_renamed_sprint = Sprint::Name.parse(name_of_first_renamed_sprint)
16
+ end
17
+
18
+ def name_for(sprint_name)
19
+ if outside_planning_interval_of_sprint_next_to_first_renamed_sprint?(sprint_name)
20
+ sprint_name
21
+ else
22
+ next_name_in_planning_interval
23
+ end
24
+ end
25
+
26
+ def new_name_of_sprint_next_to_first_renamed_sprint
27
+ @new_name_of_sprint_next_to_first_renamed_sprint ||=
28
+ if pulling_sprint_into_previous_planning_interval?
29
+ original_name_of_first_renamed_sprint
30
+ else
31
+ name_of_first_renamed_sprint.next_in_planning_interval
32
+ end
33
+ end
34
+
35
+ def next_name_in_planning_interval
36
+ @next_name_in_planning_interval ||= new_name_of_sprint_next_to_first_renamed_sprint
37
+
38
+ next_name = @next_name_in_planning_interval.to_s
39
+
40
+ @next_name_in_planning_interval = @next_name_in_planning_interval.next_in_planning_interval
41
+
42
+ next_name
43
+ end
44
+
45
+ def pulling_sprint_into_previous_planning_interval?
46
+ (name_of_first_renamed_sprint.planning_interval <=>
47
+ original_name_of_first_renamed_sprint.planning_interval)
48
+ .negative?
49
+ end
50
+
51
+ def outside_planning_interval_of_sprint_next_to_first_renamed_sprint?(sprint_name)
52
+ new_name_of_sprint_next_to_first_renamed_sprint.planning_interval !=
53
+ Sprint::Name.parse(sprint_name).planning_interval
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "sprint_renamer"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Performer
9
+ class QuarterlySprintRenamer < SprintRenamer
10
+ require_relative "quarterly_sprint_renamer/next_name_generator"
11
+
12
+ def next_sprint_name_generator_class
13
+ QuarterlySprintRenamer::NextNameGenerator
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "prefix_sprint_updater"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Performer
9
+ class SprintEndDateUpdater < PrefixSprintUpdater
10
+ attr_reader :from_string_regex, :new_end_date
11
+
12
+ def initialize(tool, from_string, new_end_date)
13
+ super(tool)
14
+
15
+ @from_string_regex = Regexp.new(Regexp.escape(from_string))
16
+ @new_end_date = Time.parse(new_end_date)
17
+ end
18
+
19
+ def act_on_sprints_for_sprint_prefix(sprint_prefix)
20
+ prefix_sprints = sprint_prefix.sprints
21
+ update_action = :do_nothing
22
+ new_start_date = nil
23
+
24
+ prefix_sprints.each do |sprint|
25
+ if first_sprint_to_act_on?(sprint.name)
26
+ update_sprint_end_date(sprint)
27
+ update_action = :shift_sprint_to_new_start_date
28
+ else
29
+ send(update_action, sprint, new_start_date)
30
+ end
31
+
32
+ new_start_date = sprint.end_date
33
+ end
34
+ end
35
+
36
+ def do_nothing(_sprint, _new_start_date) end
37
+
38
+ def update_sprint_end_date(sprint)
39
+ sprint.end_date = new_end_date
40
+ sprint.save
41
+ end
42
+
43
+ def shift_sprint_to_new_start_date(sprint, new_start_date)
44
+ length_in_days = sprint.length_in_days
45
+
46
+ sprint.start_date = new_start_date
47
+ sprint.end_date = new_start_date + length_in_days.days
48
+
49
+ sprint.save
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/performer/sprint_renamer"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Performer
9
+ class SprintRenamer
10
+ class KeepSameNameGenerator
11
+ def name_for(sprint_name)
12
+ sprint_name
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/performer/sprint_renamer"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Performer
9
+ class SprintRenamer
10
+ class NextNameGenerator
11
+ attr_reader :original_name_of_first_renamed_sprint, :name_of_first_renamed_sprint
12
+
13
+ def initialize(original_name_of_first_renamed_sprint, name_of_first_renamed_sprint)
14
+ @original_name_of_first_renamed_sprint = Sprint::Name.parse(original_name_of_first_renamed_sprint)
15
+ @name_of_first_renamed_sprint = Sprint::Name.parse(name_of_first_renamed_sprint)
16
+ end
17
+
18
+ def name_for(_sprint_name)
19
+ next_name_in_planning_interval
20
+ end
21
+
22
+ def new_name_of_sprint_next_to_first_renamed_sprint
23
+ @new_name_of_sprint_next_to_first_renamed_sprint ||=
24
+ name_of_first_renamed_sprint.next_in_planning_interval
25
+ end
26
+
27
+ def next_name_in_planning_interval
28
+ @next_name_in_planning_interval ||= new_name_of_sprint_next_to_first_renamed_sprint
29
+
30
+ next_name = @next_name_in_planning_interval.to_s
31
+
32
+ @next_name_in_planning_interval = @next_name_in_planning_interval.next_in_planning_interval
33
+
34
+ next_name
35
+ end
36
+
37
+ def pulling_sprint_into_previous_planning_interval?
38
+ (name_of_first_renamed_sprint.planning_interval <=>
39
+ original_name_of_first_renamed_sprint.planning_interval)
40
+ .negative?
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "prefix_sprint_updater"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Performer
9
+ class SprintRenamer < PrefixSprintUpdater
10
+ require_relative "sprint_renamer/keep_same_name_generator"
11
+ require_relative "sprint_renamer/next_name_generator"
12
+
13
+ attr_reader :from_string_regex, :to_string
14
+
15
+ def initialize(tool, from_string, to_string)
16
+ super(tool)
17
+
18
+ @from_string_regex = Regexp.new(Regexp.escape(from_string))
19
+ @to_string = to_string
20
+ end
21
+
22
+ def act_on_sprints_for_sprint_prefix(sprint_prefix)
23
+ prefix_sprints = sprint_prefix.sprints
24
+
25
+ new_sprint_names = calculate_sprint_new_names(prefix_sprints.collect(&:name))
26
+
27
+ prefix_sprints.zip(new_sprint_names).each do |sprint, new_sprint_name|
28
+ sprint.rename_to(new_sprint_name)
29
+ end
30
+ end
31
+
32
+ def calculate_sprint_new_names(sprint_names)
33
+ name_generator = Performer::SprintRenamer::KeepSameNameGenerator.new
34
+
35
+ sprint_names.collect do |sprint_name|
36
+ if first_sprint_to_act_on?(sprint_name)
37
+ sprint_new_name = sprint_name.sub(from_string_regex, to_string)
38
+
39
+ name_generator = next_sprint_name_generator_class.new(sprint_name, sprint_new_name)
40
+
41
+ sprint_new_name
42
+ else
43
+ name_generator.name_for(sprint_name)
44
+ end
45
+ end
46
+ end
47
+
48
+ def next_sprint_name_generator_class
49
+ NextNameGenerator
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Performer
9
+ class SprintTimeInDatesAligner
10
+ attr_reader :tool, :sprint_time_in_dates
11
+
12
+ def initialize(tool, sprint_time_in_dates)
13
+ @tool = tool
14
+ @sprint_time_in_dates =
15
+ if sprint_time_in_dates.is_a?(String)
16
+ Time.parse(sprint_time_in_dates)
17
+ else
18
+ sprint_time_in_dates
19
+ end
20
+ end
21
+
22
+ def run
23
+ update_sprint_dates_with_expected_time
24
+ end
25
+
26
+ def update_sprint_dates_with_expected_time
27
+ tool.unclosed_sprints.each { |sprint| update_sprint_dates_for(sprint) }
28
+ end
29
+
30
+ def update_sprint_dates_for(sprint)
31
+ return if sprint.closed? || !(sprint.start_date? || sprint.end_date?)
32
+
33
+ sprint.start_date = date_with_expected_time(sprint.start_date) if sprint.start_date?
34
+ sprint.end_date = date_with_expected_time(sprint.end_date) if sprint.end_date?
35
+
36
+ sprint.save
37
+ end
38
+
39
+ private
40
+
41
+ def date_with_expected_time(original_date)
42
+ original_date = Time.parse(original_date) if original_date.is_a?(String)
43
+
44
+ original_date.change(
45
+ hour: sprint_time_in_dates.hour, min: sprint_time_in_dates.min, sec: sprint_time_in_dates.sec
46
+ )
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/project"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Project
9
+ class Options
10
+ def self.add(tool, parser)
11
+ parser.section_header "Project"
12
+
13
+ parser.on("--project-field-list",
14
+ "Display the fields pertaining to the ticket types of a project") do
15
+ tool.project.list_ticket_fields
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jira
4
+ module Auto
5
+ class Tool
6
+ class Project
7
+ class TicketFields
8
+ attr_reader :tool, :project
9
+
10
+ def initialize(tool, project)
11
+ @tool = tool
12
+ @project = project
13
+ end
14
+
15
+ def list
16
+ table = Terminal::Table.new(
17
+ title: "Project #{project.key} Ticket Fields",
18
+ headings: table_row_header,
19
+ rows: table_rows
20
+ )
21
+
22
+ puts table
23
+ end
24
+
25
+ def table_row_header
26
+ ["Ticket Type", "Field Key", "Field Name", "Field Type", "Allowed Values"]
27
+ end
28
+
29
+ def table_rows
30
+ rows = []
31
+
32
+ each_issue_type_field do |issue_type_name, field|
33
+ rows << [issue_type_name, field["key"], field["name"], field["schema"]["type"], allowed_values(field)]
34
+ end
35
+
36
+ rows.sort
37
+ end
38
+
39
+ def each_issue_type_field
40
+ tool.jira_client.Createmeta.all({ projectKeys: project.key, "expand" => "projects.issuetypes.fields" })
41
+ .each do |createmeta|
42
+ createmeta.attrs["issuetypes"].each do |issue_type|
43
+ issue_type_name = issue_type["name"]
44
+ issue_type["fields"].each_value do |field|
45
+ yield(issue_type_name, field)
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def allowed_values(field)
54
+ field_allowed_values = field["allowedValues"]
55
+
56
+ if field_allowed_values
57
+ field_allowed_values.collect do |allowed_value|
58
+ value = allowed_value["value"] || allowed_value["name"]
59
+
60
+ "#{value} (#{allowed_value["id"]})"
61
+ end
62
+ else
63
+ "n/a"
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "terminal-table"
4
+ require "jira/auto/tool/field_controller"
5
+ require "jira/auto/tool/project/ticket_fields"
6
+
7
+ module Jira
8
+ module Auto
9
+ class Tool
10
+ class Project
11
+ def self.find(tool, project_key)
12
+ new(tool, tool.jira_client.Project.find(project_key))
13
+ end
14
+
15
+ def initialize(tool, jira_project)
16
+ @tool = tool
17
+ @jira_project = jira_project
18
+ end
19
+
20
+ def key
21
+ jira_project.key
22
+ end
23
+
24
+ def list_ticket_fields
25
+ ticket_fields.list
26
+ end
27
+
28
+ def ticket_fields
29
+ TicketFields.new(tool, self)
30
+
31
+ # FieldController.new(jira_client).ticket_fields
32
+ end
33
+
34
+ private
35
+
36
+ attr_reader :tool, :jira_project
37
+ end
38
+ end
39
+ end
40
+ end