apadmi_grout 0.0.5 → 1.2.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 +13 -4
- data/lib/apadmi/grout/di.rb +4 -3
- data/lib/apadmi/grout/jira/actions/find_tickets_to_move_action.rb +57 -18
- data/lib/apadmi/grout/jira/models/find_tickets_options.rb +10 -0
- data/lib/apadmi/grout/jira/models/flag_messages.rb +23 -6
- data/lib/apadmi/grout/jira/wrapper/jira_wrapper.rb +5 -2
- data/lib/apadmi/grout/release_notes/actions/issues_from_changelog_action.rb +0 -2
- data/lib/apadmi/grout/utils/filename_utils.rb +40 -0
- data/lib/apadmi/grout/utils/git_utils.rb +39 -0
- data/lib/apadmi/grout/version.rb +1 -1
- data/lib/apadmi_grout.rb +3 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a8f0b024bb98a0372b85755e14bc753370328418273d6e0d07aba74f664bb9a
|
4
|
+
data.tar.gz: '04286d3e985378d78f1dec57c220f1a4a5ec35d90ccf4a407d3dde66f75f9a6d'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c9d6d5fce5da1ffaa1ab71a7f54e4bba85a3f4fda470b0613049a2169ca9466d180b655c3129561e9046fe3d0a4041b85c2cf25d70dfd8ac7f521b70a6d5113
|
7
|
+
data.tar.gz: 13b59090e27577e87f7a52ef9dbcc085db1af1b0575a20e98642669155b0652e0e91976a4943f027e33c25d325fe0aa205afa481d761b7a944f5aebf3bf4138a
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,16 @@
|
|
1
|
-
#
|
1
|
+
# Core Changelog
|
2
2
|
|
3
|
-
## [
|
3
|
+
## [1.2.0] - 2022-05-17
|
4
|
+
* Filter out issues missing from git changelogs when finding tickets to move.
|
4
5
|
|
5
|
-
## [
|
6
|
+
## [1.1.0] - 2022-05-12
|
7
|
+
* Support allowing tickets that aren't assigned to a sprint to be moved.
|
8
|
+
* Created utilities for getting the git root and number of commits
|
6
9
|
|
7
|
-
- Initial release
|
10
|
+
## [1.0.0] - 2022-05-09 - Initial release
|
11
|
+
|
12
|
+
* Various JIRA utilities to simplify JIRA interactions
|
13
|
+
* Action for searching JIRA tickets
|
14
|
+
* Action for transitioning JIRA tickets
|
15
|
+
* Action for generating release notes
|
16
|
+
* Action for extracting ticket keys from Git changelog
|
data/lib/apadmi/grout/di.rb
CHANGED
@@ -4,7 +4,7 @@ module Apadmi
|
|
4
4
|
module Grout
|
5
5
|
# Convenience class for initializing and accessing the various actions
|
6
6
|
class DependencyInjector
|
7
|
-
attr_reader :move_jira_tickets_action, :find_tickets_to_move_action, :generate_release_notes_action, :issues_from_changelog_action
|
7
|
+
attr_reader :jira_wrapper, :move_jira_tickets_action, :find_tickets_to_move_action, :generate_release_notes_action, :issues_from_changelog_action
|
8
8
|
|
9
9
|
# @param [String] username
|
10
10
|
# @param [String] token
|
@@ -15,10 +15,11 @@ module Apadmi
|
|
15
15
|
def initialize(username, token, base_url, context_path, project, logger)
|
16
16
|
@jira_wrapper = Apadmi::Grout::JiraWrapper.new(username, token, base_url, context_path, project)
|
17
17
|
|
18
|
+
git_utils = Apadmi::Grout::GitUtils
|
18
19
|
@move_jira_tickets_action = Apadmi::Grout::MoveJiraTicketsAction.new(@jira_wrapper, logger)
|
19
|
-
@find_tickets_to_move_action = Apadmi::Grout::FindTicketsToMoveAction.new(@jira_wrapper, logger)
|
20
|
-
@generate_release_notes_action = Apadmi::Grout::GenerateReleaseNotesAction.new(base_url)
|
21
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)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
end
|
@@ -2,74 +2,113 @@
|
|
2
2
|
|
3
3
|
module Apadmi
|
4
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
|
+
|
5
13
|
# Finds and returns a list of all the issues that are ready to be moved
|
6
14
|
# Any tickets found that have the given status but *don't* appear ready to be moved will
|
7
15
|
# be flagged.
|
8
16
|
class FindTicketsToMoveAction
|
9
17
|
# @param [Apadmi::Grout::JiraWrapper]
|
18
|
+
# @param [Apadmi::Grout::GitUtils]
|
19
|
+
# @param [Apadmi::Grout::IssuesFromChangelogAction]
|
10
20
|
# @param [Apadmi::Grout::DefaultLogger] // or your own logger!
|
11
|
-
def initialize(jira_wrapper, logger)
|
21
|
+
def initialize(jira_wrapper, git_utils, issues_from_chnglg, logger)
|
12
22
|
@jira_wrapper = jira_wrapper
|
23
|
+
@git_utils = git_utils
|
24
|
+
@issues_from_changelog_action = issues_from_chnglg
|
13
25
|
@logger = logger
|
14
26
|
end
|
15
27
|
|
16
28
|
# @param component [String] Only include tickets tagged with this component
|
17
29
|
# @param status [String] The status of tickets to be moved (Usually "Awaiting QA Release")
|
18
30
|
# @param excluded_ticket_keys [Array<String>] ticket keys to be excluded from consideration
|
19
|
-
# @param custom_flag_messages [FlagMessages]
|
31
|
+
# @param custom_flag_messages [Apadmi::Grout::FlagMessages]
|
32
|
+
# @param options [Apadmi::Grout::FindTicketsOptions]
|
20
33
|
# @return [Array<JIRA::Resource::Issue>] the issues ready to move
|
21
34
|
def run(
|
22
35
|
component,
|
23
36
|
status,
|
24
37
|
excluded_ticket_keys,
|
25
|
-
custom_flag_messages =
|
26
|
-
|
27
|
-
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has no MERGED PRs and at least one OPEN PR",
|
28
|
-
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has only DECLINED PRs",
|
29
|
-
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has no MERGED PRs"
|
30
|
-
)
|
38
|
+
custom_flag_messages = nil,
|
39
|
+
options = nil
|
31
40
|
)
|
32
|
-
|
33
|
-
|
41
|
+
custom_flag_messages ||= Apadmi::Grout::FlagMessages.default(status)
|
42
|
+
options ||= Apadmi::Grout::FindTicketsOptions.new(include_no_sprint_tickets: false)
|
43
|
+
|
44
|
+
issues = @jira_wrapper.search_unblocked_issues(
|
45
|
+
component, status, [],
|
46
|
+
allow_no_sprint: options.include_no_sprint_tickets
|
47
|
+
).reject do |issue|
|
34
48
|
excluded_ticket_keys.include? issue.key
|
35
49
|
end
|
36
50
|
|
37
|
-
|
51
|
+
issue_keys = issues.map(&:key)
|
52
|
+
@logger.message("Found issues to consider #{issue_keys.join(", ")}")
|
53
|
+
|
54
|
+
# Get the issues in the git changelog, filtered for the issues being considered
|
55
|
+
changelog_ids = @issues_from_changelog_action.issue_ids_from_changelog(
|
56
|
+
@git_utils.merge_changelog(issue_keys)
|
57
|
+
)
|
58
|
+
|
38
59
|
final_list = issues.filter do |issue|
|
39
60
|
# Decide whether to include this ticket based on PRs
|
40
|
-
process_prs(issue, custom_flag_messages)
|
61
|
+
status = process_prs(issue, custom_flag_messages)
|
62
|
+
decide_should_include(issue, status, changelog_ids)
|
41
63
|
end
|
64
|
+
|
42
65
|
@logger.message("Final list: #{final_list.map(&:key).join(", ")}")
|
43
66
|
final_list
|
44
67
|
end
|
45
68
|
|
46
69
|
private
|
47
70
|
|
71
|
+
# @param issue [JIRA::Resource::Issue]
|
72
|
+
# @param pr_status [Int] status of whether or not we can move
|
73
|
+
# @param changelog_ids [Array<String>] the ticket ids pulled from git
|
74
|
+
def decide_should_include(issue, pr_status, changelog_ids)
|
75
|
+
# For merged PRs, check if the ticket appears in the changelog.
|
76
|
+
# If it doesn't then it's likely it was merged AFTER this release build was triggered and shouldn't be moved
|
77
|
+
# by this build
|
78
|
+
if pr_status == PrStatus::MERGED
|
79
|
+
is_in_changelog = changelog_ids.include?(issue.key)
|
80
|
+
@logger.message("NOTE: Not including #{issue.key} since it's not in the git changelog.") unless is_in_changelog
|
81
|
+
return is_in_changelog
|
82
|
+
end
|
83
|
+
|
84
|
+
pr_status != PrStatus::UN_INCLUDED
|
85
|
+
end
|
86
|
+
|
48
87
|
# @param issue [JIRA::Resource::Issue]
|
49
88
|
# @param custom_flag_messages [FlagMessages]
|
50
|
-
# @return [
|
89
|
+
# @return [Int] status of whether or not we can move
|
51
90
|
def process_prs(issue, custom_flag_messages)
|
52
91
|
prs = @jira_wrapper.get_ticket_prs(issue)
|
53
92
|
|
54
93
|
if prs.empty?
|
55
94
|
@logger.message("#{issue.key} has no PRs. Flagging it: STILL MOVABLE")
|
56
95
|
@jira_wrapper.flag_ticket(issue.key, custom_flag_messages.no_prs_flag_msg)
|
57
|
-
return
|
96
|
+
return PrStatus::UNMERGED_BUT_INCLUDED
|
58
97
|
elsif prs.all?(&:open)
|
59
98
|
@logger.message("#{issue.key} has only open PRs. Flagging it: NOT MOVABLE")
|
60
99
|
@jira_wrapper.flag_ticket(issue.key, custom_flag_messages.open_prs_flag_msg)
|
61
|
-
return
|
100
|
+
return PrStatus::UN_INCLUDED
|
62
101
|
elsif prs.all?(&:declined)
|
63
102
|
@logger.message("#{issue.key} has only declined PRs. Flagging it: NOT MOVABLE")
|
64
103
|
@jira_wrapper.flag_ticket(issue.key, custom_flag_messages.declined_prs_flag_msg)
|
65
|
-
return
|
104
|
+
return PrStatus::UN_INCLUDED
|
66
105
|
elsif prs.none?(&:merged)
|
67
106
|
@logger.message("#{issue.key} has no merged PRs. Flagging it: NOT MOVABLE")
|
68
107
|
@jira_wrapper.flag_ticket(issue.key, custom_flag_messages.no_merged_prs_flag_msg)
|
69
|
-
return
|
108
|
+
return PrStatus::UN_INCLUDED
|
70
109
|
end
|
71
110
|
|
72
|
-
|
111
|
+
PrStatus::MERGED # At least one merged PR
|
73
112
|
end
|
74
113
|
end
|
75
114
|
end
|
@@ -1,8 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
module Apadmi
|
4
|
+
module Grout
|
5
|
+
FlagMessages = Struct.new(
|
6
|
+
:no_prs_flag_msg,
|
7
|
+
:open_prs_flag_msg,
|
8
|
+
:declined_prs_flag_msg,
|
9
|
+
:no_merged_prs_flag_msg
|
10
|
+
) do
|
11
|
+
# Get default flag messages
|
12
|
+
# @param status [String] status ticket will be moving to
|
13
|
+
# @return [Apadmi::Grout::FlagMessages]
|
14
|
+
def self.default(status)
|
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. " \
|
17
|
+
"(WARNING: this may cause the `fix_versions` to be incorrect)",
|
18
|
+
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has no MERGED PRs and at least one OPEN PR",
|
19
|
+
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has only DECLINED PRs",
|
20
|
+
"REASON FOR FLAG: Check this ticket - it was put in #{status} but has no MERGED PRs"
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -46,16 +46,19 @@ module Apadmi
|
|
46
46
|
# @param [String] component
|
47
47
|
# @param [String] status
|
48
48
|
# @param [String[]] ticket_types
|
49
|
+
# @param [Boolean] allow_no_sprint
|
49
50
|
# @return [Array<JIRA::Resource::Issue>]
|
50
|
-
def search_unblocked_issues(component, status, ticket_types = [])
|
51
|
+
def search_unblocked_issues(component, status, ticket_types = [], allow_no_sprint: false)
|
51
52
|
component_filter = (" AND component = '#{component}' " unless component.empty?) || ""
|
52
53
|
status_filter = (" AND status = '#{status}' " unless status.empty?) || ""
|
53
54
|
type_filter = ("AND (#{ticket_types.map { |type| "type = #{type}" }.join("OR ")})" unless ticket_types.empty?) || ""
|
55
|
+
empty_sprint_condition = ("OR sprint is EMPTY" if allow_no_sprint) || ""
|
54
56
|
|
55
57
|
jql_search = %{
|
56
58
|
project = '#{@project}'
|
57
59
|
#{status_filter} #{component_filter} #{type_filter}
|
58
|
-
AND sprint in openSprints() AND (labels not in(Blocked) or labels is EMPTY)
|
60
|
+
AND (sprint in openSprints() #{empty_sprint_condition}) AND (labels not in(Blocked) or labels is EMPTY)
|
61
|
+
AND Flagged is EMPTY
|
59
62
|
}
|
60
63
|
|
61
64
|
@jira_client.Issue.jql(jql_search, { max_results: 1000 }).uniq
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Apadmi
|
4
|
+
module Grout
|
5
|
+
# Generic Filename Utils
|
6
|
+
class FilenameUtils
|
7
|
+
# Constructs a standard Apadmi filename for a build output binary
|
8
|
+
# @param [Hash] options the options to create a filename with
|
9
|
+
# @option options [String] :client_name The client name
|
10
|
+
# @option options [String] :product_name The product name
|
11
|
+
# @option options [String] :platform The product's platform
|
12
|
+
# @option options [String] :version Version number
|
13
|
+
# @option options [String] :build_number Build number
|
14
|
+
# @option options [String] :data (Today) The date for the file
|
15
|
+
# @option options [String] :suffix Suffix to identify what this output is (e.g. SOURCE, UAT, QA, DEV)
|
16
|
+
# @return [String] A filename formatted such as "Apadmi-Grout-Ruby-v1.0.0(2921)-2021-06-22-DEV"
|
17
|
+
def self.binary_output_filename(options = {})
|
18
|
+
client_name = options[:client_name]
|
19
|
+
raise ":client_name shouldn't be empty" if client_name.blank?
|
20
|
+
|
21
|
+
product_name = options[:product_name]
|
22
|
+
raise ":product_name shouldn't be empty" if product_name.blank?
|
23
|
+
|
24
|
+
platform = options[:platform]
|
25
|
+
raise ":platform shouldn't be empty" if platform.blank?
|
26
|
+
|
27
|
+
version = options[:version]
|
28
|
+
raise ":version shouldn't be empty" if version.blank?
|
29
|
+
|
30
|
+
build_number = options[:build_number]
|
31
|
+
raise ":build_number shouldn't be empty" if build_number.blank?
|
32
|
+
|
33
|
+
date = options[:date] || Time.now.strftime("%Y-%m-%d")
|
34
|
+
suffix = ("-#{options[:suffix]}" unless options[:suffix].blank?) || ""
|
35
|
+
|
36
|
+
"#{client_name}-#{product_name}-#{platform}-v#{version}(#{build_number})-#{date}#{suffix}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "open3"
|
4
|
+
|
5
|
+
module Apadmi
|
6
|
+
module Grout
|
7
|
+
# Generic Git related utils
|
8
|
+
class GitUtils
|
9
|
+
# Gets the root of the Git repo we're in
|
10
|
+
# @return [String] The full path for the root of this Git repo
|
11
|
+
def self.git_root
|
12
|
+
stdout, stderr, = Open3.capture3("git rev-parse --show-toplevel")
|
13
|
+
raise "Failed to get git root: #{stderr}" unless stderr.strip.empty?
|
14
|
+
|
15
|
+
stdout.strip
|
16
|
+
end
|
17
|
+
|
18
|
+
# Gets the number of commits accessible from HEAD treating the history as a graph.
|
19
|
+
# See more details here: https://git-scm.com/docs/git-rev-list
|
20
|
+
# @return [String] The number of commits
|
21
|
+
def self.number_of_commits
|
22
|
+
stdout, stderr, = Open3.capture3("git rev-list HEAD --count")
|
23
|
+
raise "Failed to get commit number: #{stderr}" unless stderr.strip.empty?
|
24
|
+
|
25
|
+
stdout.strip
|
26
|
+
end
|
27
|
+
|
28
|
+
# Gets all the merges accessible from the current HEAD which matches at least one of the given grep conditions
|
29
|
+
# @param grep_conditions [Array<String>] values to be passed in as grep cases (https://git-scm.com/docs/git-log)
|
30
|
+
def self.merge_changelog(grep_conditions)
|
31
|
+
command = "git log HEAD --merges --format=%s#{grep_conditions.map { |c| " --grep #{c}" }.join(" ")}"
|
32
|
+
stdout, stderr, = Open3.capture3(command)
|
33
|
+
raise "Failed to get changelog: #{stderr}" unless stderr.strip.empty?
|
34
|
+
|
35
|
+
stdout
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/apadmi/grout/version.rb
CHANGED
data/lib/apadmi_grout.rb
CHANGED
@@ -16,5 +16,8 @@ require_relative "apadmi/grout/jira/wrapper/jira_wrapper"
|
|
16
16
|
require_relative "apadmi/grout/jira/models/pull_request"
|
17
17
|
require_relative "apadmi/grout/jira/models/version"
|
18
18
|
require_relative "apadmi/grout/jira/models/flag_messages"
|
19
|
+
require_relative "apadmi/grout/jira/models/find_tickets_options"
|
19
20
|
|
20
21
|
require_relative "apadmi/grout/utils/logger"
|
22
|
+
require_relative "apadmi/grout/utils/git_utils"
|
23
|
+
require_relative "apadmi/grout/utils/filename_utils"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apadmi_grout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Apadmi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-05-
|
11
|
+
date: 2022-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -66,6 +66,7 @@ files:
|
|
66
66
|
- lib/apadmi/grout/di.rb
|
67
67
|
- lib/apadmi/grout/jira/actions/find_tickets_to_move_action.rb
|
68
68
|
- lib/apadmi/grout/jira/actions/move_jira_tickets_action.rb
|
69
|
+
- lib/apadmi/grout/jira/models/find_tickets_options.rb
|
69
70
|
- lib/apadmi/grout/jira/models/flag_messages.rb
|
70
71
|
- lib/apadmi/grout/jira/models/pull_request.rb
|
71
72
|
- lib/apadmi/grout/jira/models/version.rb
|
@@ -74,6 +75,8 @@ files:
|
|
74
75
|
- lib/apadmi/grout/release_notes/actions/issues_from_changelog_action.rb
|
75
76
|
- lib/apadmi/grout/release_notes/models/release_notes_config.rb
|
76
77
|
- lib/apadmi/grout/release_notes/models/release_notes_templates.rb
|
78
|
+
- lib/apadmi/grout/utils/filename_utils.rb
|
79
|
+
- lib/apadmi/grout/utils/git_utils.rb
|
77
80
|
- lib/apadmi/grout/utils/logger.rb
|
78
81
|
- lib/apadmi/grout/version.rb
|
79
82
|
- lib/apadmi_grout.rb
|