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,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/helpers/option_parser"
4
+ require "jira/auto/tool/board_controller"
5
+ require "jira/auto/tool/board/cache"
6
+
7
+ module Jira
8
+ module Auto
9
+ class Tool
10
+ class BoardController
11
+ class Options
12
+ def self.add(tool, parser)
13
+ parser.section_header "Board"
14
+
15
+ parser.on("--board-name=STRING", String) do |board_name|
16
+ tool.jira_board_name = board_name
17
+ end
18
+
19
+ parser.on("--board-cache-clear", "Clear the board cache so they are requested again.") do
20
+ tool.board_controller.clear_cache
21
+ end
22
+
23
+ parser.on("--board-list", "List boards. The output can be controlled via the " \
24
+ "#{Tool::Environment::JIRA_BOARD_NAME_REGEX} environment variable") do
25
+ tool.board_controller.list_boards
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/board"
4
+ require "jira/auto/tool/board/cache"
5
+
6
+ module Jira
7
+ module Auto
8
+ class Tool
9
+ class BoardController
10
+ attr_reader :jira_client, :tool
11
+
12
+ def initialize(tool)
13
+ @tool = tool
14
+ @jira_client = tool.jira_client
15
+ end
16
+
17
+ def list_boards
18
+ table = Terminal::Table.new(
19
+ title: "Boards",
20
+ headings: ["Project Key", "Name", "Board UI URL"],
21
+ rows: boards.collect { |board| [board.project_key, board.name, board.ui_url] }
22
+ )
23
+
24
+ puts table
25
+ end
26
+
27
+ def boards
28
+ return cached_boards if valid_cache?
29
+
30
+ cache_boards(filtered_boards)
31
+ end
32
+
33
+ def clear_cache
34
+ cache.clear
35
+ end
36
+
37
+ private
38
+
39
+ def filtered_boards
40
+ named_filtered_boards = apply_board_name_filter(unfiltered_boards)
41
+
42
+ apply_project_key_filter(named_filtered_boards)
43
+ end
44
+
45
+ def unfiltered_boards
46
+ @unfiltered_boards ||= request_boards
47
+ end
48
+
49
+ def valid_cache?
50
+ cache.valid?
51
+ end
52
+
53
+ def cached_boards
54
+ cache.boards
55
+ end
56
+
57
+ def cache
58
+ @cache ||= Board::Cache.new(tool)
59
+ end
60
+
61
+ def request_boards
62
+ jira_client.Board.all.collect { |board| Board.new(tool, board) }
63
+ end
64
+
65
+ def cache_boards(boards)
66
+ cache.save(boards)
67
+ end
68
+
69
+ def apply_project_key_filter(boards_to_filter)
70
+ return boards_to_filter unless tool.jira_project_key_defined?
71
+
72
+ boards_to_filter.find_all do |board|
73
+ !board.with_project_information? ||
74
+ board.project_key == tool.jira_project_key
75
+ end
76
+ end
77
+
78
+ def apply_board_name_filter(boards_to_filter)
79
+ return boards_to_filter unless tool.jira_board_name_regex_defined?
80
+
81
+ board_name_regex = Regexp.new(tool.jira_board_name_regex)
82
+
83
+ boards_to_filter.find_all { |board| board.name =~ board_name_regex }
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/helpers/option_parser"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ module CommonOptions
9
+ DISPLAY_HELP_OPTION = "--help"
10
+
11
+ def self.add(tool, parser)
12
+ ::ARGV << DISPLAY_HELP_OPTION if ARGV.empty?
13
+
14
+ add_help_banner_and_options(parser)
15
+
16
+ parser.on("-v", "--version", "Print the version") do
17
+ Kernel.puts tool.class::VERSION
18
+
19
+ Kernel.exit 1
20
+ end
21
+ end
22
+
23
+ def self.add_help_banner_and_options(parser)
24
+ parser.banner = <<~EOBANNER
25
+ Usage: #{File.basename($PROGRAM_NAME)} [options]*
26
+
27
+ EOBANNER
28
+
29
+ parser.on("-h", DISPLAY_HELP_OPTION, "Print this help") do
30
+ Kernel.puts parser
31
+ Kernel.exit 1
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jira
4
+ module Auto
5
+ class Tool
6
+ class Config
7
+ class Options
8
+ def self.add(_tool, parser)
9
+ # parser.section_header "Config"
10
+ #
11
+ # parser.on("--config-list") do
12
+ # # TODO: tool.config.list
13
+ # end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class Config
9
+ include Comparable
10
+
11
+ attr_reader :tool
12
+
13
+ def initialize(tool)
14
+ @tool = tool
15
+ end
16
+
17
+ def []=(key, value)
18
+ value_store[sanitize_key(key)] = value
19
+ save
20
+ end
21
+
22
+ def [](key)
23
+ value_store[sanitize_key(key)]
24
+ end
25
+
26
+ def key?(key)
27
+ value_store.key?(sanitize_key(key))
28
+ end
29
+
30
+ def <=>(other)
31
+ value_store <=> (other.is_a?(Config) ? other.value_store : other)
32
+ end
33
+
34
+ def path
35
+ File.join(dir, "jira-auto-tool.config.yml")
36
+ end
37
+
38
+ def dir
39
+ config_dir = File.join(Dir.home, ".config/jira-auto-tool")
40
+ FileUtils.makedirs(config_dir)
41
+ config_dir
42
+ end
43
+
44
+ private
45
+
46
+ def sanitize_key(key)
47
+ key.to_s
48
+ end
49
+
50
+ def save
51
+ File.write(path, value_store.to_yaml)
52
+ end
53
+
54
+ def value_store
55
+ @value_store ||= load
56
+ end
57
+
58
+ def load
59
+ File.exist?(path) ? YAML.safe_load_file(path) : {}
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # require "jira-ruby"
5
+ # require "json"
6
+ #
7
+ # # Jira client setup
8
+ # options = {
9
+ # username: "your_username", # Replace with Jira username
10
+ # password: "your_api_token", # Replace with your API token for authentication
11
+ # site: "https://your-domain.atlassian.net", # Replace with your Jira instance URL
12
+ # context_path: "",
13
+ # auth_type: :basic
14
+ # }
15
+ #
16
+ # client = JIRA::Client.new(options)
17
+ #
18
+ # # Get issue metadata for project and custom field
19
+ # def fetch_custom_field_options(client, project_key, custom_field_id)
20
+ # # Fetch issue metadata for the project
21
+ # response = client.get("/rest/api/3/issue/createmeta?projectKeys=#{project_key}&expand=projects.issuetypes.fields")
22
+ # metadata = JSON.parse(response.body)
23
+ #
24
+ # # Parse metadata for the specified custom field
25
+ # options = []
26
+ # metadata["projects"].each do |project|
27
+ # project["issuetypes"].each do |issuetype|
28
+ # fields = issuetype["fields"] || {}
29
+ # if fields[custom_field_id] && fields[custom_field_id]["allowedValues"]
30
+ # options = fields[custom_field_id]["allowedValues"].map { |option| option["value"] }
31
+ # end
32
+ # end
33
+ # end
34
+ #
35
+ # # Return unique options
36
+ # options.uniq
37
+ # rescue JIRA::HTTPError => e
38
+ # puts "Failed to fetch field metadata: #{e.response.body}"
39
+ # nil
40
+ # end
41
+ #
42
+ # # Replace with your Jira project key and the custom field ID
43
+ # project_key = "PROJ"
44
+ # custom_field_id = "customfield_10021" # Replace with your custom field ID
45
+ # options = fetch_custom_field_options(client, project_key, custom_field_id)
46
+ #
47
+ # puts "Possible values: #{options}"
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool/request_builder/field_context_fetcher"
4
+ require "jira/auto/tool/request_builder/field_option_fetcher"
5
+
6
+ module Jira
7
+ module Auto
8
+ class Tool
9
+ class Field
10
+ include Comparable
11
+
12
+ attr_reader :jira_client, :jira_field
13
+
14
+ def initialize(jira_client, jira_field)
15
+ @jira_client = jira_client
16
+ @jira_field = jira_field
17
+ end
18
+
19
+ def name
20
+ jira_field.name
21
+ end
22
+
23
+ def type
24
+ jira_field.schema.fetch("type")
25
+ end
26
+
27
+ def id
28
+ jira_field.id
29
+ end
30
+
31
+ def <=>(other)
32
+ comparison_values(self) <=> comparison_values(other)
33
+ end
34
+
35
+ def values
36
+ RequestBuilder::FieldOptionFetcher.fetch_field_options(self)
37
+ end
38
+
39
+ def field_context
40
+ contexts = field_contexts
41
+
42
+ log.warn { "field #{self} has several field contexts" } unless contexts.size == 1
43
+
44
+ field_contexts.first # TODO: - handle several field contexts
45
+ end
46
+
47
+ def field_contexts
48
+ RequestBuilder::FieldContextFetcher.fetch_field_contexts(self)
49
+ end
50
+
51
+ private
52
+
53
+ def comparison_values(object)
54
+ %i[name type id].collect { |field| object.send(field) }
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool"
4
+ require_relative "field"
5
+
6
+ module Jira
7
+ module Auto
8
+ class Tool
9
+ class FieldController
10
+ class FieldNotFoundError < StandardError; end
11
+ class ExpectedFieldTypeError < StandardError; end
12
+
13
+ def initialize(jira_client)
14
+ @jira_client = jira_client
15
+ end
16
+
17
+ def sprint_field(field_name)
18
+ field_fetcher(field_name, "array")
19
+ end
20
+
21
+ def expected_start_date_field(field_name)
22
+ field_fetcher(field_name, "date")
23
+ end
24
+
25
+ def implementation_team_field(field_name)
26
+ field_fetcher(field_name, "option|any")
27
+ end
28
+
29
+ def ticket_fields
30
+ @ticket_fields ||= @jira_client.Field.all.collect { |field| Field.new(@jira_client, field) }
31
+ rescue StandardError => e
32
+ raise "Error fetching project ticket fields: Something went wrong:\n#{e}"
33
+ end
34
+
35
+ private
36
+
37
+ def field_fetcher(field_name, expected_type)
38
+ field = ticket_fields.find { |f| f.name == field_name } or
39
+ raise FieldNotFoundError, "Field '#{field_name}' not found!"
40
+
41
+ field.type =~ Regexp.new(expected_type) or
42
+ raise ExpectedFieldTypeError,
43
+ "Field '#{field_name}' expected to have type '#{expected_type}', but was '#{field.type}'."
44
+
45
+ field
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ class FieldOption
9
+ include Comparable
10
+
11
+ attr_reader :jira_client, :id, :value
12
+
13
+ def initialize(jira_client, id, value)
14
+ @jira_client = jira_client
15
+ @id = id
16
+ @value = value
17
+ end
18
+
19
+ def to_s
20
+ "FieldOption(id: #{id}, value: '#{value}')"
21
+ end
22
+
23
+ def <=>(other)
24
+ comparison_values(self) <=> comparison_values(other)
25
+ end
26
+
27
+ private
28
+
29
+ def comparison_values(object)
30
+ %i[id value].collect { |field| object.send(field) }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # def get_createmeta_for_project(client, project_key)
5
+ # response = client.get("/rest/api/3/issue/createmeta?projectKeys=#{project_key}")
6
+ # metadata = JSON.parse(response.body)
7
+ #
8
+ # # Parse issue types and fields for the project
9
+ # metadata["projects"].each do |project|
10
+ # project["issuetypes"].each do |issuetype|
11
+ # puts "Issue Type: #{issuetype["name"]}"
12
+ #
13
+ # issuetype["fields"].each do |field_id, field_details|
14
+ # puts "Field: #{field_details["name"]}, ID: #{field_id}"
15
+ # end
16
+ # end
17
+ # end
18
+ # rescue JIRA::HTTPError => e
19
+ # puts "Failed to retrieve create metadata: #{e.response.body}"
20
+ # end
21
+ #
22
+ # # Replace with your project key
23
+ # project_key = "PROJ"
24
+ # get_createmeta_for_project(client, project_key)
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jira
4
+ module Auto
5
+ class Tool
6
+ module Helpers
7
+ module EnvironmentBasedValue
8
+ # TODO: overly complex - simplify by moving this to the Config and define a Configurable module
9
+ def define_overridable_environment_based_value(method_name)
10
+ define_reader(method_name)
11
+ define_predicate(method_name)
12
+ define_reader_accepting_default_value(method_name)
13
+ define_writer(method_name)
14
+ define_environment_variable_name_constant(method_name)
15
+ end
16
+
17
+ def define_reader(method_name)
18
+ define_method(method_name) do
19
+ self.class.instance_variable_name(method_name)
20
+
21
+ if config.key?(method_name)
22
+ config[method_name]
23
+ else
24
+ self.class.fetch_corresponding_environment_variable(method_name.to_s)
25
+ end
26
+ end
27
+ end
28
+
29
+ def define_predicate(method_name)
30
+ define_method(:"#{method_name}_defined?") do
31
+ self.class.corresponding_environment_variable_defined?(method_name.to_s)
32
+ end
33
+ end
34
+
35
+ def define_reader_accepting_default_value(method_name)
36
+ define_method(:"#{method_name}_when_defined_else") do |value|
37
+ if config.key?(method_name)
38
+ config[method_name]
39
+ elsif self.class.corresponding_environment_variable_defined?(method_name.to_s)
40
+ send(method_name)
41
+ else
42
+ value
43
+ end
44
+ end
45
+ end
46
+
47
+ def define_writer(method_name)
48
+ define_method(:"#{method_name}=") do |value|
49
+ log.debug { "#{method_name}= #{value.inspect}" }
50
+ config[method_name] = value
51
+ end
52
+ end
53
+
54
+ def define_environment_variable_name_constant(method_name)
55
+ constant_name = method_name.to_s.upcase
56
+ class_eval <<-EOCONSTANT, __FILE__, __LINE__ + 1
57
+ # This module and constant will be interpolated as follows:
58
+ # module Environment
59
+ # CONSTANT_NAME = "CONSTANT_NAME"
60
+ # end
61
+ module Environment
62
+ #{constant_name} = #{constant_name.inspect}
63
+ end
64
+ EOCONSTANT
65
+ end
66
+
67
+ def fetch_corresponding_environment_variable(
68
+ caller_method_name = caller_locations(1, 1).first.base_label
69
+ )
70
+ environment_variable_name = caller_method_name.upcase
71
+
72
+ value = ENV.fetch(environment_variable_name) do |name|
73
+ raise KeyError, "Missing #{name} environment variable!"
74
+ end
75
+
76
+ log.debug { "fetch_corresponding_environment_variable(#{environment_variable_name}) - #{value.inspect}" }
77
+
78
+ value
79
+ end
80
+
81
+ def corresponding_environment_variable_defined?(caller_method_name = caller_locations(1, 1).first.base_label)
82
+ environment_variable_name = caller_method_name.upcase
83
+
84
+ log.debug { "corresponding_environment_variable_defined?(#{environment_variable_name})" }
85
+
86
+ ENV.key?(environment_variable_name)
87
+ end
88
+
89
+ def instance_variable_name(method_name)
90
+ "@#{method_name}"
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "optparse"
4
+ require "jira/auto/tool"
5
+
6
+ class OptionParser
7
+ def section_header(section_name)
8
+ header_name = "#{section_name}:"
9
+
10
+ on <<~EOSH
11
+
12
+ #{header_name}
13
+ #{header_name.gsub(/./, "-")}
14
+ EOSH
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jira
4
+ module Auto
5
+ class Tool
6
+ module Helpers
7
+ class OverridableTime
8
+ def self.now
9
+ overridden_date_time = ENV.fetch("JAT_CURRENT_DATE_TIME", nil)
10
+ log.debug { "JAT_CURRENT_DATE_TIME is set to #{overridden_date_time}" }
11
+
12
+ (overridden_date_time ? Time.parse(overridden_date_time) : Time.now)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jira
4
+ module Auto
5
+ class Tool
6
+ module Helpers
7
+ module Pagination
8
+ PAGE_SIZE = 50
9
+
10
+ def self.fetch_all_object_pages(parameter_naming_convention = :camelCase)
11
+ all_objects = []
12
+ start_at = 0
13
+
14
+ loop do
15
+ pagination_options = build_pagination_options(parameter_naming_convention, PAGE_SIZE, start_at)
16
+
17
+ log.debug { "Fetching objects from Jira (#{pagination_options.inspect})..." }
18
+
19
+ fetched_objects = yield(pagination_options)
20
+
21
+ log.debug { "Fetched #{fetched_objects.size} object." }
22
+
23
+ all_objects.concat(fetched_objects)
24
+
25
+ start_at += PAGE_SIZE
26
+
27
+ break if fetched_objects.empty?
28
+ end
29
+
30
+ log.debug { all_objects.collect(&:name).join(", ") }
31
+
32
+ all_objects
33
+ end
34
+
35
+ def self.build_pagination_options(parameter_naming_convention, max_results, start_at)
36
+ PAGINATION_PARAMETER_STYLES
37
+ .fetch(parameter_naming_convention) { |k| "#{k.inspect}: not found in #{PAGINATION_PARAMETER_STYLES}" }
38
+ .zip([max_results, start_at])
39
+ .to_h
40
+ end
41
+
42
+ PAGINATION_PARAMETER_STYLES = {
43
+ camelCase: %i[maxResults startAt],
44
+ snake_case: %i[max_results start_at]
45
+ }.freeze
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jira/auto/tool"
4
+
5
+ module Jira
6
+ module Auto
7
+ class Tool
8
+ module JiraHttpOptions
9
+ DISPLAY_HELP_OPTION = "--help"
10
+
11
+ def self.add(tool, parser)
12
+ parser.section_header "JIRA HTTP"
13
+ parser.on("--[no-]jira-http-debug", "Enable or disable HTTP debug mode") do |v|
14
+ tool.jira_http_debug = v
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end