Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions .buildkite/cleanup-pr-build-branches.sh
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')

Comment on lines +57 to +63
Copy link
Copy Markdown
Contributor Author

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 jq present

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
16 changes: 14 additions & 2 deletions .buildkite/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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' .
Expand All @@ -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: |
Expand Down Expand Up @@ -149,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
28 changes: 28 additions & 0 deletions .buildkite/publish-pr-xcframework.sh
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
74 changes: 74 additions & 0 deletions fastlane/Fastfile
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
Expand All @@ -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
Expand All @@ -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',
Expand Down Expand Up @@ -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
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced this whole method with the release toolkit version

Loading