apadmi_grout 1.2.0 → 2.0.0

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_action.rb +24 -0
  4. data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_ado_action.rb +91 -0
  5. data/lib/apadmi/grout/{jira/actions/find_tickets_to_move_action.rb → actions/find_tickets_to_move_action/find_tickets_to_move_jira_action.rb} +16 -17
  6. data/lib/apadmi/grout/actions/generate_release_notes_action/generate_release_notes_action.rb +48 -0
  7. data/lib/apadmi/grout/actions/generate_release_notes_action/issue_classifier.rb +51 -0
  8. data/lib/apadmi/grout/{release_notes/actions → actions}/issues_from_changelog_action.rb +11 -6
  9. data/lib/apadmi/grout/actions/move_tickets_action.rb +34 -0
  10. data/lib/apadmi/grout/di.rb +53 -9
  11. data/lib/apadmi/grout/{jira/models/find_tickets_options.rb → models/ado_config.rb} +2 -2
  12. data/lib/apadmi/grout/models/find_tickets_options.rb +44 -0
  13. data/lib/apadmi/grout/{jira/models → models}/flag_messages.rb +0 -0
  14. data/lib/apadmi/grout/models/issue.rb +49 -0
  15. data/lib/apadmi/grout/{jira/models → models}/pull_request.rb +0 -0
  16. data/lib/apadmi/grout/models/release_notes_config.rb +47 -0
  17. data/lib/apadmi/grout/{release_notes/models → models}/release_notes_templates.rb +6 -6
  18. data/lib/apadmi/grout/service/board_service/ado_board_service.rb +199 -0
  19. data/lib/apadmi/grout/service/board_service/board_service.rb +59 -0
  20. data/lib/apadmi/grout/{jira/wrapper/jira_wrapper.rb → service/board_service/jira_board_service.rb} +65 -107
  21. data/lib/apadmi/grout/utils/git_utils.rb +18 -0
  22. data/lib/apadmi/grout/utils/network_service.rb +88 -0
  23. data/lib/apadmi/grout/version.rb +1 -1
  24. data/lib/apadmi_grout.rb +3 -21
  25. metadata +20 -13
  26. data/lib/apadmi/grout/jira/actions/move_jira_tickets_action.rb +0 -58
  27. data/lib/apadmi/grout/jira/models/version.rb +0 -23
  28. data/lib/apadmi/grout/release_notes/actions/generate_release_notes_action.rb +0 -39
  29. data/lib/apadmi/grout/release_notes/models/release_notes_config.rb +0 -74
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9a8f0b024bb98a0372b85755e14bc753370328418273d6e0d07aba74f664bb9a
4
- data.tar.gz: '04286d3e985378d78f1dec57c220f1a4a5ec35d90ccf4a407d3dde66f75f9a6d'
3
+ metadata.gz: 8da866b98ba10eeb0dd9a8a664bd926e8c657cb918bf8a73474d1e3aacd941c4
4
+ data.tar.gz: '0759b30f1bcbe228e7c1a22eecc52bad02f8174d31584d130936a9d3203d35ef'
5
5
  SHA512:
6
- metadata.gz: 2c9d6d5fce5da1ffaa1ab71a7f54e4bba85a3f4fda470b0613049a2169ca9466d180b655c3129561e9046fe3d0a4041b85c2cf25d70dfd8ac7f521b70a6d5113
7
- data.tar.gz: 13b59090e27577e87f7a52ef9dbcc085db1af1b0575a20e98642669155b0652e0e91976a4943f027e33c25d325fe0aa205afa481d761b7a944f5aebf3bf4138a
6
+ metadata.gz: fd158cbb2bda541b4fbb80137200c60793d86d1257705aec7ffe5450f1fd66a6d8dc13f6e1135ca5c2db28a8049a92db4e1d76b678b0aa745314cdd7bdd92fed
7
+ data.tar.gz: f0a7a913306577749fcba0a68ce1311e073ce4f21ac0e15d50f87527f7a65a8c27e0f3136e871d7d0b5ab457da99369bc9c99a91007f361f4cb102c435e3751f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Core Changelog
2
2
 
3
+ ## [2.0.0] - 2022-05-25
4
+ * Almost total refactor to support having different board providers (other than jira)
5
+ * Implement ADO support for all existing actions
6
+
3
7
  ## [1.2.0] - 2022-05-17
4
8
  * Filter out issues missing from git changelogs when finding tickets to move.
5
9
 
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ # Generic action for finding tickets to move
6
+ class FindTicketsToMoveAction
7
+ # @param _component [String] Only include tickets tagged with this component
8
+ # @param _status [String] The status of tickets to be moved (Usually "Awaiting QA Release")
9
+ # @param _excluded_ticket_keys [Array<String>] ticket keys to be excluded from consideration
10
+ # @param _custom_flag_messages [Apadmi::Grout::FlagMessages]
11
+ # @param _options [Apadmi::Grout::JiraFindTicketsOptions|Apadmi::Grout::AdoFindTicketsOptions]
12
+ # @return [Array<Apadmi::Grout::Issue>] the issues ready to move
13
+ def run(
14
+ _component,
15
+ _status,
16
+ _excluded_ticket_keys,
17
+ _custom_flag_messages = nil,
18
+ _options = nil
19
+ )
20
+ raise "Unimplemented :("
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ # ATTENTION: Any changes to the logic here should be reflected in the docs (find-tickets.md)
6
+
7
+ # Finds and returns a list of all the issues that are ready to be moved
8
+ # Any tickets found that have the given status but *don't* appear ready to be moved will
9
+ # be flagged.
10
+ class FindTicketsToMoveAdoAction < FindTicketsToMoveAction
11
+ # @param [Apadmi::Grout::AdoBoardService]
12
+ # @param [String]
13
+ # @param [Apadmi::Grout::GitUtils]
14
+ # @param [Apadmi::Grout::IssuesFromChangelogAction]
15
+ # @param [Apadmi::Grout::DefaultLogger] // or your own logger!
16
+ def initialize(ado_board_service, ticket_prefix, git_utils, issues_from_chnglg, logger)
17
+ @ado_board_service = ado_board_service
18
+ @ticket_prefix = ticket_prefix
19
+ @git_utils = git_utils
20
+ @issues_from_changelog_action = issues_from_chnglg
21
+ @logger = logger
22
+ end
23
+
24
+ # @param component [String] Only include tickets tagged with this component
25
+ # @param status [String] The status of tickets to be moved (Usually "Awaiting QA Release")
26
+ # @param excluded_ticket_keys [Array<String>] ticket keys to be excluded from consideration
27
+ # @param custom_flag_messages [Apadmi::Grout::FlagMessages]
28
+ # @param options [Apadmi::Grout::JiraFindTicketsOptions]
29
+ # @return [Array<Apadmi::Grout::Issue>] the issues ready to move
30
+ def run(
31
+ component,
32
+ status,
33
+ excluded_ticket_keys,
34
+ custom_flag_messages = nil,
35
+ options = nil
36
+ )
37
+ custom_flag_messages ||= Apadmi::Grout::FlagMessages.default(status)
38
+ options ||= Apadmi::Grout::AdoFindTicketsOptions.new(required_tags: [],
39
+ not_tags: [])
40
+
41
+ issues = @ado_board_service.search_unblocked_issues(
42
+ component, status, [], options
43
+ ).reject { |issue| excluded_ticket_keys.include? issue.key }
44
+
45
+ issue_keys = issues.map(&:key)
46
+ @logger.message("Found issues to consider #{issue_keys.join(", ")}")
47
+
48
+ # Get the issues in the git changelog, filtered for the issues being considered
49
+ changelog_ids = @issues_from_changelog_action.issue_ids_from_changelog(
50
+ @git_utils.merge_changelog(issue_keys)
51
+ ).map { |id| id.delete_prefix(@ticket_prefix) }
52
+
53
+ @git_utils.fetch_all
54
+ # issues that have been merged, but aren't in this build.
55
+ # This is a valid state (e.g. merged into a release branch but not develop)
56
+ invert_changelog_ids = @issues_from_changelog_action.issue_ids_from_changelog(
57
+ @git_utils.invert_changelog(issue_keys)
58
+ ).map { |id| id.delete_prefix(@ticket_prefix) }
59
+
60
+ final_list = issues.filter do |issue|
61
+ # Flag the ticket if it doesn't include in either changelog since
62
+ # this means the ticket is likely in an incorrect state
63
+ decide_should_include(changelog_ids, custom_flag_messages, invert_changelog_ids, issue)
64
+ end
65
+
66
+ @logger.message("Final list: #{final_list.map(&:key).join(", ")}")
67
+ final_list
68
+ end
69
+
70
+ private
71
+
72
+ def decide_should_include(changelog_ids, custom_flag_messages, invert_changelog_ids, issue)
73
+ if !changelog_ids.include?(issue.key) && !invert_changelog_ids.include?(issue.key)
74
+ @ado_board_service.flag_ticket(
75
+ issue.key,
76
+ custom_flag_messages.no_prs_flag_msg
77
+ )
78
+ @logger.message("Not including #{issue.key} since it's not in the changelog at all")
79
+ return false
80
+ end
81
+
82
+ if !changelog_ids.include?(issue.key) && invert_changelog_ids.include?(issue.key)
83
+ @logger.message("Not including #{issue.key} since it's merged but not in this build")
84
+ return false
85
+ end
86
+
87
+ true
88
+ end
89
+ end
90
+ end
91
+ end
@@ -13,13 +13,13 @@ module Apadmi
13
13
  # Finds and returns a list of all the issues that are ready to be moved
14
14
  # Any tickets found that have the given status but *don't* appear ready to be moved will
15
15
  # be flagged.
16
- class FindTicketsToMoveAction
17
- # @param [Apadmi::Grout::JiraWrapper]
16
+ class FindTicketsToMoveJiraAction < FindTicketsToMoveAction
17
+ # @param [Apadmi::Grout::JiraBoardService]
18
18
  # @param [Apadmi::Grout::GitUtils]
19
19
  # @param [Apadmi::Grout::IssuesFromChangelogAction]
20
20
  # @param [Apadmi::Grout::DefaultLogger] // or your own logger!
21
- def initialize(jira_wrapper, git_utils, issues_from_chnglg, logger)
22
- @jira_wrapper = jira_wrapper
21
+ def initialize(jira_board_service, git_utils, issues_from_chnglg, logger)
22
+ @jira_board_service = jira_board_service
23
23
  @git_utils = git_utils
24
24
  @issues_from_changelog_action = issues_from_chnglg
25
25
  @logger = logger
@@ -29,8 +29,8 @@ module Apadmi
29
29
  # @param status [String] The status of tickets to be moved (Usually "Awaiting QA Release")
30
30
  # @param excluded_ticket_keys [Array<String>] ticket keys to be excluded from consideration
31
31
  # @param custom_flag_messages [Apadmi::Grout::FlagMessages]
32
- # @param options [Apadmi::Grout::FindTicketsOptions]
33
- # @return [Array<JIRA::Resource::Issue>] the issues ready to move
32
+ # @param options [Apadmi::Grout::JiraFindTicketsOptions]
33
+ # @return [Array<Apadmi::Grout::Issue>] the issues ready to move
34
34
  def run(
35
35
  component,
36
36
  status,
@@ -39,11 +39,10 @@ module Apadmi
39
39
  options = nil
40
40
  )
41
41
  custom_flag_messages ||= Apadmi::Grout::FlagMessages.default(status)
42
- options ||= Apadmi::Grout::FindTicketsOptions.new(include_no_sprint_tickets: false)
42
+ options ||= Apadmi::Grout::JiraFindTicketsOptions.new(include_no_sprint_tickets: false)
43
43
 
44
- issues = @jira_wrapper.search_unblocked_issues(
45
- component, status, [],
46
- allow_no_sprint: options.include_no_sprint_tickets
44
+ issues = @jira_board_service.search_unblocked_issues(
45
+ component, status, [], options
47
46
  ).reject do |issue|
48
47
  excluded_ticket_keys.include? issue.key
49
48
  end
@@ -68,7 +67,7 @@ module Apadmi
68
67
 
69
68
  private
70
69
 
71
- # @param issue [JIRA::Resource::Issue]
70
+ # @param issue [Apadmi::Grout::Issue]
72
71
  # @param pr_status [Int] status of whether or not we can move
73
72
  # @param changelog_ids [Array<String>] the ticket ids pulled from git
74
73
  def decide_should_include(issue, pr_status, changelog_ids)
@@ -84,27 +83,27 @@ module Apadmi
84
83
  pr_status != PrStatus::UN_INCLUDED
85
84
  end
86
85
 
87
- # @param issue [JIRA::Resource::Issue]
86
+ # @param issue [Apadmi::Grout::Issue]
88
87
  # @param custom_flag_messages [FlagMessages]
89
88
  # @return [Int] status of whether or not we can move
90
89
  def process_prs(issue, custom_flag_messages)
91
- prs = @jira_wrapper.get_ticket_prs(issue)
90
+ prs = @jira_board_service.get_ticket_prs(issue)
92
91
 
93
92
  if prs.empty?
94
93
  @logger.message("#{issue.key} has no PRs. Flagging it: STILL MOVABLE")
95
- @jira_wrapper.flag_ticket(issue.key, custom_flag_messages.no_prs_flag_msg)
94
+ @jira_board_service.flag_ticket(issue.key, custom_flag_messages.no_prs_flag_msg)
96
95
  return PrStatus::UNMERGED_BUT_INCLUDED
97
96
  elsif prs.all?(&:open)
98
97
  @logger.message("#{issue.key} has only open PRs. Flagging it: NOT MOVABLE")
99
- @jira_wrapper.flag_ticket(issue.key, custom_flag_messages.open_prs_flag_msg)
98
+ @jira_board_service.flag_ticket(issue.key, custom_flag_messages.open_prs_flag_msg)
100
99
  return PrStatus::UN_INCLUDED
101
100
  elsif prs.all?(&:declined)
102
101
  @logger.message("#{issue.key} has only declined PRs. Flagging it: NOT MOVABLE")
103
- @jira_wrapper.flag_ticket(issue.key, custom_flag_messages.declined_prs_flag_msg)
102
+ @jira_board_service.flag_ticket(issue.key, custom_flag_messages.declined_prs_flag_msg)
104
103
  return PrStatus::UN_INCLUDED
105
104
  elsif prs.none?(&:merged)
106
105
  @logger.message("#{issue.key} has no merged PRs. Flagging it: NOT MOVABLE")
107
- @jira_wrapper.flag_ticket(issue.key, custom_flag_messages.no_merged_prs_flag_msg)
106
+ @jira_board_service.flag_ticket(issue.key, custom_flag_messages.no_merged_prs_flag_msg)
108
107
  return PrStatus::UN_INCLUDED
109
108
  end
110
109
 
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mustache"
4
+
5
+ module Apadmi
6
+ module Grout
7
+ # Finds and returns a list of all the issues who's ids exist in the given changelog
8
+ class GenerateReleaseNotesAction
9
+ # @param classifier [Apadmi::Grout::IssueClassifier]
10
+ def initialize(classifier)
11
+ @classifier = classifier
12
+ end
13
+
14
+ # @param config [Apadmi::Grout::ReleaseNotesConfig]
15
+ def run(config)
16
+ moved = @classifier.classify(config.moved_issues)
17
+ release = @classifier.classify(config.release_issues)
18
+
19
+ CustomMustache.render(
20
+ config.templates.document_template,
21
+ config: config,
22
+ rendered_moved_issues: render_classified_issues(config.templates.list_template, moved),
23
+ rendered_release_issues: render_classified_issues(config.templates.list_template, release)
24
+ ).strip
25
+ end
26
+
27
+ private
28
+
29
+ def render_classified_issues(template, classified_issues)
30
+ return if classified_issues.empty
31
+
32
+ CustomMustache.render(
33
+ template,
34
+ classified_issues: classified_issues
35
+ )
36
+ end
37
+ end
38
+
39
+ # Custom mustache class which doesn't escape HTML, since we're generating markdown
40
+ class CustomMustache < Mustache
41
+ # rubocop:disable Naming/MethodName
42
+ def escapeHTML(txt)
43
+ txt
44
+ end
45
+ # rubocop:enable Naming/MethodName
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ # Generic issue classifier
6
+ class IssueClassifier
7
+ # @param types [Array<String>] List of types to match on
8
+ # @param issues [Array<Apadmi::Grout::Issue>]
9
+ # @return [Array<Apadmi::Grout::Issue>]
10
+ def filter_issues_by_type(types, issues)
11
+ issues.find_all { |issue| types.include?(issue.issue_type) }
12
+ end
13
+ end
14
+
15
+ # Jira specific issue classifier
16
+ class JiraIssueClassifier < IssueClassifier
17
+ # @param issues [Array<Apadmi::Grout::Issue>]
18
+ def classify(issues)
19
+ tasks = filter_issues_by_type(%w[Task], issues)
20
+ features = filter_issues_by_type(%w[Story], issues)
21
+ improvements = filter_issues_by_type(%w[Improvement Rework Debt], issues)
22
+ defects = filter_issues_by_type(%w[Bug], issues)
23
+ ClassifiedIssues.new(
24
+ tasks,
25
+ features,
26
+ improvements,
27
+ defects,
28
+ issues - tasks - features - improvements - defects
29
+ )
30
+ end
31
+ end
32
+
33
+ # Ado specific issue classifier
34
+ class AdoIssueClassifier < IssueClassifier
35
+ # @param issues [Array<Apadmi::Grout::Issue>]
36
+ def classify(issues)
37
+ tasks = filter_issues_by_type(%w[Task], issues)
38
+ features = filter_issues_by_type(["User Story", "Feature"], issues)
39
+ improvements = filter_issues_by_type(["Improvement", "Tech Debt"], issues)
40
+ defects = filter_issues_by_type(%w[Bug], issues)
41
+ ClassifiedIssues.new(
42
+ tasks,
43
+ features,
44
+ improvements,
45
+ defects,
46
+ issues - tasks - features - improvements - defects
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end
@@ -4,26 +4,31 @@ module Apadmi
4
4
  module Grout
5
5
  # Finds and returns a list of all the issues who's ids exist in the given changelog
6
6
  class IssuesFromChangelogAction
7
- # @param [Apadmi::Grout::JiraWrapper]
7
+ # @param board_service [Apadmi::Grout::BoardService]
8
+ # @param ticket_prefix [String] The prefix for the ticket numbers in git e.g "PPT-"
8
9
  # @param [Apadmi::Grout::DefaultLogger] // or your own logger!
9
- def initialize(jira_wrapper, logger)
10
- @jira_wrapper = jira_wrapper
10
+ def initialize(board_service, ticket_prefix, logger)
11
+ @board_service = board_service
11
12
  @logger = logger
13
+ @ticket_prefix = ticket_prefix
12
14
  end
13
15
 
14
16
  # @param changelog [String] raw git changelog
15
- # @return [Array<JIRA::Resource::Issue>] list of issues from changelog
17
+ # @return [Array<Apadmi::Grout::Issue>] list of issues from changelog
16
18
  def run(changelog)
17
19
  ids = issue_ids_from_changelog(changelog)
18
20
 
19
21
  @logger.message("Found issue ids: #{ids.join(", ")}")
20
- @jira_wrapper.find_issues_by_keys(ids)
22
+ @board_service.find_issues_by_keys(ids)
21
23
  end
22
24
 
23
25
  # @param changelog [String] raw git changelog
24
26
  # @return [Array<String>] list of issue ids from changelog
25
27
  def issue_ids_from_changelog(changelog)
26
- changelog.scan(/(#{@jira_wrapper.issue_id_prefix}-\d+)/).flatten.uniq
28
+ # Changelog often has the pull request number in it, this can mess up the parsing so strip it out if we can
29
+ changelog = changelog.gsub(/\(pull request.*\)/, "")
30
+
31
+ changelog.scan(/(#{@ticket_prefix}\d+)/).flatten.uniq
27
32
  end
28
33
  end
29
34
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ # Moves all given tickets to a given status and assigns fix versions
6
+ class MoveTicketsAction
7
+ # @param [Apadmi::Grout::BoardService]
8
+ # @param [Apadmi::Grout::DefaultLogger] // or your own logger!
9
+ def initialize(board_service, logger)
10
+ @board_service = board_service
11
+ @logger = logger
12
+ end
13
+
14
+ # @param [Array<String>] version_strings
15
+ # @param [Array<Apadmi::Grout::Issue>] issues
16
+ # @param [String] new_status
17
+ def run(version_strings, issues, new_status)
18
+ if issues.empty?
19
+ @logger.error("No issues found, aborting")
20
+ return
21
+ end
22
+
23
+ @logger.message("Transitioning issues: #{issues.map(&:key).join(", ")}")
24
+
25
+ issues.each do |issue|
26
+ @board_service.transition_issue(issue, new_status)
27
+ @board_service.assign_fixversions(issue.key, version_strings)
28
+ end
29
+
30
+ @logger.success("Issues transitioned successfully :D")
31
+ end
32
+ end
33
+ end
34
+ end
@@ -3,23 +3,67 @@
3
3
  module Apadmi
4
4
  module Grout
5
5
  # Convenience class for initializing and accessing the various actions
6
- class DependencyInjector
7
- attr_reader :jira_wrapper, :move_jira_tickets_action, :find_tickets_to_move_action, :generate_release_notes_action, :issues_from_changelog_action
8
-
6
+ DependencyInjector = Struct.new(
7
+ :board_service,
8
+ :move_tickets_action,
9
+ :find_tickets_to_move_action,
10
+ :issues_from_changelog_action,
11
+ :generate_release_notes_action,
12
+ keyword_init: true
13
+ ) do
9
14
  # @param [String] username
10
15
  # @param [String] token
11
16
  # @param [String] base_url
12
17
  # @param [String] context_path
13
18
  # @param [String] project
14
19
  # @param [Logger] logger
15
- def initialize(username, token, base_url, context_path, project, logger)
16
- @jira_wrapper = Apadmi::Grout::JiraWrapper.new(username, token, base_url, context_path, project)
20
+ def self.init_for_jira(
21
+ username,
22
+ token,
23
+ base_url,
24
+ context_path,
25
+ project,
26
+ logger = Apadmi::Grout::DefaultLogger.new
27
+ )
28
+ network_service = Apadmi::Grout::NetworkService.new(username, token, base_url)
29
+ jira_board_service = Apadmi::Grout::JiraBoardService.new(username, token, base_url, context_path, project, network_service)
30
+ git_utils = Apadmi::Grout::GitUtils
31
+ issues_from_changelog_action = Apadmi::Grout::IssuesFromChangelogAction.new(jira_board_service, "#{project}-", logger)
32
+
33
+ DependencyInjector.new(
34
+ board_service: jira_board_service,
35
+ move_tickets_action: Apadmi::Grout::MoveTicketsAction.new(jira_board_service, logger),
36
+ issues_from_changelog_action: issues_from_changelog_action,
37
+ find_tickets_to_move_action: Apadmi::Grout::FindTicketsToMoveJiraAction.new(jira_board_service, git_utils, issues_from_changelog_action, logger),
38
+ generate_release_notes_action: Apadmi::Grout::GenerateReleaseNotesAction.new(Apadmi::Grout::JiraIssueClassifier.new)
39
+ )
40
+ end
17
41
 
42
+ # @param [String] personal_access_token
43
+ # @param [String] base_url
44
+ # @param [Logger] logger
45
+ def self.init_for_ado(
46
+ personal_access_token,
47
+ base_url,
48
+ logger = Apadmi::Grout::DefaultLogger.new,
49
+ ado_config = Apadmi::Grout::AdoConfig.new(
50
+ flag_tag: "FLAGGED"
51
+ )
52
+ )
53
+ ticket_prefix = "#"
54
+ network_service = Apadmi::Grout::NetworkService.new("", personal_access_token, base_url)
55
+ ado_board_service = Apadmi::Grout::AdoBoardService.new(network_service, ado_config, logger)
18
56
  git_utils = Apadmi::Grout::GitUtils
19
- @move_jira_tickets_action = Apadmi::Grout::MoveJiraTicketsAction.new(@jira_wrapper, logger)
20
- @issues_from_changelog_action = Apadmi::Grout::IssuesFromChangelogAction.new(@jira_wrapper, logger)
21
- @find_tickets_to_move_action = Apadmi::Grout::FindTicketsToMoveAction.new(@jira_wrapper, git_utils, issues_from_changelog_action, logger)
22
- @generate_release_notes_action = Apadmi::Grout::GenerateReleaseNotesAction.new(base_url)
57
+ issues_from_changelog_action = Apadmi::Grout::IssuesFromChangelogAction.new(ado_board_service, ticket_prefix, logger)
58
+
59
+ DependencyInjector.new(
60
+ board_service: ado_board_service,
61
+ move_tickets_action: Apadmi::Grout::MoveTicketsAction.new(ado_board_service, logger),
62
+ issues_from_changelog_action: issues_from_changelog_action,
63
+ find_tickets_to_move_action: Apadmi::Grout::FindTicketsToMoveAdoAction.new(ado_board_service, ticket_prefix, git_utils, issues_from_changelog_action,
64
+ logger),
65
+ generate_release_notes_action: Apadmi::Grout::GenerateReleaseNotesAction.new(Apadmi::Grout::AdoIssueClassifier.new)
66
+ )
23
67
  end
24
68
  end
25
69
  end
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Apadmi
4
4
  module Grout
5
- FindTicketsOptions = Struct.new(
6
- :include_no_sprint_tickets,
5
+ AdoConfig = Struct.new(
6
+ :flag_tag,
7
7
  keyword_init: true
8
8
  )
9
9
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ # Generic find tickets options
6
+ class FindTicketsOptions; end
7
+
8
+ # Jira specific find tickets options
9
+ class JiraFindTicketsOptions < FindTicketsOptions
10
+ attr_reader :include_no_sprint_tickets
11
+
12
+ # @param include_no_sprint_tickets [String] whether or not to include tickets with no sprint assigned
13
+ def initialize(include_no_sprint_tickets: false)
14
+ @include_no_sprint_tickets = include_no_sprint_tickets
15
+ end
16
+
17
+ def ==(other)
18
+ other.class == self.class && other.state == state
19
+ end
20
+
21
+ def state
22
+ [@include_no_sprint_tickets]
23
+ end
24
+ end
25
+
26
+ # Ado specific find tickets options
27
+ class AdoFindTicketsOptions < FindTicketsOptions
28
+ attr_reader :required_tags, :not_tags
29
+
30
+ def initialize(required_tags: [], not_tags: [])
31
+ @required_tags = required_tags
32
+ @not_tags = not_tags
33
+ end
34
+
35
+ def ==(other)
36
+ other.class == self.class && other.state == state
37
+ end
38
+
39
+ def state
40
+ [@required_tags, @not_tags]
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ # A generic issue type wrapping up the underlying JIRA & ADO object
6
+ # @param key [String] Unique key identifying the issue
7
+ # @param summary [String] The issue title
8
+ # @param status [String] The current status on the board
9
+ # @param issue_type [String] Raw string representing issue type e.g "In Progress"
10
+ # @param url [String] url to the issue
11
+ # @param raw_object [JIRA::Resource::Issue|Hash] The underlying raw object from the platform
12
+ Issue = Struct.new(:key, :summary, :status, :issue_type, :url, :raw_object) do
13
+ # @return [Apadmi::Grout::Issue]
14
+ def self.from_ado_hash(hash)
15
+ Issue.new(
16
+ hash["id"].to_s,
17
+ hash["fields"]["System.Title"],
18
+ hash["fields"]["System.State"],
19
+ hash["fields"]["System.WorkItemType"],
20
+ hash["url"],
21
+ hash
22
+ )
23
+ end
24
+
25
+ # @return [Array<Apadmi::Grout::Issue>]
26
+ def self.from_ado_hashes(hashes)
27
+ hashes.map { |h| Issue.from_ado_hash(h) }
28
+ end
29
+
30
+ # @param issue [JIRA::Resource::Issue]
31
+ # @return [Apadmi::Grout::Issue]
32
+ def self.from_jira_issue(issue, base_url)
33
+ Issue.new(
34
+ issue.key,
35
+ issue.summary,
36
+ issue.status.name,
37
+ issue.issuetype.name,
38
+ "#{base_url}/browse/#{issue.key}",
39
+ issue
40
+ )
41
+ end
42
+
43
+ # @return [Array<Apadmi::Grout::Issue>]
44
+ def self.from_jira_issues(issues, base_url)
45
+ issues.map { |i| Issue.from_jira_issue(i, base_url) }
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ # @param tasks [Array<Apadmi::Grout::Issue>]
6
+ # @param features [Array<Apadmi::Grout::Issue>] aka stories
7
+ # @param improvements [Array<Apadmi::Grout::Issue>]
8
+ # @param defects [Array<Apadmi::Grout::Issue>] aka bugs
9
+ # @param others [Array<Apadmi::Grout::Issue>] any other non-standard issue types
10
+ ClassifiedIssues = Struct.new(
11
+ :tasks,
12
+ :features,
13
+ :improvements,
14
+ :defects,
15
+ :others
16
+ ) do
17
+ # @return returns true if all categories are empty
18
+ def empty
19
+ tasks.empty? && features.empty? &&
20
+ improvements.empty? && defects.empty? && others.empty?
21
+ end
22
+ end
23
+
24
+ # @param title [String] The title of the document
25
+ # @param app_version [String] The app version pertaining to this release
26
+ # @param min_os_version [String] The min supported os version of this release
27
+ # @param date [String] Today's date
28
+ # @param moved_issues [Array<Apadmi::Grout::Issue>] Issues moved to a new state by this build job
29
+ # @param release_issues [Array<Apadmi::Grout::Issue>] Issues considered part of this release
30
+ # @param commit_hash [String] Commit hash from which release was built
31
+ # @param ci_build_number [String] CI build number which built the release
32
+ # @param ci_build_url [String] Link to CI build job
33
+ # @param templates [Apadmi::Grout::Templates] Mustache templates to use to generate the document
34
+ ReleaseNotesConfig = Struct.new(
35
+ :title,
36
+ :app_version,
37
+ :min_os_version,
38
+ :date,
39
+ :moved_issues,
40
+ :release_issues,
41
+ :commit_hash,
42
+ :ci_build_number,
43
+ :ci_build_url,
44
+ :templates
45
+ )
46
+ end
47
+ end