apadmi_grout 1.2.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -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 +58 -9
  11. data/lib/apadmi/grout/{jira/models/find_tickets_options.rb → models/ado_config.rb} +2 -2
  12. data/lib/apadmi/grout/models/bitrise.rb +38 -0
  13. data/lib/apadmi/grout/models/find_tickets_options.rb +44 -0
  14. data/lib/apadmi/grout/{jira/models → models}/flag_messages.rb +0 -0
  15. data/lib/apadmi/grout/models/issue.rb +49 -0
  16. data/lib/apadmi/grout/{jira/models → models}/pull_request.rb +0 -0
  17. data/lib/apadmi/grout/models/release_notes_config.rb +47 -0
  18. data/lib/apadmi/grout/{release_notes/models → models}/release_notes_templates.rb +8 -8
  19. data/lib/apadmi/grout/service/bitrise_service/bitrise_service.rb +103 -0
  20. data/lib/apadmi/grout/service/board_service/ado_board_service.rb +199 -0
  21. data/lib/apadmi/grout/service/board_service/board_service.rb +59 -0
  22. data/lib/apadmi/grout/{jira/wrapper/jira_wrapper.rb → service/board_service/jira_board_service.rb} +65 -107
  23. data/lib/apadmi/grout/utils/git_utils.rb +32 -6
  24. data/lib/apadmi/grout/utils/network_service.rb +123 -0
  25. data/lib/apadmi/grout/version.rb +1 -1
  26. data/lib/apadmi_grout.rb +3 -21
  27. metadata +24 -15
  28. data/lib/apadmi/grout/jira/actions/move_jira_tickets_action.rb +0 -58
  29. data/lib/apadmi/grout/jira/models/version.rb +0 -23
  30. data/lib/apadmi/grout/release_notes/actions/generate_release_notes_action.rb +0 -39
  31. 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: 01621f3c2ed3d2ca284e8f7778bdff16ed635a8bb25a2bae11c79962ce1d9d00
4
+ data.tar.gz: e480a79994bff440381d288826315960900909d2b7d94b3ec4e36415d13420b2
5
5
  SHA512:
6
- metadata.gz: 2c9d6d5fce5da1ffaa1ab71a7f54e4bba85a3f4fda470b0613049a2169ca9466d180b655c3129561e9046fe3d0a4041b85c2cf25d70dfd8ac7f521b70a6d5113
7
- data.tar.gz: 13b59090e27577e87f7a52ef9dbcc085db1af1b0575a20e98642669155b0652e0e91976a4943f027e33c25d325fe0aa205afa481d761b7a944f5aebf3bf4138a
6
+ metadata.gz: d6e74bc59d0434dea89aea4f35b593d3b59f85bca8bbb1f003f53b287ba0c4ebdf02aa246c66da327579c4eb174ee7f1df504022ad034b121b1b2ee7e878f82f
7
+ data.tar.gz: 8d173b1f108f290be9d554c95201c469d34d20989d8826d4a9020e31ff645d85e9075e374300a4d42f93e22cc000ab4bbdadd713623787975869fae9cf318dea
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Core Changelog
2
2
 
3
+ ## [2.2.0] - 2022-08-16
4
+ * Bumped min ruby version to 3.0.0 to prevent subtle bugs
5
+ * Removed references to JIRA in default release note templates
6
+ * Fixed issues with git warnings being treated like errors
7
+
8
+ ## [2.1.0] - 2022-07-26
9
+ * Added a few bitrise utils to support listing builds, triggering builds and downloading artifacts.
10
+ * Added utilities to the network service to upload files
11
+
12
+ ## [2.0.0] - 2022-05-25
13
+ * Almost total refactor to support having different board providers (other than jira)
14
+ * Implement ADO support for all existing actions
15
+
3
16
  ## [1.2.0] - 2022-05-17
4
17
  * Filter out issues missing from git changelogs when finding tickets to move.
5
18
 
@@ -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,72 @@
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.init_for_basic_auth(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)
17
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
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.init_for_basic_auth("", 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
+ )
67
+ end
68
+
69
+ def self.bitrise_service(api_token, base_url, app_slug)
70
+ network_service = Apadmi::Grout::NetworkService.new(api_token, base_url)
71
+ Apadmi::Grout::BitriseService.new(app_slug, network_service)
23
72
  end
24
73
  end
25
74
  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,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ STATUS_RUNNING = 0
6
+ STATUS_SUCCESS = 1
7
+ STATUS_FAILED = 2
8
+ STATUS_ABORTED_FAIL = 3
9
+ STATUS_ABORTED_SUCCESS = 4
10
+
11
+ BitriseBuild = Struct.new(:triggered_at, :slug, :status, :commit_hash) do
12
+ def finished_with_success
13
+ STATUS_SUCCESS == status
14
+ end
15
+ end
16
+
17
+ Artifact = Struct.new(:title, :download_url)
18
+
19
+ TriggeredBitriseBuild = Struct.new(:build_number, :build_slug, :build_url, :message, :service, :slug, :status, :triggered_workflow) do
20
+ def finished_with_success
21
+ STATUS_SUCCESS == status
22
+ end
23
+
24
+ def self.from_json(json)
25
+ TriggeredBitriseBuild.new(
26
+ json["build_number"],
27
+ json["build_slug"],
28
+ json["build_url"],
29
+ json["message"],
30
+ json["service"],
31
+ json["slug"],
32
+ json["status"],
33
+ json["triggered_workflow"]
34
+ )
35
+ end
36
+ end
37
+ end
38
+ 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