apadmi_grout 0.0.5 → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16022940fafd296f9f52d02c001b1a78906bfe59e1d9a0c3f9c27968ca1d1386
4
- data.tar.gz: efc3cc64e75a693b32307c1b58e85a5a23d155e450187a1ecd4612d1b017d838
3
+ metadata.gz: 9a8f0b024bb98a0372b85755e14bc753370328418273d6e0d07aba74f664bb9a
4
+ data.tar.gz: '04286d3e985378d78f1dec57c220f1a4a5ec35d90ccf4a407d3dde66f75f9a6d'
5
5
  SHA512:
6
- metadata.gz: 2191857e5bfdfcf6fbca7c1b2fbb889f38ec1e198d4d149c51fa696d63eba6991ae948bd95bf057764c3d2a1528d01038d77b95a241ebf9154fb21d0f11253a6
7
- data.tar.gz: 5dcb577071f78db6e03bc5c06da0a873493f2ac9bbd3c1ecc8ec6586ba50003558d5e843d676b548cf369b330b2197b2d27029032ac9b0eece90904316a03e52
6
+ metadata.gz: 2c9d6d5fce5da1ffaa1ab71a7f54e4bba85a3f4fda470b0613049a2169ca9466d180b655c3129561e9046fe3d0a4041b85c2cf25d70dfd8ac7f521b70a6d5113
7
+ data.tar.gz: 13b59090e27577e87f7a52ef9dbcc085db1af1b0575a20e98642669155b0652e0e91976a4943f027e33c25d325fe0aa205afa481d761b7a944f5aebf3bf4138a
data/CHANGELOG.md CHANGED
@@ -1,7 +1,16 @@
1
- # Fastlane Plugin Changelog
1
+ # Core Changelog
2
2
 
3
- ## [Unreleased]
3
+ ## [1.2.0] - 2022-05-17
4
+ * Filter out issues missing from git changelogs when finding tickets to move.
4
5
 
5
- ## [0.1.0] - 2022-01-23
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
@@ -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 = FlagMessages.new(
26
- "REASON FOR FLAG: Check this ticket - it was put in #{status} but has no PRs connected to it. CI still moved it",
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
- issues = @jira_wrapper.search_unblocked_issues(component, status).reject do |issue|
33
- puts issue.key
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
- @logger.message("Found issues to consider #{issues.map(&:key).join(", ")}")
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 [Boolean] whether or not this ticket can be moved
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 true
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 false
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 false
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 false
108
+ return PrStatus::UN_INCLUDED
70
109
  end
71
110
 
72
- true # At least one merged PR, so it's included
111
+ PrStatus::MERGED # At least one merged PR
73
112
  end
74
113
  end
75
114
  end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Apadmi
4
+ module Grout
5
+ FindTicketsOptions = Struct.new(
6
+ :include_no_sprint_tickets,
7
+ keyword_init: true
8
+ )
9
+ end
10
+ end
@@ -1,8 +1,25 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- FlagMessages = Struct.new(
4
- :no_prs_flag_msg,
5
- :open_prs_flag_msg,
6
- :declined_prs_flag_msg,
7
- :no_merged_prs_flag_msg
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) AND Flagged 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
@@ -20,8 +20,6 @@ module Apadmi
20
20
  @jira_wrapper.find_issues_by_keys(ids)
21
21
  end
22
22
 
23
- private
24
-
25
23
  # @param changelog [String] raw git changelog
26
24
  # @return [Array<String>] list of issue ids from changelog
27
25
  def issue_ids_from_changelog(changelog)
@@ -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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Apadmi
4
4
  module Grout
5
- VERSION = "0.0.5"
5
+ VERSION = "1.2.0"
6
6
  end
7
7
  end
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: 0.0.5
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
- - Sam
7
+ - Apadmi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-09 00:00:00.000000000 Z
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