apadmi_grout 1.1.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_action.rb +24 -0
- data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_ado_action.rb +91 -0
- data/lib/apadmi/grout/actions/find_tickets_to_move_action/find_tickets_to_move_jira_action.rb +114 -0
- data/lib/apadmi/grout/actions/generate_release_notes_action/generate_release_notes_action.rb +48 -0
- data/lib/apadmi/grout/actions/generate_release_notes_action/issue_classifier.rb +51 -0
- data/lib/apadmi/grout/{release_notes/actions → actions}/issues_from_changelog_action.rb +11 -8
- data/lib/apadmi/grout/actions/move_tickets_action.rb +34 -0
- data/lib/apadmi/grout/di.rb +59 -9
- data/lib/apadmi/grout/{jira/models/find_tickets_options.rb → models/ado_config.rb} +2 -2
- data/lib/apadmi/grout/models/bitrise.rb +38 -0
- data/lib/apadmi/grout/models/find_tickets_options.rb +44 -0
- data/lib/apadmi/grout/{jira/models → models}/flag_messages.rb +2 -1
- data/lib/apadmi/grout/models/issue.rb +49 -0
- data/lib/apadmi/grout/{jira/models → models}/pull_request.rb +0 -0
- data/lib/apadmi/grout/models/release_notes_config.rb +47 -0
- data/lib/apadmi/grout/{release_notes/models → models}/release_notes_templates.rb +6 -6
- data/lib/apadmi/grout/service/bitrise_service/bitrise_service.rb +103 -0
- data/lib/apadmi/grout/service/board_service/ado_board_service.rb +199 -0
- data/lib/apadmi/grout/service/board_service/board_service.rb +59 -0
- data/lib/apadmi/grout/{jira/wrapper/jira_wrapper.rb → service/board_service/jira_board_service.rb} +65 -107
- data/lib/apadmi/grout/utils/git_utils.rb +36 -0
- data/lib/apadmi/grout/utils/network_service.rb +123 -0
- data/lib/apadmi/grout/version.rb +1 -1
- data/lib/apadmi_grout.rb +3 -21
- metadata +23 -14
- data/lib/apadmi/grout/jira/actions/find_tickets_to_move_action.rb +0 -80
- data/lib/apadmi/grout/jira/actions/move_jira_tickets_action.rb +0 -58
- data/lib/apadmi/grout/jira/models/version.rb +0 -23
- data/lib/apadmi/grout/release_notes/actions/generate_release_notes_action.rb +0 -39
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1a387b90108f0657af3875457b1eb98440b54d7fdf6d62a9750fe1af10aeca5
|
4
|
+
data.tar.gz: 25fe6b6ca1083a991f088d2161f49997f04944dda1225617192c9fcf2494f511
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ca2dee35cca80456ca3ebd01b8ab1e1c89eb6c9bad069b36a1e20f8ae36bb511144fed3de12d8dccc31cc8c9dd364684bbdcee4af69244ff180d6e8af4ac24d2
|
7
|
+
data.tar.gz: 49414ee4d8507110fed1bbf50946c16357039aacbc1e6657f32c1c4b4777d0ea013559da5c99fc65c77ea5a55d5d4e61479ed7b433dac81885d532dcab4eeaaa
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Core Changelog
|
2
2
|
|
3
|
+
## [2.1.0] - 22-07-26
|
4
|
+
* Added a few bitrise utils to support listing builds, triggering builds and downloading artifacts.
|
5
|
+
* Added utilities to the network service to upload files
|
6
|
+
|
7
|
+
## [2.0.0] - 2022-05-25
|
8
|
+
* Almost total refactor to support having different board providers (other than jira)
|
9
|
+
* Implement ADO support for all existing actions
|
10
|
+
|
11
|
+
## [1.2.0] - 2022-05-17
|
12
|
+
* Filter out issues missing from git changelogs when finding tickets to move.
|
13
|
+
|
3
14
|
## [1.1.0] - 2022-05-12
|
4
15
|
* Support allowing tickets that aren't assigned to a sprint to be moved.
|
5
16
|
* Created utilities for getting the git root and number of commits
|
@@ -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
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Apadmi
|
4
|
+
module Grout
|
5
|
+
module PrStatus
|
6
|
+
MERGED = 1
|
7
|
+
UNMERGED_BUT_INCLUDED = 2
|
8
|
+
UN_INCLUDED = 3
|
9
|
+
end
|
10
|
+
|
11
|
+
# ATTENTION: Any changes to the logic here should be reflected in the docs (find-tickets.md)
|
12
|
+
|
13
|
+
# Finds and returns a list of all the issues that are ready to be moved
|
14
|
+
# Any tickets found that have the given status but *don't* appear ready to be moved will
|
15
|
+
# be flagged.
|
16
|
+
class FindTicketsToMoveJiraAction < FindTicketsToMoveAction
|
17
|
+
# @param [Apadmi::Grout::JiraBoardService]
|
18
|
+
# @param [Apadmi::Grout::GitUtils]
|
19
|
+
# @param [Apadmi::Grout::IssuesFromChangelogAction]
|
20
|
+
# @param [Apadmi::Grout::DefaultLogger] // or your own logger!
|
21
|
+
def initialize(jira_board_service, git_utils, issues_from_chnglg, logger)
|
22
|
+
@jira_board_service = jira_board_service
|
23
|
+
@git_utils = git_utils
|
24
|
+
@issues_from_changelog_action = issues_from_chnglg
|
25
|
+
@logger = logger
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param component [String] Only include tickets tagged with this component
|
29
|
+
# @param status [String] The status of tickets to be moved (Usually "Awaiting QA Release")
|
30
|
+
# @param excluded_ticket_keys [Array<String>] ticket keys to be excluded from consideration
|
31
|
+
# @param custom_flag_messages [Apadmi::Grout::FlagMessages]
|
32
|
+
# @param options [Apadmi::Grout::JiraFindTicketsOptions]
|
33
|
+
# @return [Array<Apadmi::Grout::Issue>] the issues ready to move
|
34
|
+
def run(
|
35
|
+
component,
|
36
|
+
status,
|
37
|
+
excluded_ticket_keys,
|
38
|
+
custom_flag_messages = nil,
|
39
|
+
options = nil
|
40
|
+
)
|
41
|
+
custom_flag_messages ||= Apadmi::Grout::FlagMessages.default(status)
|
42
|
+
options ||= Apadmi::Grout::JiraFindTicketsOptions.new(include_no_sprint_tickets: false)
|
43
|
+
|
44
|
+
issues = @jira_board_service.search_unblocked_issues(
|
45
|
+
component, status, [], options
|
46
|
+
).reject do |issue|
|
47
|
+
excluded_ticket_keys.include? issue.key
|
48
|
+
end
|
49
|
+
|
50
|
+
issue_keys = issues.map(&:key)
|
51
|
+
@logger.message("Found issues to consider #{issue_keys.join(", ")}")
|
52
|
+
|
53
|
+
# Get the issues in the git changelog, filtered for the issues being considered
|
54
|
+
changelog_ids = @issues_from_changelog_action.issue_ids_from_changelog(
|
55
|
+
@git_utils.merge_changelog(issue_keys)
|
56
|
+
)
|
57
|
+
|
58
|
+
final_list = issues.filter do |issue|
|
59
|
+
# Decide whether to include this ticket based on PRs
|
60
|
+
status = process_prs(issue, custom_flag_messages)
|
61
|
+
decide_should_include(issue, status, changelog_ids)
|
62
|
+
end
|
63
|
+
|
64
|
+
@logger.message("Final list: #{final_list.map(&:key).join(", ")}")
|
65
|
+
final_list
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
# @param issue [Apadmi::Grout::Issue]
|
71
|
+
# @param pr_status [Int] status of whether or not we can move
|
72
|
+
# @param changelog_ids [Array<String>] the ticket ids pulled from git
|
73
|
+
def decide_should_include(issue, pr_status, changelog_ids)
|
74
|
+
# For merged PRs, check if the ticket appears in the changelog.
|
75
|
+
# If it doesn't then it's likely it was merged AFTER this release build was triggered and shouldn't be moved
|
76
|
+
# by this build
|
77
|
+
if pr_status == PrStatus::MERGED
|
78
|
+
is_in_changelog = changelog_ids.include?(issue.key)
|
79
|
+
@logger.message("NOTE: Not including #{issue.key} since it's not in the git changelog.") unless is_in_changelog
|
80
|
+
return is_in_changelog
|
81
|
+
end
|
82
|
+
|
83
|
+
pr_status != PrStatus::UN_INCLUDED
|
84
|
+
end
|
85
|
+
|
86
|
+
# @param issue [Apadmi::Grout::Issue]
|
87
|
+
# @param custom_flag_messages [FlagMessages]
|
88
|
+
# @return [Int] status of whether or not we can move
|
89
|
+
def process_prs(issue, custom_flag_messages)
|
90
|
+
prs = @jira_board_service.get_ticket_prs(issue)
|
91
|
+
|
92
|
+
if prs.empty?
|
93
|
+
@logger.message("#{issue.key} has no PRs. Flagging it: STILL MOVABLE")
|
94
|
+
@jira_board_service.flag_ticket(issue.key, custom_flag_messages.no_prs_flag_msg)
|
95
|
+
return PrStatus::UNMERGED_BUT_INCLUDED
|
96
|
+
elsif prs.all?(&:open)
|
97
|
+
@logger.message("#{issue.key} has only open PRs. Flagging it: NOT MOVABLE")
|
98
|
+
@jira_board_service.flag_ticket(issue.key, custom_flag_messages.open_prs_flag_msg)
|
99
|
+
return PrStatus::UN_INCLUDED
|
100
|
+
elsif prs.all?(&:declined)
|
101
|
+
@logger.message("#{issue.key} has only declined PRs. Flagging it: NOT MOVABLE")
|
102
|
+
@jira_board_service.flag_ticket(issue.key, custom_flag_messages.declined_prs_flag_msg)
|
103
|
+
return PrStatus::UN_INCLUDED
|
104
|
+
elsif prs.none?(&:merged)
|
105
|
+
@logger.message("#{issue.key} has no merged PRs. Flagging it: NOT MOVABLE")
|
106
|
+
@jira_board_service.flag_ticket(issue.key, custom_flag_messages.no_merged_prs_flag_msg)
|
107
|
+
return PrStatus::UN_INCLUDED
|
108
|
+
end
|
109
|
+
|
110
|
+
PrStatus::MERGED # At least one merged PR
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -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,28 +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::
|
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(
|
10
|
-
@
|
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<
|
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
|
-
@
|
22
|
+
@board_service.find_issues_by_keys(ids)
|
21
23
|
end
|
22
24
|
|
23
|
-
private
|
24
|
-
|
25
25
|
# @param changelog [String] raw git changelog
|
26
26
|
# @return [Array<String>] list of issue ids from changelog
|
27
27
|
def issue_ids_from_changelog(changelog)
|
28
|
-
|
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
|
29
32
|
end
|
30
33
|
end
|
31
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
|
data/lib/apadmi/grout/di.rb
CHANGED
@@ -3,22 +3,72 @@
|
|
3
3
|
module Apadmi
|
4
4
|
module Grout
|
5
5
|
# Convenience class for initializing and accessing the various actions
|
6
|
-
|
7
|
-
|
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
|
16
|
-
|
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)
|
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)
|
56
|
+
git_utils = Apadmi::Grout::GitUtils
|
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
|
17
68
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@issues_from_changelog_action = Apadmi::Grout::IssuesFromChangelogAction.new(@jira_wrapper, logger)
|
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)
|
22
72
|
end
|
23
73
|
end
|
24
74
|
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
|
@@ -13,7 +13,8 @@ module Apadmi
|
|
13
13
|
# @return [Apadmi::Grout::FlagMessages]
|
14
14
|
def self.default(status)
|
15
15
|
FlagMessages.new(
|
16
|
-
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has no PRs connected to it. CI still moved it"
|
16
|
+
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has no PRs connected to it. CI still moved it. " \
|
17
|
+
"(WARNING: this may cause the `fix_versions` to be incorrect)",
|
17
18
|
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has no MERGED PRs and at least one OPEN PR",
|
18
19
|
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has only DECLINED PRs",
|
19
20
|
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has no MERGED PRs"
|
@@ -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
|