fastlane-plugin-wpmreleasetoolkit 12.2.1 → 12.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24ece3855d069cb67f8e0a3eca97e11f14af1361ce2c00084289a21b23b99b10
4
- data.tar.gz: 7fda68ae30e60082db9276f89649dcea58d279be623f1ecd6ea96eddf47394cc
3
+ metadata.gz: 29b49bd4df79d18988c3e1b1b87fa0abaea61430ca6a8a0167d298d39812e52c
4
+ data.tar.gz: b926ccff2acbb0bfd97e334ea3734247320910253dc55de9711fa0bfc6c1d80c
5
5
  SHA512:
6
- metadata.gz: 351386b21f54221ebd0b821caaa9bf1b3a2584c4d14d292e4d5a607ab1ef421443df685d25c464da00dc9a6d5abe7b4934a670afa092b9a04776f504f457eba6
7
- data.tar.gz: 5ac9f250c4baef6cab62e214b94ead03fd6ecd5c5618a37f62ed230963c34c7d04951db78b3382e4d0abfa8510d1a571e8a12ca8d0df42d814d5cbe9a51c7679
6
+ metadata.gz: 4ed448099a4ed93e613cd076178c406c2f7f20dcb85d5c8e6a4df76e9aa5a12527e88a31ea4c5293e7db21d02f950c27cab72925ca70c7578854c76e2b6127ae
7
+ data.tar.gz: 293e5690ff3f8cd488d8128b45f2d96f1ebda0ac02a67b9fceee2779c098ee75913e6b96287498183d311f0a585d93b7447cf9f356e11db7783edd51aedc9ee4
@@ -1,17 +1,20 @@
1
1
  module Fastlane
2
2
  module Actions
3
3
  class BuildkitePipelineUploadAction < Action
4
- DEFAULT_ENV_FILE = File.join('.buildkite', 'shared-pipeline-vars').freeze
4
+ DEFAULT_BUILDKITE_PIPELINE_FOLDER = '.buildkite'.freeze
5
+ DEFAULT_ENV_FILE = File.join(DEFAULT_BUILDKITE_PIPELINE_FOLDER, 'shared-pipeline-vars').freeze
5
6
 
6
7
  def self.run(params)
7
8
  pipeline_file = params[:pipeline_file]
9
+ pipeline_file = File.join(DEFAULT_BUILDKITE_PIPELINE_FOLDER, pipeline_file) unless File.absolute_path?(pipeline_file)
8
10
  env_file = params[:env_file]
9
- environment = params[:environment]
11
+ # Both keys and values need to be passed as strings otherwise Fastlane.sh will fail to parse the command.
12
+ environment = params[:environment].to_h { |k, v| [k.to_s, v.to_s] }
10
13
 
11
14
  UI.user_error!("Pipeline file not found: #{pipeline_file}") unless File.exist?(pipeline_file)
12
15
  UI.user_error!('This action can only be called from a Buildkite CI build') unless ENV['BUILDKITE'] == 'true'
13
16
 
14
- UI.message "Adding steps from `#{pipeline_file}` to the current build"
17
+ UI.message("Adding steps from `#{pipeline_file}` to the current build")
15
18
 
16
19
  if env_file && File.exist?(env_file)
17
20
  UI.message(" - Sourcing environment file beforehand: #{env_file}")
@@ -31,7 +34,7 @@ module Fastlane
31
34
  [
32
35
  FastlaneCore::ConfigItem.new(
33
36
  key: :pipeline_file,
34
- description: 'The path to the YAML pipeline file to upload',
37
+ description: 'The path to the YAML pipeline file to upload. If a relative path is provided, it will be prefixed with the `.buildkite/` folder path. Absolute paths are used as-is',
35
38
  optional: false,
36
39
  type: String
37
40
  ),
@@ -9,8 +9,10 @@ module Fastlane
9
9
  version = params[:version]
10
10
  assets = params[:release_assets]
11
11
  release_notes = params[:release_notes_file_path].nil? ? '' : File.read(params[:release_notes_file_path])
12
- # Replace full URLS to PRs/Issues with shorthand, because GitHub does not render them properly otherwise.
13
- release_notes.gsub!(%r{https://github.com/([^/]*/[^/]*)/(pulls?|issues?)/([0-9]*)}, '\1#\3')
12
+ # Replace full URLs to PRs/Issues that are in `[https://some-url]` brackets with shorthand, because GitHub does not render them properly otherwise.
13
+ # That syntax is sometimes used by devs in `RELEASE-NOTES.txt` when pointing to a PR to another repo (e.g. Gutenberg PR from WP repo).
14
+ # We should NOT transform URLs to PRs/Issues that are in `[text](url)` form (those are valid), only the ones directly in `[]` brackets.
15
+ release_notes.gsub!(%r{\[https://github.com/([^/]*/[^/]*)/(pulls?|issues?)/([0-9]*)\]}, '[\1#\3]')
14
16
  prerelease = params[:prerelease]
15
17
  is_draft = params[:is_draft]
16
18
 
@@ -89,24 +89,33 @@ module Fastlane
89
89
  # @return [String] The URL of the created Pull Request, or `nil` if no PR was created.
90
90
  #
91
91
  def self.create_backmerge_pr(token:, repository:, title:, head_branch:, base_branch:, labels:, milestone:, reviewers:, team_reviewers:, intermediate_branch_created_callback:)
92
- intermediate_branch = "merge/#{head_branch.gsub('/', '-')}-into-#{base_branch.gsub('/', '-')}"
92
+ # Do an early pre-check to see if the PR would be valid, but only if no callback (as a callback might add new commits on intermediate branch)
93
+ if intermediate_branch_created_callback.nil? && !can_merge?(head_branch, into: base_branch)
94
+ UI.error("Nothing to merge from #{head_branch} into #{base_branch}. Skipping PR creation.")
95
+ return nil
96
+ end
93
97
 
98
+ # Create the intermediate branch
99
+ intermediate_branch = "merge/#{head_branch.gsub('/', '-')}-into-#{base_branch.gsub('/', '-')}"
94
100
  if Fastlane::Helper::GitHelper.branch_exists_on_remote?(branch_name: intermediate_branch)
95
101
  UI.important("An intermediate branch `#{intermediate_branch}` already exists on the remote. It will be deleted and GitHub will close any associated existing PR.")
96
102
  Fastlane::Helper::GitHelper.delete_remote_branch_if_exists!(intermediate_branch)
97
103
  end
98
-
99
104
  Fastlane::Helper::GitHelper.delete_local_branch_if_exists!(intermediate_branch)
100
105
  Fastlane::Helper::GitHelper.create_branch(intermediate_branch)
101
106
 
102
- intermediate_branch_created_callback&.call(base_branch, intermediate_branch)
103
-
104
- # if there's a callback, make sure it didn't switch branches
105
- other_action.ensure_git_branch(branch: "^#{intermediate_branch}/") unless intermediate_branch_created_callback.nil?
106
-
107
- if Fastlane::Helper::GitHelper.point_to_same_commit?(base_branch, head_branch)
108
- UI.error("No differences between #{head_branch} and #{base_branch}. Skipping PR creation.")
109
- return nil
107
+ # Call the callback if one was provided to allow the use to add commits on the intermediate branch (e.g. solve conflicts)
108
+ unless intermediate_branch_created_callback.nil?
109
+ intermediate_branch_created_callback.call(base_branch, intermediate_branch)
110
+ # Make sure the callback block didn't switch branches
111
+ other_action.ensure_git_branch(branch: "^#{intermediate_branch}$")
112
+
113
+ # When a callback was provided, do the pre-check about valid PR _only_ at that point, in case the callback added new commits
114
+ unless can_merge?(intermediate_branch, into: base_branch)
115
+ UI.error("Nothing to merge from #{intermediate_branch} into #{base_branch}. Skipping PR creation.")
116
+ Fastlane::Helper::GitHelper.delete_local_branch_if_exists!(intermediate_branch)
117
+ return nil
118
+ end
110
119
  end
111
120
 
112
121
  other_action.push_to_git_remote(tags: false)
@@ -138,6 +147,23 @@ module Fastlane
138
147
  )
139
148
  end
140
149
 
150
+ # Determine if a `head->base` PR would be considered valid by GitHub.
151
+ #
152
+ # Note that a PR with an empty diff can still be valid (e.g. if you merge a commit and its revert)
153
+ #
154
+ # This method returns false mostly when all commits from `head` has already been merged into `base`
155
+ # and that there are no new commits to merge (in which case GitHub would refuse creating the PR)
156
+ #
157
+ # @param head [String] the head reference (commit sha or branch name) we want to merge
158
+ # @param into [String] the base reference (commit sha or branch name) we want to merge into
159
+ # @return [Boolean] true if there are commits in `head` that are not yet in `base` and a merge can happen;
160
+ # false if all commits from `head` are already in `base` and a merge would be rejected
161
+ #
162
+ def self.can_merge?(head, into:)
163
+ merge_base = Fastlane::Helper::GitHelper.find_merge_base(into, head)
164
+ !Fastlane::Helper::GitHelper.point_to_same_commit?(merge_base, head)
165
+ end
166
+
141
167
  def self.description
142
168
  'Creates backmerge PRs for a release branch into target branches'
143
169
  end
@@ -201,7 +227,9 @@ module Fastlane
201
227
  optional: true,
202
228
  type: Array),
203
229
  FastlaneCore::ConfigItem.new(key: :intermediate_branch_created_callback,
204
- description: 'Callback to allow for the caller to perform operations on the intermediate branch before pushing. The call back receives two parameters: the base (target) branch for the PR and the intermediate branch name',
230
+ description: 'Callback to allow for the caller to perform operations on the intermediate branch (e.g. pushing new commits to pre-solve conflicts) before creating the PR. ' \
231
+ + 'The callback receives two parameters: the base (target) branch for the PR and the intermediate branch name that has been created.' \
232
+ + 'Note that if you use the callback to add new commits to the intermediate branch, you are responsible for git-pushing them too',
205
233
  optional: true,
206
234
  type: Proc),
207
235
  Fastlane::Helper::GithubHelper.github_token_config_item,
@@ -113,11 +113,19 @@ module Fastlane
113
113
 
114
114
  # Get the SHA of a given git ref. Typically useful to get the SHA of the current HEAD commit.
115
115
  #
116
- # @param [String] ref The git ref (commit, branch name, 'HEAD', …) to resolve as a SHA
116
+ # @param ref [String]
117
+ # The git ref (commit, branch name, 'HEAD', …) to resolve as a SHA
118
+ # @param prepend_origin_if_needed [Boolean]
119
+ # If true, will retry the rev-parse by prefixing `origin/` to the ref it it failed without it
117
120
  # @return [String] The commit SHA of the ref
118
121
  #
119
- def self.get_commit_sha(ref: 'HEAD')
120
- Git.open(Dir.pwd).revparse(ref)
122
+ def self.get_commit_sha(ref: 'HEAD', prepend_origin_if_needed: false)
123
+ repo = Git.open(Dir.pwd)
124
+ repo.revparse(ref)
125
+ rescue Git::FailedError
126
+ raise unless prepend_origin_if_needed
127
+
128
+ repo.revparse("origin/#{ref}")
121
129
  end
122
130
 
123
131
  # Creates a tag for the given version, and optionally push it to the remote.
@@ -170,28 +178,36 @@ module Fastlane
170
178
  Action.sh('git', 'fetch', '--tags')
171
179
  end
172
180
 
181
+ # Use `git merge-base` to find as good a common ancestors as possible for a merge
182
+ #
183
+ # @param ref1 [String] The first git reference (sha1, ref name…)to find the common ancestor of
184
+ # @param ref2 [String] The second git reference (sha1, ref name…)to find the common ancestor of
185
+ # @return [String] The merge-base aka common ancestor for the 2 commits provided
186
+ # @note If a reference (e.g. branch name) can't be found locally, it will try with the same ref prefixed with `origin/`
187
+ #
188
+ def self.find_merge_base(ref1, ref2)
189
+ git_repo = Git.open(Dir.pwd)
190
+ # Resolve to shas, mostly so that we can support cases with and without `origin/` explicit prefix on branch names
191
+ ref1_sha, ref2_sha = [ref1, ref2].map { |ref| get_commit_sha(ref: ref, prepend_origin_if_needed: true) }
192
+
193
+ git_repo.merge_base(ref1_sha, ref2_sha)&.first&.sha
194
+ end
195
+
173
196
  # Checks if two git references point to the same commit.
174
197
  #
175
198
  # @param ref1 [String] the first git reference to check.
176
199
  # @param ref2 [String] the second git reference to check.
177
- # @param remote_name [String] the name of the remote repository to use (default is 'origin').
178
- # If nil or empty, no remote prefix will be used.
179
200
  #
180
201
  # @return [Boolean] true if the two references point to the same commit, false otherwise.
181
202
  #
182
- def self.point_to_same_commit?(ref1, ref2, remote_name: 'origin')
183
- git_repo = Git.open(Dir.pwd)
184
-
185
- ref1_full = remote_name.to_s.empty? ? ref1 : "#{remote_name}/#{ref1}"
186
- ref2_full = remote_name.to_s.empty? ? ref2 : "#{remote_name}/#{ref2}"
203
+ def self.point_to_same_commit?(ref1, ref2)
187
204
  begin
188
- ref1_commit = git_repo.gcommit(ref1_full)
189
- ref2_commit = git_repo.gcommit(ref2_full)
205
+ ref1_sha = get_commit_sha(ref: ref1, prepend_origin_if_needed: true)
206
+ ref2_sha = get_commit_sha(ref: ref2, prepend_origin_if_needed: true)
190
207
  rescue StandardError => e
191
- UI.error "Error fetching commits for #{ref1_full} and #{ref2_full}: #{e.message}"
192
- return false
208
+ UI.user_error! "Error fetching commits for #{ref1} and/or #{ref2}: #{e.message}"
193
209
  end
194
- ref1_commit.sha == ref2_commit.sha
210
+ ref1_sha == ref2_sha
195
211
  end
196
212
 
197
213
  # Returns the current git branch, or "HEAD" if it's not checked out to any branch
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Fastlane
4
4
  module Wpmreleasetoolkit
5
- VERSION = '12.2.1'
5
+ VERSION = '12.3.1'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fastlane-plugin-wpmreleasetoolkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 12.2.1
4
+ version: 12.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Automattic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-18 00:00:00.000000000 Z
11
+ date: 2024-11-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport