fastlane-plugin-wpmreleasetoolkit 14.7.0 → 14.8.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: c3a14d01dbfc81f832b5d1b4c009e16c2b78ff148b3c25c592a1bc32c5e8d442
4
- data.tar.gz: 8fbb44705e3d5799cb21857647c83f8321b9494548dc0342eb093781f707538d
3
+ metadata.gz: 614ca9504d0baa99cc411f83e7bb94f179260a0e8154d738aecfed47a4de7ce5
4
+ data.tar.gz: 7edfec9cb051abe68182905d8ee0620c73a0da73c02e829e7dcb69f1593ea49e
5
5
  SHA512:
6
- metadata.gz: 73ee821d6f4d161a1479bedd639f7427c6022cd17551dce93d8d87cabf47d95513a9595f8aeb02523b60ada7d52141fb82bbbbbbb9538b0b9aaa2dd9d7e681ef
7
- data.tar.gz: 4dee3516eb30a8bbbc97ef67568f97d541452a44dd56a16080606bbdbe790fe816890108ca97a843ee8af511fb307d39dd19f7999cb5429d2d0ee4a630c44afd
6
+ metadata.gz: 32d6b0a90838debf53ba8fc6c5fbdf729a5159a12c0b69ffa8be1709cca3be571d28e1471d2736bb911c97f67d6720b2aec2320bbcd221362a873b14d597ecf0
7
+ data.tar.gz: d9170171309d3b0f11cf102757bd4bdcdc380ef207f7b06d17e2f03e4aef17094c77bf84e1eba17de43cf2e29f833b9a8de4277dab853118a26ba6fa23e391d2
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fastlane/action'
4
+ require_relative '../../helper/github_helper'
5
+
6
+ module Fastlane
7
+ module Actions
8
+ class FindOrCreatePullRequestAction < Action
9
+ def self.run(params)
10
+ github_helper = Fastlane::Helper::GithubHelper.new(github_token: params[:github_token])
11
+
12
+ existing_pr = github_helper.find_pull_request(
13
+ repository: params[:repository],
14
+ head: params[:head],
15
+ base: params[:base]
16
+ )
17
+
18
+ unless existing_pr.nil?
19
+ UI.message("An open Pull Request already exists for `#{params[:head]}`: #{existing_pr.html_url}")
20
+ return existing_pr.html_url
21
+ end
22
+
23
+ other_action.create_pull_request(
24
+ api_url: params[:api_url],
25
+ api_token: params[:github_token],
26
+ repo: params[:repository],
27
+ title: params[:title],
28
+ body: params[:body],
29
+ draft: params[:draft],
30
+ head: params[:head],
31
+ base: params[:base],
32
+ labels: params[:labels],
33
+ assignees: params[:assignees],
34
+ reviewers: params[:reviewers],
35
+ team_reviewers: params[:team_reviewers],
36
+ milestone: params[:milestone]
37
+ )
38
+ end
39
+
40
+ def self.description
41
+ 'Returns the URL of the open Pull Request for a head branch, creating one if none exists yet'
42
+ end
43
+
44
+ def self.details
45
+ <<~DETAILS
46
+ Looks for an open Pull Request whose head is the given branch and which targets the given base,
47
+ and returns its URL if found. Otherwise, creates a new Pull Request and returns its URL.
48
+
49
+ This is useful for "rolling" automations (e.g. a daily translations or dependency-update job) that
50
+ force-push the same head branch on every run: GitHub automatically refreshes the diff of the existing
51
+ PR, so this action only needs to open a PR the first time.
52
+ DETAILS
53
+ end
54
+
55
+ def self.authors
56
+ ['Automattic']
57
+ end
58
+
59
+ def self.return_type
60
+ :string
61
+ end
62
+
63
+ def self.return_value
64
+ 'The URL of the existing or newly-created Pull Request'
65
+ end
66
+
67
+ def self.available_options
68
+ # Parameters we forward as-is from Fastlane's `create_pull_request` action
69
+ forwarded_param_keys = %i[
70
+ api_url
71
+ draft
72
+ labels
73
+ assignees
74
+ reviewers
75
+ team_reviewers
76
+ milestone
77
+ ].freeze
78
+
79
+ forwarded_params = Fastlane::Actions::CreatePullRequestAction.available_options.select do |opt|
80
+ forwarded_param_keys.include?(opt.key)
81
+ end
82
+
83
+ [
84
+ *forwarded_params,
85
+ Fastlane::Helper::GithubHelper.github_token_config_item, # forwarded to `api_token` in the `create_pull_request` action
86
+ FastlaneCore::ConfigItem.new(
87
+ key: :repository,
88
+ env_name: 'GHHELPER_REPOSITORY',
89
+ description: 'The remote path of the GH repository on which we work, e.g. `wordpress-mobile/wordpress-ios`',
90
+ optional: false,
91
+ type: String
92
+ ),
93
+ FastlaneCore::ConfigItem.new(
94
+ key: :title,
95
+ description: 'The title of the Pull Request to create if none exists yet',
96
+ optional: false,
97
+ type: String
98
+ ),
99
+ FastlaneCore::ConfigItem.new(
100
+ key: :body,
101
+ description: 'The body of the Pull Request to create if none exists yet',
102
+ optional: true,
103
+ type: String
104
+ ),
105
+ FastlaneCore::ConfigItem.new(
106
+ key: :head,
107
+ description: 'The head branch of the Pull Request (the branch with the changes)',
108
+ optional: false,
109
+ type: String
110
+ ),
111
+ FastlaneCore::ConfigItem.new(
112
+ key: :base,
113
+ description: 'The base branch the Pull Request targets (e.g. `trunk`)',
114
+ optional: false,
115
+ type: String
116
+ ),
117
+ ]
118
+ end
119
+
120
+ def self.is_supported?(platform)
121
+ true
122
+ end
123
+ end
124
+ end
125
+ end
@@ -304,6 +304,23 @@ module Fastlane
304
304
  reuse_identifier
305
305
  end
306
306
 
307
+ # Find an existing Pull Request matching the given head (and optionally base) branch.
308
+ #
309
+ # @param [String] repository The repository name, including the organization (e.g. `wordpress-mobile/wordpress-ios`)
310
+ # @param [String] head The head branch to look for. May be given as `branch` or as the fully-qualified `owner:branch`;
311
+ # when unqualified, it is automatically prefixed with the repository's owner.
312
+ # @param [String?] base The base branch the PR should target. If nil, PRs targeting any base are considered.
313
+ # @param [String] state The PR state to match (`open`, `closed`, or `all`). Defaults to `open`.
314
+ # @return [Sawyer::Resource, nil] The first matching Pull Request, or nil if none matches.
315
+ #
316
+ def find_pull_request(repository:, head:, base: nil, state: 'open')
317
+ qualified_head = head.include?(':') ? head : "#{repository.split('/').first}:#{head}"
318
+ options = { state: state, head: qualified_head }
319
+ options[:base] = base unless base.nil?
320
+
321
+ client.pull_requests(repository, options).first
322
+ end
323
+
307
324
  # Update a milestone for a repository
308
325
  #
309
326
  # @param [String] repository The repository name (including the organization)
@@ -3,6 +3,6 @@
3
3
  module Fastlane
4
4
  module Wpmreleasetoolkit
5
5
  NAME = 'fastlane-plugin-wpmreleasetoolkit'
6
- VERSION = '14.7.0'
6
+ VERSION = '14.8.0'
7
7
  end
8
8
  end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fastlane
4
+ module Wpmreleasetoolkit
5
+ module Versioning
6
+ # Google Play Store's maximum allowed versionCode.
7
+ MAX_PLAY_STORE_VERSION_CODE = 2_100_000_000
8
+
9
+ # The `ContinuousBuildCodeFormatter` derives an Android Play Store `versionCode` for a
10
+ # "continuous trunk" release model, where the low-order term is a high-cardinality,
11
+ # monotonically increasing build number (e.g. a Buildkite build number).
12
+ #
13
+ # The build code is computed as:
14
+ #
15
+ # versionCode = (major * 10 + minor) * 10^build_digits + build_number
16
+ #
17
+ # It takes `major`, `minor`, and `build_number` as explicit arguments rather than an
18
+ # `AppVersion`, because the inputs come from different sources and an `AppVersion` does not
19
+ # model them: the marketing `major`/`minor` come from the parsed version, while `build_number`
20
+ # is an independent CI counter (e.g. `BUILDKITE_BUILD_NUMBER`). Notably, `AppVersion#build_number`
21
+ # means something else in this domain (the RC/beta iteration counter, e.g. `-rc-1`), so taking an
22
+ # `AppVersion` here would invite reading the wrong field. There is also no `patch`: in a
23
+ # continuous-trunk model the build number strictly orders every build and subsumes patch's
24
+ # ordering role (hotfixes get a new build number, not a patch digit in the code).
25
+ #
26
+ # Because the build number is globally monotonic and the version prefix only ever increases,
27
+ # the resulting code is always strictly increasing — even if the build number eventually
28
+ # exceeds `10^build_digits` (which only costs human-readability, not ordering). The only hard
29
+ # correctness constraint is staying at or below the Play Store's max versionCode.
30
+ #
31
+ # Unlike `DerivedBuildCodeFormatter` (fixed-width string concatenation capped at 8 total digits
32
+ # and 3 digits per component, i.e. build <= 999), this formatter can hold a large build number.
33
+ # The two formatters target different release models; this one does not replace the other.
34
+ class ContinuousBuildCodeFormatter
35
+ # @param [Integer] build_digits Number of digits reserved for the build number, which sets the
36
+ # multiplier applied to the `major * 10 + minor` prefix (multiplier = 10^build_digits).
37
+ # Must be a positive integer. Defaults to 6 (multiplier = 1_000_000).
38
+ #
39
+ def initialize(build_digits: 6)
40
+ validate_build_digits!(build_digits)
41
+ @build_digits = build_digits
42
+ end
43
+
44
+ # Derive the build code (Android `versionCode`).
45
+ #
46
+ # @param [Integer] major The major (marketing) version number.
47
+ # @param [Integer] minor The minor (marketing) version number. Must be 9 or lower.
48
+ # @param [Integer] build_number A high-cardinality, monotonically increasing build number
49
+ # (e.g. a Buildkite build number). This is a CI counter, not `AppVersion#build_number`.
50
+ #
51
+ # @return [Integer] The derived `versionCode`.
52
+ #
53
+ def build_code(major:, minor:, build_number:)
54
+ # Validate up front so bad input (e.g. strings from env vars or file reads) raises a
55
+ # user-friendly error rather than an opaque `TypeError` from the arithmetic below.
56
+ validate_component!('major', major)
57
+ validate_component!('minor', minor)
58
+ validate_component!('build_number', build_number)
59
+
60
+ # `major * 10 + minor` is only unambiguous while minor is a single digit.
61
+ if minor > 9
62
+ UI.user_error!("Minor version (#{minor}) must be 9 or lower to derive an unambiguous build code with `#{self.class.name}`")
63
+ end
64
+
65
+ prefix = (major * 10) + minor
66
+ code = (prefix * (10**@build_digits)) + build_number
67
+
68
+ # Sanity check: Play Store versionCodes must be positive integers.
69
+ if code <= 0
70
+ UI.user_error!("Derived build code (#{code}) must be a positive integer")
71
+ end
72
+
73
+ if code > MAX_PLAY_STORE_VERSION_CODE
74
+ UI.user_error!("Derived build code (#{code}) exceeds the maximum allowed Play Store versionCode (#{MAX_PLAY_STORE_VERSION_CODE})")
75
+ end
76
+
77
+ code
78
+ end
79
+
80
+ private
81
+
82
+ # Validates that a version component is a non-negative integer.
83
+ #
84
+ # @param [String] name The component name, used in the error message
85
+ # @param [Integer] value The value to validate
86
+ #
87
+ # @raise [StandardError] If the value is not a non-negative integer
88
+ #
89
+ def validate_component!(name, value)
90
+ unless value.is_a?(Integer)
91
+ UI.user_error!("`#{name}` must be an integer, got: #{value.class}")
92
+ end
93
+
94
+ return unless value.negative?
95
+
96
+ UI.user_error!("`#{name}` must be a non-negative integer, got: #{value}")
97
+ end
98
+
99
+ # Validates that `build_digits` is a positive integer.
100
+ #
101
+ # @param [Integer] build_digits The build digit count to validate
102
+ #
103
+ # @raise [StandardError] If the value is not a positive integer
104
+ #
105
+ def validate_build_digits!(build_digits)
106
+ unless build_digits.is_a?(Integer)
107
+ UI.user_error!("`build_digits` must be an integer, got: #{build_digits.class}")
108
+ end
109
+
110
+ return if build_digits.positive?
111
+
112
+ UI.user_error!("`build_digits` must be a positive integer, got: #{build_digits}")
113
+ end
114
+ end
115
+ end
116
+ end
117
+ 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: 14.7.0
4
+ version: 14.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Automattic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-12 00:00:00.000000000 Z
11
+ date: 2026-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: buildkit
@@ -439,6 +439,7 @@ files:
439
439
  - lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_new_milestone_action.rb
440
440
  - lib/fastlane/plugin/wpmreleasetoolkit/actions/common/create_release_backmerge_pull_request_action.rb
441
441
  - lib/fastlane/plugin/wpmreleasetoolkit/actions/common/extract_release_notes_for_version_action.rb
442
+ - lib/fastlane/plugin/wpmreleasetoolkit/actions/common/find_or_create_pull_request_action.rb
442
443
  - lib/fastlane/plugin/wpmreleasetoolkit/actions/common/find_previous_tag.rb
443
444
  - lib/fastlane/plugin/wpmreleasetoolkit/actions/common/firebase_login.rb
444
445
  - lib/fastlane/plugin/wpmreleasetoolkit/actions/common/get_prs_between_tags.rb
@@ -519,6 +520,7 @@ files:
519
520
  - lib/fastlane/plugin/wpmreleasetoolkit/versioning/files/android_version_file.rb
520
521
  - lib/fastlane/plugin/wpmreleasetoolkit/versioning/files/ios_version_file.rb
521
522
  - lib/fastlane/plugin/wpmreleasetoolkit/versioning/formatters/abstract_version_formatter.rb
523
+ - lib/fastlane/plugin/wpmreleasetoolkit/versioning/formatters/continuous_build_code_formatter.rb
522
524
  - lib/fastlane/plugin/wpmreleasetoolkit/versioning/formatters/derived_build_code_formatter.rb
523
525
  - lib/fastlane/plugin/wpmreleasetoolkit/versioning/formatters/four_part_build_code_formatter.rb
524
526
  - lib/fastlane/plugin/wpmreleasetoolkit/versioning/formatters/four_part_version_formatter.rb