-
Notifications
You must be signed in to change notification settings - Fork 3
ci(ios): publish + prune per-PR XCFramework snapshot branches #495
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+202
−2
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
8c8515a
ci(ios): publish a per-PR XCFramework snapshot branch
jkmassel 7d3a2e6
ci: prune `pr-build/*` branches for closed PRs on trunk pushes
jkmassel f23dad6
Apply suggestions from code review
jkmassel 5b1c3a6
ci(ios): tighten PR-number parsing and inline `BUILDKITE_PULL_REQUEST`
jkmassel d7e9792
ci(ios): use release-toolkit `comment_on_pr` + read `BUILDKITE_PULL_R…
jkmassel c7f1e45
ci(ios): swap inline CI env parsing for release-toolkit `EnvManager`
jkmassel 53d0301
fastlane: require wpmreleasetoolkit so EnvManager is loaded
jkmassel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,86 @@ | ||
| #!/bin/bash | ||
|
|
||
| set -euo pipefail | ||
|
|
||
| # Deletes `pr-build/<n>` 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 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| #!/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 | ||
|
|
||
| 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 #${BUILDKITE_PULL_REQUEST}" | ||
| bundle exec fastlane publish_pr_xcframework |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,14 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require 'fastlane/plugin/wpmreleasetoolkit' | ||
|
|
||
| PROJECT_ROOT = File.expand_path('..', __dir__) | ||
|
|
||
| APPLE_TEAM_ID = 'PZYM8XX95Q' | ||
|
|
||
| GITHUB_REPO = 'wordpress-mobile/GutenbergKit' | ||
| XCFRAMEWORK_COMMENT_REUSE_ID = 'gutenbergkit-xcframework-build' | ||
|
|
||
| ASC_API_KEY_ENV_VARS = %w[ | ||
| APP_STORE_CONNECT_API_KEY_KEY_ID | ||
| APP_STORE_CONNECT_API_KEY_ISSUER_ID | ||
|
|
@@ -15,6 +20,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 | ||
|
|
@@ -39,6 +50,27 @@ lane :publish_to_s3 do |options| | |
| ) | ||
| end | ||
|
|
||
| lane :publish_pr_xcframework do | ||
| 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}" | ||
|
|
||
| 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.commit_hash) | ||
| comment_on_pr( | ||
| project: GITHUB_REPO, | ||
| pr_number: pr_number, | ||
| body: body, | ||
| reuse_identifier: XCFRAMEWORK_COMMENT_REUSE_ID | ||
| ) | ||
| post_buildkite_annotation(body: body) | ||
| end | ||
|
|
||
| lane :xcframework_sign do | ||
| sh( | ||
| 'codesign', | ||
|
|
@@ -123,3 +155,45 @@ 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 Build | ||
|
|
||
| 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}") | ||
| ``` | ||
|
|
||
| <sub>Built from #{short_sha}</sub> | ||
| MARKDOWN | ||
|
Comment on lines
+180
to
+192
|
||
| end | ||
|
|
||
| def post_buildkite_annotation(body:) | ||
| return unless ENV['BUILDKITE_AGENT_ACCESS_TOKEN'] | ||
|
|
||
| buildkite_annotate(context: 'xcframework', style: 'info', message: body) | ||
| end | ||
|
Comment on lines
+195
to
+199
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replaced this whole method with the release toolkit version |
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is fine – all of our CI is pretty much guaranteed to have
jqpresent