fastlane-plugin-wpmreleasetoolkit 12.2.0 → 12.3.0

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: 5efb1f6f88d1bb1617313112768ef93497cabe3897bb4834c5026a4e9a68ced0
4
- data.tar.gz: 8eaef85d47b2ec7be3e2da0693856923c02e6f67b65707e4143d7241147a7435
3
+ metadata.gz: bd7622da309b11a1d3f03e67692147361bc620cd3b9ecb0fa6d9b923cd5e8c15
4
+ data.tar.gz: 662c4964c041947dd71506cee2aeb6c01bfaf2ae35281ff41192ab1231c82201
5
5
  SHA512:
6
- metadata.gz: 7c05b2af7ca941a5afb52b703f3f0ee315e0ab48ffa87de88c48c5f22bbc1db82f0a819c7e4f2b10e754b6558569d801431e64b00f491e282eadd9885c4f75ac
7
- data.tar.gz: fc832aaf5f31e2e6b3f51c268f38529f056dc40a0971fb67e962da313634e2c56580eeac23f8d2e94f2f3dcc74ad3d69b0a465724afb7c178501103a5c01d309
6
+ metadata.gz: 891584b03637c76ea99ef7843db8639a8154565efd90e9339167f8edf40d7e24fc53c371c42f8e0de06f32fb6050010288b367920b326d9c34beec60d78fb9b4
7
+ data.tar.gz: 3e8d0bc1db3db2e76c64ed79285113d63fdad4ffcd08cc7444c741aedf62611a194c47f73ea0c35246320855e5cc3e77e302fbf789c86f3de0378adb4682261e
data/README.md CHANGED
@@ -24,15 +24,17 @@ This guide also includes some tips about configuring your environment and IDE (e
24
24
 
25
25
  When you need to do a new release of the `release-toolkit`, simply run `rake new_release` and follow the instructions.
26
26
 
27
+ > [!NOTE]
27
28
  > This task will:
28
29
  > - Show you the CHANGELOG/release notes it's about to use for that version
29
30
  > - Deduce which version number to use according to [SemVer](https://semver.org/) rules, and ask you to confirm that version number
30
31
  > - Create a `release/<x.y>` branch, update the version number in all the right places, and create a PR for those changes
31
32
 
32
- Submit the PR, adding the `Releases` label to it and adding the `owl-team` as reviewers.
33
+ Submit the PR, adding the `Releases` label to it and adding the `@wordpress-mobile/apps-infrastructure` team as reviewers.
33
34
 
34
35
  Once that PR is approved and merged, create a new GitHub Release, copy/pasting the CHANGELOG entries for that GH release's description.
35
36
 
37
+ > [!IMPORTANT]
36
38
  > Publishing the GitHub Release will create the associated tag as well, which will trigger the CI job that will ultimately `gem push` the gem on RubyGems.
37
39
 
38
40
  ## Security
@@ -50,4 +52,4 @@ If you have questions about getting setup or just want to say hi, join the [Word
50
52
 
51
53
  ## License
52
54
 
53
- Mobile Release Toolkit is an Open Source project covered by the [GNU General Public License version 2](LICENSE).
55
+ Mobile Release Toolkit is an Open Source project covered by the [GNU General Public License version 2](LICENSE).
@@ -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
  ),
@@ -89,23 +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('/', '-')}"
93
-
94
- if Fastlane::Helper::GitHelper.branch_exists_on_remote?(branch_name: intermediate_branch)
95
- UI.user_error!("The intermediate branch `#{intermediate_branch}` already exists. Please check if there is an existing Pull Request that needs to be merged or closed first, or delete the branch.")
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.")
96
95
  return nil
97
96
  end
98
97
 
98
+ # Create the intermediate branch
99
+ intermediate_branch = "merge/#{head_branch.gsub('/', '-')}-into-#{base_branch.gsub('/', '-')}"
100
+ if Fastlane::Helper::GitHelper.branch_exists_on_remote?(branch_name: intermediate_branch)
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.")
102
+ Fastlane::Helper::GitHelper.delete_remote_branch_if_exists!(intermediate_branch)
103
+ end
104
+ Fastlane::Helper::GitHelper.delete_local_branch_if_exists!(intermediate_branch)
99
105
  Fastlane::Helper::GitHelper.create_branch(intermediate_branch)
100
106
 
101
- intermediate_branch_created_callback&.call(base_branch, intermediate_branch)
102
-
103
- # if there's a callback, make sure it didn't switch branches
104
- other_action.ensure_git_branch(branch: "^#{intermediate_branch}/") unless intermediate_branch_created_callback.nil?
105
-
106
- if Fastlane::Helper::GitHelper.point_to_same_commit?(base_branch, head_branch)
107
- UI.error("No differences between #{head_branch} and #{base_branch}. Skipping PR creation.")
108
- 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
109
119
  end
110
120
 
111
121
  other_action.push_to_git_remote(tags: false)
@@ -137,6 +147,23 @@ module Fastlane
137
147
  )
138
148
  end
139
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
+
140
167
  def self.description
141
168
  'Creates backmerge PRs for a release branch into target branches'
142
169
  end
@@ -200,7 +227,9 @@ module Fastlane
200
227
  optional: true,
201
228
  type: Array),
202
229
  FastlaneCore::ConfigItem.new(key: :intermediate_branch_created_callback,
203
- 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',
204
233
  optional: true,
205
234
  type: Proc),
206
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
@@ -232,6 +248,32 @@ module Fastlane
232
248
  !Action.sh('git', 'ls-remote', '--heads', remote_name, branch_name).empty?
233
249
  end
234
250
 
251
+ # Delete a local branch if it exists.
252
+ #
253
+ # @param [String] branch_name The name of the local branch to delete.
254
+ # @return [Boolean] true if the branch was deleted, false if not (e.g. no such local branch existed in the first place)
255
+ #
256
+ def self.delete_local_branch_if_exists!(branch_name)
257
+ git_repo = Git.open(Dir.pwd)
258
+ return false unless git_repo.is_local_branch?(branch_name)
259
+
260
+ git_repo.branch(branch_name).delete
261
+ true
262
+ end
263
+
264
+ # Delete a remote branch if it exists.
265
+ #
266
+ # @param [String] branch_name The name of the remote branch to delete.
267
+ # @param [String] remote_name The name of the remote to delete the branch from. Defaults to 'origin'
268
+ # @return [Boolean] true if the branch was deleted, false if not (e.g. no such local branch existed in the first place)
269
+ #
270
+ def self.delete_remote_branch_if_exists!(branch_name, remote_name: 'origin')
271
+ git_repo = Git.open(Dir.pwd)
272
+ return false unless git_repo.branches.any? { |b| b.remote&.name == remote_name && b.name == branch_name }
273
+
274
+ git_repo.push(remote_name, branch_name, delete: true)
275
+ end
276
+
235
277
  # Checks whether a given path is ignored by Git, relying on Git's `check-ignore` under the hood.
236
278
  #
237
279
  # @param [String] path The path to check against `.gitignore`
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Fastlane
4
4
  module Wpmreleasetoolkit
5
- VERSION = '12.2.0'
5
+ VERSION = '12.3.0'
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.0
4
+ version: 12.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Automattic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-30 00:00:00.000000000 Z
11
+ date: 2024-10-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport