From 8c8515abe5ecff2e4535ab153a4dd524ee32fdeb Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 5 May 2026 17:16:52 -0600 Subject: [PATCH 1/7] ci(ios): publish a per-PR XCFramework snapshot branch For each PR commit, upload the signed XCFramework to S3 under `gutenbergkit/pr-builds//`, force-push a `pr-build/` branch with `Package.swift` rewritten to consume that binary target, and post a sticky PR comment + Buildkite annotation pointing consumers at the branch. Mirrors the wordpress-rs flow. The existing trunk/tag publish step is gated on `build.pull_request.id == null` so we don't double-upload on PRs. Skips fork PRs explicitly to avoid `git push` 403s. --- .buildkite/pipeline.yml | 11 ++- .buildkite/publish-pr-xcframework.sh | 30 ++++++++ fastlane/Fastfile | 110 +++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 2 deletions(-) create mode 100755 .buildkite/publish-pr-xcframework.sh diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index fac81e13f..ad5a69415 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -80,8 +80,8 @@ steps: - label: ':xcode: Build XCFramework' key: build-xcframework depends_on: - - build-react - - swift-test-library + - build-react + - swift-test-library command: | buildkite-agent artifact download dist.tar.gz . tar -xzf dist.tar.gz @@ -97,6 +97,7 @@ steps: - label: ':s3: Publish XCFramework to S3' depends_on: build-xcframework + if: build.pull_request.id == null command: | buildkite-agent artifact download '*.xcframework.zip' . buildkite-agent artifact download '*.xcframework.zip.checksum.txt' . @@ -107,6 +108,12 @@ steps: bundle exec fastlane publish_to_s3 version:${NEW_VERSION:-${BUILDKITE_TAG:-$BUILDKITE_COMMIT}} plugins: *plugins + - label: ':swift: :package: Publish PR XCFramework' + depends_on: build-xcframework + if: build.pull_request.id != null + command: .buildkite/publish-pr-xcframework.sh + plugins: *plugins + - label: ':ios: Test iOS E2E' depends_on: build-react command: | diff --git a/.buildkite/publish-pr-xcframework.sh b/.buildkite/publish-pr-xcframework.sh new file mode 100755 index 000000000..43ea831be --- /dev/null +++ b/.buildkite/publish-pr-xcframework.sh @@ -0,0 +1,30 @@ +#!/bin/bash +set -euo pipefail + +if [[ "${BUILDKITE_PULL_REQUEST:-false}" == "false" ]]; then + echo "Not a PR build, skipping PR XCFramework publish" + exit 0 +fi + +# Skip on fork PRs: bot credentials and S3 secrets aren't available, and we +# couldn't push the snapshot branch back to the canonical repo anyway. +if [[ -n "${BUILDKITE_PULL_REQUEST_REPO:-}" ]] \ + && [[ "$BUILDKITE_PULL_REQUEST_REPO" != *"wordpress-mobile/GutenbergKit"* ]]; then + echo "PR is from a fork (${BUILDKITE_PULL_REQUEST_REPO}), skipping XCFramework publish" + exit 0 +fi + +PR_NUMBER="$BUILDKITE_PULL_REQUEST" + +echo '--- :robot_face: Use bot for Git operations' +source use-bot-for-git + +echo '--- :arrow_down: Downloading XCFramework artifacts' +buildkite-agent artifact download '*.xcframework.zip' . --step "build-xcframework" +buildkite-agent artifact download '*.xcframework.zip.checksum.txt' . --step "build-xcframework" + +echo '--- :rubygems: Setting up Gems' +install_gems + +echo "--- :rocket: Publishing PR build for PR #${PR_NUMBER}" +bundle exec fastlane publish_pr_xcframework pr_number:"$PR_NUMBER" diff --git a/fastlane/Fastfile b/fastlane/Fastfile index c4af8566f..7cd7960e0 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -4,6 +4,9 @@ PROJECT_ROOT = File.expand_path('..', __dir__) APPLE_TEAM_ID = 'PZYM8XX95Q' +GITHUB_REPO = 'wordpress-mobile/GutenbergKit' +XCFRAMEWORK_COMMENT_MARKER = '' + ASC_API_KEY_ENV_VARS = %w[ APP_STORE_CONNECT_API_KEY_KEY_ID APP_STORE_CONNECT_API_KEY_ISSUER_ID @@ -39,6 +42,21 @@ lane :publish_to_s3 do |options| ) end +lane :publish_pr_xcframework do |options| + pr_number = options[:pr_number].to_s + UI.user_error!('pr_number is required') if pr_number.empty? + + branch_name = "pr-build/#{pr_number}" + version = "pr-builds/#{pr_number}" + + publish_to_s3(version: version) + push_xcframework_snapshot_branch(branch_name: branch_name, version: version, checksum: xcframework_checksum) + + body = xcframework_comment_body(branch_name: branch_name, commit_sha: ENV.fetch('BUILDKITE_COMMIT', nil)) + upsert_pr_xcframework_comment(pr_number: pr_number, body: body) + post_buildkite_annotation(body: body) +end + lane :xcframework_sign do sh( 'codesign', @@ -123,3 +141,95 @@ def get_required_env!(key) UI.user_error!("Environment variable `#{key}` is not set.") end + +def push_xcframework_snapshot_branch(branch_name:, version:, checksum:) + package_swift = File.join(PROJECT_ROOT, 'Package.swift') + rewrite_resources_mode!(package_swift, version: version, checksum: checksum) + + sh("git checkout -B #{branch_name}") + git_commit(path: package_swift, message: "Update Package.swift for #{version}") + sh("git push -f origin #{branch_name}") +end + +def rewrite_resources_mode!(package_swift, version:, checksum:) + prefix = 'let resourcesMode: DependencyMode =' + replacement = %(#{prefix} .release(version: "#{version}", checksum: "#{checksum}")\n) + + lines = File.readlines(package_swift) + matches = lines.count { |line| line.start_with?(prefix) } + UI.user_error!("Expected exactly one `#{prefix}` line in Package.swift, found #{matches}") unless matches == 1 + + rewritten = lines.map { |line| line.start_with?(prefix) ? replacement : line } + File.write(package_swift, rewritten.join) +end + +def xcframework_comment_body(branch_name:, commit_sha:) + short_sha = (commit_sha || 'unknown')[0, 8] + <<~MARKDOWN + #{XCFRAMEWORK_COMMENT_MARKER} + ## XCFramework Build + + This PR's XCFramework is available for testing. Add to your `Package.swift`: + + ```swift + .package(url: "https://github.com/#{GITHUB_REPO}", branch: "#{branch_name}") + ``` + + Built from #{short_sha} + MARKDOWN +end + +def upsert_pr_xcframework_comment(pr_number:, body:) + github_token = ENV.fetch('GITHUB_TOKEN', nil) + unless github_token + UI.important('GITHUB_TOKEN not set, skipping PR comment') + return + end + + existing = find_existing_xcframework_comment(github_token: github_token, pr_number: pr_number) + if existing + github_api( + server_url: 'https://api.github.com', + api_token: github_token, + http_method: 'PATCH', + path: "/repos/#{GITHUB_REPO}/issues/comments/#{existing['id']}", + body: { body: body } + ) + else + github_api( + server_url: 'https://api.github.com', + api_token: github_token, + http_method: 'POST', + path: "/repos/#{GITHUB_REPO}/issues/#{pr_number}/comments", + body: { body: body } + ) + end +end + +def find_existing_xcframework_comment(github_token:, pr_number:) + page = 1 + loop do + result = github_api( + server_url: 'https://api.github.com', + api_token: github_token, + http_method: 'GET', + path: "/repos/#{GITHUB_REPO}/issues/#{pr_number}/comments?per_page=100&page=#{page}" + ) + comments = result[:json] || [] + return nil if comments.empty? + + found = comments.find { |c| c['body']&.include?(XCFRAMEWORK_COMMENT_MARKER) } + return found if found + return nil if comments.length < 100 + + page += 1 + end +end + +def post_buildkite_annotation(body:) + return unless ENV['BUILDKITE_AGENT_ACCESS_TOKEN'] + + IO.popen(['buildkite-agent', 'annotate', '--context', 'xcframework', '--style', 'info'], 'w') do |io| + io.write(body) + end +end From 7d3a2e697552080d6459285fb98a2ea8c0e2bd52 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 5 May 2026 17:31:55 -0600 Subject: [PATCH 2/7] ci: prune `pr-build/*` branches for closed PRs on trunk pushes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each PR build force-pushes a `pr-build/` snapshot branch. Nothing prunes them, so they accumulate. Add a Buildkite step that runs on trunk pushes, lists `pr-build/*` refs, queries each PR's state via the GitHub API, and deletes the refs whose PR is `closed` (covers both merged and rejected — GitHub collapses them). Skips a branch on any non-200 response so we never delete a ref we couldn't verify. Mirrors the wordpress-rs sweep step. --- .buildkite/cleanup-pr-build-branches.sh | 86 +++++++++++++++++++++++++ .buildkite/pipeline.yml | 5 ++ 2 files changed, 91 insertions(+) create mode 100755 .buildkite/cleanup-pr-build-branches.sh diff --git a/.buildkite/cleanup-pr-build-branches.sh b/.buildkite/cleanup-pr-build-branches.sh new file mode 100755 index 000000000..13db49b6b --- /dev/null +++ b/.buildkite/cleanup-pr-build-branches.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +set -euo pipefail + +# Deletes `pr-build/` branches whose PR is closed (merged or rejected). +# Runs on trunk pushes so the just-merged PR's branch gets cleaned up +# immediately, and any orphans accumulated from prior failures get swept too. + +if [[ "${BUILDKITE_BRANCH:-}" != "trunk" ]]; then + echo "Not a trunk build (branch=${BUILDKITE_BRANCH:-unset}), skipping" + exit 0 +fi + +if [[ -z "${GITHUB_TOKEN:-}" ]]; then + echo "GITHUB_TOKEN not set, cannot query PR state" >&2 + exit 1 +fi + +GITHUB_REPO="wordpress-mobile/GutenbergKit" + +echo '--- :robot_face: Use bot for Git operations' +source use-bot-for-git + +echo "--- :mag: Listing pr-build/* branches on origin" +mapfile -t branches < <( + git ls-remote --heads origin 'refs/heads/pr-build/*' \ + | awk '{print $2}' \ + | sed 's|^refs/heads/||' +) + +echo "Found ${#branches[@]} pr-build branches" + +if [[ ${#branches[@]} -eq 0 ]]; then + exit 0 +fi + +echo "--- :github: Checking PR state for each branch" +to_delete=() +for branch in "${branches[@]}"; do + pr_number="${branch#pr-build/}" + if ! [[ "$pr_number" =~ ^[0-9]+$ ]]; then + echo "Skipping $branch (unexpected suffix)" + continue + fi + + response=$( + curl --silent --show-error \ + --write-out $'\n%{http_code}' \ + --header "Authorization: Bearer ${GITHUB_TOKEN}" \ + --header "Accept: application/vnd.github+json" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/${GITHUB_REPO}/pulls/${pr_number}" + ) + http_code=$(printf '%s' "$response" | tail -n1) + body=$(printf '%s' "$response" | sed '$d') + + if [[ "$http_code" != "200" ]]; then + echo "Skipping $branch (HTTP $http_code from GitHub)" + continue + fi + + state=$(printf '%s' "$body" | jq -r '.state') + + if [[ "$state" == "closed" ]]; then + echo "Marking $branch for deletion (PR #$pr_number is closed)" + to_delete+=("$branch") + else + echo "Keeping $branch (PR #$pr_number is $state)" + fi +done + +if [[ ${#to_delete[@]} -eq 0 ]]; then + echo "No closed PR branches to delete" + exit 0 +fi + +echo "--- :wastebasket: Deleting ${#to_delete[@]} stale branches" +chunk_size=50 +for ((i=0; i<${#to_delete[@]}; i+=chunk_size)); do + chunk=("${to_delete[@]:i:chunk_size}") + refspecs=() + for branch in "${chunk[@]}"; do + refspecs+=(":refs/heads/${branch}") + done + git push origin "${refspecs[@]}" +done diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index ad5a69415..4ac399855 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -156,3 +156,8 @@ steps: - 'android/Gutenberg/build/outputs/androidTest-results/connected/**/*' - 'android/Gutenberg/build/outputs/buildkite-logs/**/*' - 'android/Gutenberg/build/outputs/connected_android_test_additional_output/**/*' + + - label: ':wastebasket: Clean up `pr-build/*` branches for closed PRs' + if: build.branch == "trunk" + command: .buildkite/cleanup-pr-build-branches.sh + plugins: *plugins From f23dad6603fc70c1e660289400fecd6a18a72f0b Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Mon, 11 May 2026 10:46:16 -0600 Subject: [PATCH 3/7] Apply suggestions from code review Co-authored-by: Gio Lodi --- fastlane/Fastfile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 7cd7960e0..9e1f9ef1f 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -169,7 +169,7 @@ def xcframework_comment_body(branch_name:, commit_sha:) #{XCFRAMEWORK_COMMENT_MARKER} ## XCFramework Build - This PR's XCFramework is available for testing. Add to your `Package.swift`: + This PR's XCFramework is available for testing. Add the following to your `Package.swift`: ```swift .package(url: "https://github.com/#{GITHUB_REPO}", branch: "#{branch_name}") @@ -229,7 +229,5 @@ end def post_buildkite_annotation(body:) return unless ENV['BUILDKITE_AGENT_ACCESS_TOKEN'] - IO.popen(['buildkite-agent', 'annotate', '--context', 'xcframework', '--style', 'info'], 'w') do |io| - io.write(body) - end + buildkite_annotate(context: 'xcframework', style: 'info', message: body) end From 5b1c3a642974892da156d37a5eb000c4de372af5 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Mon, 11 May 2026 10:52:09 -0600 Subject: [PATCH 4/7] ci(ios): tighten PR-number parsing and inline `BUILDKITE_PULL_REQUEST` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address review: - Parse `pr_number` via `Integer(_, 10)` rather than `.to_s + empty?`, so non-numeric input fails loudly instead of producing a `pr-build/abc` ref. Base 10 is explicit to avoid the `"0042" → 34` octal gotcha. - Drop the `PR_NUMBER` shell local — `BUILDKITE_PULL_REQUEST` is already guarded for non-`"false"` above, and the local doesn't earn its keep for one downstream use. --- .buildkite/publish-pr-xcframework.sh | 6 ++---- fastlane/Fastfile | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.buildkite/publish-pr-xcframework.sh b/.buildkite/publish-pr-xcframework.sh index 43ea831be..8a05909e8 100755 --- a/.buildkite/publish-pr-xcframework.sh +++ b/.buildkite/publish-pr-xcframework.sh @@ -14,8 +14,6 @@ if [[ -n "${BUILDKITE_PULL_REQUEST_REPO:-}" ]] \ exit 0 fi -PR_NUMBER="$BUILDKITE_PULL_REQUEST" - echo '--- :robot_face: Use bot for Git operations' source use-bot-for-git @@ -26,5 +24,5 @@ buildkite-agent artifact download '*.xcframework.zip.checksum.txt' . --step "bui echo '--- :rubygems: Setting up Gems' install_gems -echo "--- :rocket: Publishing PR build for PR #${PR_NUMBER}" -bundle exec fastlane publish_pr_xcframework pr_number:"$PR_NUMBER" +echo "--- :rocket: Publishing PR build for PR #${BUILDKITE_PULL_REQUEST}" +bundle exec fastlane publish_pr_xcframework pr_number:"$BUILDKITE_PULL_REQUEST" diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 9e1f9ef1f..82cda787b 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -43,8 +43,7 @@ lane :publish_to_s3 do |options| end lane :publish_pr_xcframework do |options| - pr_number = options[:pr_number].to_s - UI.user_error!('pr_number is required') if pr_number.empty? + pr_number = Integer(options[:pr_number].to_s, 10) branch_name = "pr-build/#{pr_number}" version = "pr-builds/#{pr_number}" From d7e97920f29b2b693d756104d55234d190cb8bce Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Mon, 11 May 2026 11:11:10 -0600 Subject: [PATCH 5/7] ci(ios): use release-toolkit `comment_on_pr` + read `BUILDKITE_PULL_REQUEST` directly - Replace ~50 lines of hand-rolled upsert/find/paginate against the GitHub comments API with the release-toolkit `comment_on_pr` action, which handles the marker-based sticky-comment dance and auto-paginates via Octokit. - Drop the `pr_number:` lane option and read `BUILDKITE_PULL_REQUEST` inside the lane, mirroring the pattern in `Fastlane::Wpmreleasetoolkit::EnvManager#pull_request_number`. The shell script no longer needs to pass it through. Net -41 lines. --- .buildkite/publish-pr-xcframework.sh | 2 +- fastlane/Fastfile | 63 +++++----------------------- 2 files changed, 12 insertions(+), 53 deletions(-) diff --git a/.buildkite/publish-pr-xcframework.sh b/.buildkite/publish-pr-xcframework.sh index 8a05909e8..a25b03543 100755 --- a/.buildkite/publish-pr-xcframework.sh +++ b/.buildkite/publish-pr-xcframework.sh @@ -25,4 +25,4 @@ echo '--- :rubygems: Setting up Gems' install_gems echo "--- :rocket: Publishing PR build for PR #${BUILDKITE_PULL_REQUEST}" -bundle exec fastlane publish_pr_xcframework pr_number:"$BUILDKITE_PULL_REQUEST" +bundle exec fastlane publish_pr_xcframework diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 82cda787b..4af7ab915 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -5,7 +5,7 @@ PROJECT_ROOT = File.expand_path('..', __dir__) APPLE_TEAM_ID = 'PZYM8XX95Q' GITHUB_REPO = 'wordpress-mobile/GutenbergKit' -XCFRAMEWORK_COMMENT_MARKER = '' +XCFRAMEWORK_COMMENT_REUSE_ID = 'gutenbergkit-xcframework-build' ASC_API_KEY_ENV_VARS = %w[ APP_STORE_CONNECT_API_KEY_KEY_ID @@ -42,8 +42,10 @@ lane :publish_to_s3 do |options| ) end -lane :publish_pr_xcframework do |options| - pr_number = Integer(options[:pr_number].to_s, 10) +lane :publish_pr_xcframework do + pr_num_str = ENV.fetch('BUILDKITE_PULL_REQUEST', 'false') + UI.user_error!('publish_pr_xcframework requires BUILDKITE_PULL_REQUEST to be a PR number') if pr_num_str == 'false' + pr_number = Integer(pr_num_str, 10) branch_name = "pr-build/#{pr_number}" version = "pr-builds/#{pr_number}" @@ -52,7 +54,12 @@ lane :publish_pr_xcframework do |options| push_xcframework_snapshot_branch(branch_name: branch_name, version: version, checksum: xcframework_checksum) body = xcframework_comment_body(branch_name: branch_name, commit_sha: ENV.fetch('BUILDKITE_COMMIT', nil)) - upsert_pr_xcframework_comment(pr_number: pr_number, body: body) + comment_on_pr( + project: GITHUB_REPO, + pr_number: pr_number, + body: body, + reuse_identifier: XCFRAMEWORK_COMMENT_REUSE_ID + ) post_buildkite_annotation(body: body) end @@ -165,7 +172,6 @@ end def xcframework_comment_body(branch_name:, commit_sha:) short_sha = (commit_sha || 'unknown')[0, 8] <<~MARKDOWN - #{XCFRAMEWORK_COMMENT_MARKER} ## XCFramework Build This PR's XCFramework is available for testing. Add the following to your `Package.swift`: @@ -178,53 +184,6 @@ def xcframework_comment_body(branch_name:, commit_sha:) MARKDOWN end -def upsert_pr_xcframework_comment(pr_number:, body:) - github_token = ENV.fetch('GITHUB_TOKEN', nil) - unless github_token - UI.important('GITHUB_TOKEN not set, skipping PR comment') - return - end - - existing = find_existing_xcframework_comment(github_token: github_token, pr_number: pr_number) - if existing - github_api( - server_url: 'https://api.github.com', - api_token: github_token, - http_method: 'PATCH', - path: "/repos/#{GITHUB_REPO}/issues/comments/#{existing['id']}", - body: { body: body } - ) - else - github_api( - server_url: 'https://api.github.com', - api_token: github_token, - http_method: 'POST', - path: "/repos/#{GITHUB_REPO}/issues/#{pr_number}/comments", - body: { body: body } - ) - end -end - -def find_existing_xcframework_comment(github_token:, pr_number:) - page = 1 - loop do - result = github_api( - server_url: 'https://api.github.com', - api_token: github_token, - http_method: 'GET', - path: "/repos/#{GITHUB_REPO}/issues/#{pr_number}/comments?per_page=100&page=#{page}" - ) - comments = result[:json] || [] - return nil if comments.empty? - - found = comments.find { |c| c['body']&.include?(XCFRAMEWORK_COMMENT_MARKER) } - return found if found - return nil if comments.length < 100 - - page += 1 - end -end - def post_buildkite_annotation(body:) return unless ENV['BUILDKITE_AGENT_ACCESS_TOKEN'] From c7f1e45d1bcb75b868ec848ffd0841edda1b6682 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Mon, 11 May 2026 11:20:58 -0600 Subject: [PATCH 6/7] ci(ios): swap inline CI env parsing for release-toolkit `EnvManager` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer the canonical helper over the hand-rolled equivalent. `pull_request_number` already handles the `BUILDKITE_PULL_REQUEST="false"` sentinel and Integer parse; `commit_hash` wraps `BUILDKITE_COMMIT`. Sets up the manager at file load with a placeholder env file name — no `.env` is shipped, CI reads straight from the process environment. --- fastlane/Fastfile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 4af7ab915..af3d7d4f0 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -18,6 +18,12 @@ CODE_SIGNING_STORAGE_ENV_VARS = %w[ MATCH_S3_SECRET_ACCESS_KEY ].freeze +# Set up the release-toolkit env manager so lanes can read CI metadata +# (`pull_request_number`, `commit_hash`, etc.) via a single canonical helper. +# We don't ship a `.env` file; on CI the values come straight from the process +# environment, and off-CI the manager will warn (not fail) about the missing file. +Fastlane::Wpmreleasetoolkit::EnvManager.set_up(env_file_name: 'gutenbergkit.env') + before_all do setup_ci end @@ -43,9 +49,9 @@ lane :publish_to_s3 do |options| end lane :publish_pr_xcframework do - pr_num_str = ENV.fetch('BUILDKITE_PULL_REQUEST', 'false') - UI.user_error!('publish_pr_xcframework requires BUILDKITE_PULL_REQUEST to be a PR number') if pr_num_str == 'false' - pr_number = Integer(pr_num_str, 10) + env = Fastlane::Wpmreleasetoolkit::EnvManager.default! + pr_number = env.pull_request_number + UI.user_error!('publish_pr_xcframework must run on a PR build (BUILDKITE_PULL_REQUEST is unset or "false")') if pr_number.nil? branch_name = "pr-build/#{pr_number}" version = "pr-builds/#{pr_number}" @@ -53,7 +59,7 @@ lane :publish_pr_xcframework do publish_to_s3(version: version) push_xcframework_snapshot_branch(branch_name: branch_name, version: version, checksum: xcframework_checksum) - body = xcframework_comment_body(branch_name: branch_name, commit_sha: ENV.fetch('BUILDKITE_COMMIT', nil)) + body = xcframework_comment_body(branch_name: branch_name, commit_sha: env.commit_hash) comment_on_pr( project: GITHUB_REPO, pr_number: pr_number, From 53d0301a09ed95196d9531d0a8dd49a3ff87f90a Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Mon, 11 May 2026 11:40:05 -0600 Subject: [PATCH 7/7] fastlane: require wpmreleasetoolkit so EnvManager is loaded MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fastlane discovers plugin actions via `lib/.../actions/**/*.rb`, but doesn't run the plugin's main entry file — so classes outside `actions/` (like `Fastlane::Wpmreleasetoolkit::EnvManager`) aren't auto-loaded. Without this require, file-load of the Fastfile dies with: Fastfile:25:in 'parsing_binding': uninitialized constant Fastlane::Wpmreleasetoolkit::EnvManager (NameError) …which fails every lane invocation, including the unrelated `xcframework_sign` lane on the `Build XCFramework` step. --- fastlane/Fastfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fastlane/Fastfile b/fastlane/Fastfile index af3d7d4f0..d5984c4ed 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,5 +1,7 @@ # frozen_string_literal: true +require 'fastlane/plugin/wpmreleasetoolkit' + PROJECT_ROOT = File.expand_path('..', __dir__) APPLE_TEAM_ID = 'PZYM8XX95Q'