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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -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/{jira/actions/find_tickets_to_move_action.rb → actions/find_tickets_to_move_action/find_tickets_to_move_jira_action.rb} +16 -17
- 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 -6
- data/lib/apadmi/grout/actions/move_tickets_action.rb +34 -0
- data/lib/apadmi/grout/di.rb +53 -9
- data/lib/apadmi/grout/{jira/models/find_tickets_options.rb → models/ado_config.rb} +2 -2
- data/lib/apadmi/grout/models/find_tickets_options.rb +44 -0
- data/lib/apadmi/grout/{jira/models → models}/flag_messages.rb +0 -0
- 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/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 +18 -0
- data/lib/apadmi/grout/utils/network_service.rb +88 -0
- data/lib/apadmi/grout/version.rb +1 -1
- data/lib/apadmi_grout.rb +3 -21
- metadata +20 -13
- 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: 8da866b98ba10eeb0dd9a8a664bd926e8c657cb918bf8a73474d1e3aacd941c4
|
4
|
+
data.tar.gz: '0759b30f1bcbe228e7c1a22eecc52bad02f8174d31584d130936a9d3203d35ef'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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::
|
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(
|
22
|
-
@
|
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::
|
33
|
-
# @return [Array<
|
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::
|
42
|
+
options ||= Apadmi::Grout::JiraFindTicketsOptions.new(include_no_sprint_tickets: false)
|
43
43
|
|
44
|
-
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 [
|
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 [
|
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 = @
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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::
|
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
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
|
-
|
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
|
data/lib/apadmi/grout/di.rb
CHANGED
@@ -3,23 +3,67 @@
|
|
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.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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
@@ -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
|
File without changes
|
@@ -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
|
File without changes
|
@@ -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
|