45aa1a67b0
Co-authored-by: Tomek Zawadzki <tomekzawadzki98@gmail.com> Co-authored-by: vineyardbovines <spencerfpope@gmail.com>
373 lines
15 KiB
YAML
373 lines
15 KiB
YAML
# Credit for fingerprint action https://github.com/expo/expo
|
||
# https://github.com/expo/expo/blob/main/.github/workflows/pr-labeler.yml
|
||
---
|
||
name: PR Tests
|
||
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
pull_request:
|
||
types: [opened, synchronize]
|
||
|
||
concurrency:
|
||
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
# Permissions are granted per-job below; anything unlisted defaults to none.
|
||
# pull-requests: write is needed by sticky-pull-request-comment to post the
|
||
# bundle-size and fingerprint diffs and the PR OTA install link
|
||
permissions: {}
|
||
|
||
# denis release tag in bluesky-social/tango whose linux-amd64 binary the PR OTA
|
||
# job downloads. Bump this one line to roll denis.
|
||
env:
|
||
DENIS_RELEASE_TAG: denis-v0.1.1
|
||
NODE_OPTIONS: --max-old-space-size=4096
|
||
|
||
jobs:
|
||
# Populate this from main so every PR can restore the same trusted baseline.
|
||
webpack-analyzer-base:
|
||
runs-on: ubuntu-24.04
|
||
if: ${{ github.event_name == 'push' }}
|
||
permissions:
|
||
contents: read
|
||
steps:
|
||
- name: ⬇️ Checkout
|
||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
|
||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||
|
||
- name: 🔧 Setup Node
|
||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||
with:
|
||
node-version-file: package.json
|
||
cache: pnpm
|
||
|
||
- name: ⬇️ Get base stats from cache
|
||
id: get-base-stats
|
||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||
with:
|
||
path: stats.json
|
||
key: stats-base-main-${{ github.sha }}
|
||
|
||
- name: 🔦 Generate stats file for base commit
|
||
if: ${{ !steps.get-base-stats.outputs.cache-hit }}
|
||
run: |
|
||
pnpm install
|
||
pnpm intl:build
|
||
pnpm generate-webpack-stats-file
|
||
|
||
- name: ⬆️ Save base stats to cache
|
||
if: ${{ !steps.get-base-stats.outputs.cache-hit }}
|
||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||
with:
|
||
path: stats.json
|
||
key: stats-base-main-${{ github.sha }}
|
||
|
||
webpack-analyzer:
|
||
runs-on: ubuntu-24.04
|
||
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.event_name == 'pull_request'}}
|
||
permissions:
|
||
contents: read
|
||
pull-requests: write
|
||
steps:
|
||
- name: ⬇️ Checkout
|
||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
with:
|
||
fetch-depth: 0
|
||
|
||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||
|
||
- name: 🔧 Setup Node
|
||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||
with:
|
||
node-version-file: package.json
|
||
cache: pnpm
|
||
|
||
- name: 🌿 Ensure tracking relevant branches and checkout base
|
||
env:
|
||
HEAD_REF: ${{ github.head_ref }}
|
||
BASE_REF: ${{ github.base_ref }}
|
||
run: |
|
||
git checkout $HEAD_REF
|
||
git checkout $BASE_REF
|
||
|
||
- name: 🔍 Get the base commit
|
||
id: base-commit
|
||
env:
|
||
BASE_REF: ${{ github.base_ref }}
|
||
run: echo base-commit=$(git log -n 1 $BASE_REF --pretty=format:'%H') >> "$GITHUB_OUTPUT"
|
||
|
||
- name: 🔀 Merge PR commit
|
||
env:
|
||
HEAD_REF: ${{ github.head_ref }}
|
||
run: |
|
||
# Have to set a git config for the merge to work
|
||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||
git config --global user.name "github-actions[bot]"
|
||
git merge --no-edit $HEAD_REF
|
||
pnpm install
|
||
pnpm intl:build
|
||
|
||
- name: 🔦 Generate stats file for PR
|
||
run: |
|
||
pnpm generate-webpack-stats-file
|
||
mv stats.json ../stats-new.json
|
||
|
||
- name: ⬇️ Get base stats from cache
|
||
id: get-base-stats
|
||
# Restore-only prevents PR-scoped fallback builds from creating caches.
|
||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||
with:
|
||
path: stats.json
|
||
key: stats-base-main-${{ steps.base-commit.outputs.base-commit }}
|
||
|
||
- name: ⏪ Restore to base commit
|
||
if: ${{ !steps.get-base-stats.outputs.cache-hit }}
|
||
env:
|
||
BASE_COMMIT: ${{ steps.base-commit.outputs.base-commit }}
|
||
run: |
|
||
git reset "$BASE_COMMIT"
|
||
git restore .
|
||
|
||
- name: 🔦 Generate stats file from base commit
|
||
if: ${{ !steps.get-base-stats.outputs.cache-hit }}
|
||
run: |
|
||
pnpm install
|
||
pnpm intl:build
|
||
pnpm generate-webpack-stats-file
|
||
|
||
- name: % Get diff
|
||
id: get-diff
|
||
uses: NejcZdovc/bundle-size-diff@5321de41d2d62a7b0f4d6e60f59d1280a0034160 # v1.1.0
|
||
with:
|
||
base_path: "stats.json"
|
||
pr_path: "../stats-new.json"
|
||
excluded_assets: "(.+).chunk.js|(.+).js.map|(.+).json|(.+).png|(.+).svg|(.+).webp|(.+).jpg|(.+).ico"
|
||
|
||
- name: 💬 Drop a comment
|
||
uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
|
||
with:
|
||
header: bundle-diff
|
||
message: |
|
||
| Old size | New size | Diff |
|
||
|----------|----------|-----------------------|
|
||
| ${{ steps.get-diff.outputs.base_file_string }} | ${{ steps.get-diff.outputs.pr_file_string }} | ${{ steps.get-diff.outputs.diff_file_string }} (${{ steps.get-diff.outputs.percent }}%) |
|
||
---
|
||
|
||
fingerprint-native:
|
||
runs-on: ubuntu-22.04
|
||
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.event_name == 'pull_request'}}
|
||
permissions:
|
||
contents: read
|
||
pull-requests: write
|
||
outputs:
|
||
# Empty when the native surface is unchanged, 'true' when it changed.
|
||
# publish-pr-ota gates on this.
|
||
includes-changes: ${{ steps.fingerprint.outputs.includes-changes }}
|
||
steps:
|
||
- name: ⬇️ Checkout
|
||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
with:
|
||
fetch-depth: 100
|
||
|
||
- name: ⬇️ Fetch commits from base branch
|
||
run: git fetch origin main:main --depth 100
|
||
if: github.event_name == 'pull_request'
|
||
|
||
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10
|
||
|
||
- name: 🔧 Setup Node
|
||
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||
with:
|
||
node-version-file: package.json
|
||
cache: pnpm
|
||
|
||
- name: 📷 Check fingerprint and install dependencies
|
||
id: fingerprint
|
||
timeout-minutes: 5
|
||
uses: bluesky-social/github-actions/fingerprint-native@abc6a46eb4badf243f55bfd7d6cec42722456300 # v0.3.0
|
||
with:
|
||
profile: pull-request
|
||
|
||
- name: 💬 Drop a comment
|
||
uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
|
||
if: ${{ steps.fingerprint.outputs.includes-changes }}
|
||
with:
|
||
header: fingerprint-diff
|
||
message: |
|
||
The Pull Request introduced native fingerprint changes against the base commit.
|
||
|
||
---
|
||
*Generated by [PR labeler](https://github.com/expo/expo/actions/workflows/pr-labeler.yml) 🤖*
|
||
|
||
- name: 💬 Delete comment
|
||
uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
|
||
if: ${{ !steps.fingerprint.outputs.includes-changes }}
|
||
with:
|
||
header: fingerprint-diff
|
||
delete: true
|
||
|
||
# publish-pr-ota is skipped once the fingerprint changes, so any install
|
||
# link left over from an earlier fingerprint-clean commit on this PR now
|
||
# points at a bundle that no longer matches the PR. Drop it.
|
||
- name: 💬 Delete stale OTA install comment
|
||
uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
|
||
if: ${{ steps.fingerprint.outputs.includes-changes }}
|
||
with:
|
||
header: pull-request-ota
|
||
delete: true
|
||
|
||
- name: 🏷️ Label as fingerprint changed
|
||
if: ${{ steps.fingerprint.outputs.includes-changes }}
|
||
env:
|
||
GH_TOKEN: ${{ github.token }}
|
||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||
run: |
|
||
gh pr edit "$PR_NUMBER" --add-label "bot: fingerprint changed" || true
|
||
|
||
- name: 🏷️ Remove fingerprint changed label
|
||
if: ${{ !steps.fingerprint.outputs.includes-changes }}
|
||
env:
|
||
GH_TOKEN: ${{ github.token }}
|
||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||
run: |
|
||
gh pr edit "$PR_NUMBER" --remove-label "bot: fingerprint changed" || true
|
||
|
||
# Automatic per-PR OTA preview, published to the pull-request-<N> channel on
|
||
# denis. Replaces the old `@github-actions ota` comment trigger. Gated to
|
||
# same-repo PRs (fork guard): a branch can only exist in this repo if someone
|
||
# with write access pushed it, so an outside contributor (who can only open a
|
||
# PR from a fork) never runs this job with the denis publish role in scope.
|
||
# This matches the fork-guard gate the other jobs in this workflow use;
|
||
# author_association is deliberately NOT checked (it can't identify a private
|
||
# org member and would skip their PRs).
|
||
#
|
||
# Bot authors are excluded: Dependabot pushes in-repo branches, so it passes
|
||
# the fork guard, but GitHub withholds repo secrets from Dependabot-triggered
|
||
# runs. EXPO_TOKEN is then empty and the job fails at setup — a red check on
|
||
# every dependabot PR. There is no OTA preview worth publishing for a
|
||
# dependency bump anyway.
|
||
#
|
||
# Gated on a clean fingerprint-native run: an OTA can only carry JS, so once
|
||
# the native surface changes the published bundle no longer represents the PR
|
||
# and installing it on a store/TestFlight client is misleading at best. Those
|
||
# PRs need a native build instead. A skipped or failed fingerprint job also
|
||
# skips this one - without a verdict we can't say the OTA is representative.
|
||
publish-pr-ota:
|
||
name: Publish PR OTA to denis
|
||
needs: fingerprint-native
|
||
runs-on: ubuntu-latest
|
||
if: >-
|
||
github.event_name == 'pull_request' &&
|
||
github.event.pull_request.head.repo.full_name == github.repository &&
|
||
github.event.pull_request.user.type != 'Bot' &&
|
||
needs.fingerprint-native.outputs.includes-changes != 'true'
|
||
concurrency:
|
||
group: pr-ota-${{ github.event.pull_request.number }}
|
||
cancel-in-progress: true
|
||
permissions:
|
||
id-token: write
|
||
contents: read
|
||
outputs:
|
||
release-version: ${{ steps.env.outputs.release-version }}
|
||
ios-build-number: ${{ steps.build-info.outputs.BSKY_IOS_BUILD_NUMBER }}
|
||
android-build-number: ${{ steps.build-info.outputs.BSKY_ANDROID_VERSION_CODE }}
|
||
steps:
|
||
- name: ⬇️ Checkout
|
||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||
with:
|
||
ref: ${{ github.event.pull_request.head.sha }}
|
||
|
||
- name: 🛠️ Setup Expo project
|
||
uses: ./.github/actions/setup-expo-project
|
||
with:
|
||
expo-token: ${{ secrets.EXPO_TOKEN }}
|
||
|
||
- name: 🔤 Compile translations
|
||
uses: ./.github/actions/compile-i18n
|
||
|
||
- name: ✏️ Write environment variables
|
||
id: env
|
||
uses: ./.github/actions/write-env
|
||
with:
|
||
env-token: ${{ secrets.ENV_TOKEN }}
|
||
sentry-dsn: ${{ secrets.SENTRY_DSN }}
|
||
bitdrift-api-key: ${{ secrets.BITDRIFT_API_KEY }}
|
||
gcp-project-id: ${{ secrets.EXPO_PUBLIC_GCP_PROJECT_ID }}
|
||
google-services-token: ${{ secrets.GOOGLE_SERVICES_TOKEN }}
|
||
expo-public-env: testflight
|
||
|
||
- name: 🏗️ Create Bundle
|
||
run: >
|
||
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }}
|
||
SENTRY_RELEASE=${{ steps.env.outputs.release-version }}
|
||
SENTRY_DIST=${{ steps.env.outputs.bundle-identifier }}
|
||
pnpm export
|
||
|
||
- name: ☁️ Configure AWS credentials (denis, PR-scoped)
|
||
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3
|
||
with:
|
||
role-to-assume: arn:aws:iam::007404326489:role/denis-ci-publish-pr
|
||
aws-region: us-east-2
|
||
# Defense-in-depth: the base role is already scoped to pr/*, but narrow
|
||
# this session further to just THIS PR's prefix so a bug can't write to
|
||
# another PR's objects or the prod tree.
|
||
inline-session-policy: |-
|
||
{
|
||
"Version": "2012-10-17",
|
||
"Statement": [
|
||
{
|
||
"Effect": "Allow",
|
||
"Action": ["s3:PutObject", "s3:DeleteObject"],
|
||
"Resource": "arn:aws:s3:::bsky-denis-ota-prod/pr/${{ github.event.pull_request.number }}/*"
|
||
},
|
||
{
|
||
"Effect": "Allow",
|
||
"Action": "s3:ListBucket",
|
||
"Resource": "arn:aws:s3:::bsky-denis-ota-prod",
|
||
"Condition": {
|
||
"StringLike": { "s3:prefix": "pr/${{ github.event.pull_request.number }}/*" }
|
||
}
|
||
}
|
||
]
|
||
}
|
||
|
||
- name: ⬇️ Setup denis CLI
|
||
uses: ./.github/actions/setup-denis
|
||
with:
|
||
release-tag: ${{ env.DENIS_RELEASE_TAG }}
|
||
app-id: ${{ vars.SYNC_INTERNAL_APP_ID }}
|
||
private-key: ${{ secrets.SYNC_INTERNAL_PK }}
|
||
|
||
- name: 🔢 Get native build numbers
|
||
id: build-info
|
||
run: bash scripts/setGitHubOutput.sh
|
||
|
||
- name: 🚀 Publish OTA to denis (S3)
|
||
run: pnpm use-build-number bash scripts/denisPublish.sh
|
||
env:
|
||
RUNTIME_VERSION: ''
|
||
CHANNEL_NAME: pull-request-${{ github.event.pull_request.number }}
|
||
# Pin the publish to the same values exposed in the install link.
|
||
BSKY_IOS_BUILD_NUMBER: ${{ steps.build-info.outputs.BSKY_IOS_BUILD_NUMBER }}
|
||
BSKY_ANDROID_VERSION_CODE: ${{ steps.build-info.outputs.BSKY_ANDROID_VERSION_CODE }}
|
||
|
||
comment-pr-ota:
|
||
name: Comment PR OTA install link
|
||
needs: publish-pr-ota
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
pull-requests: write
|
||
steps:
|
||
- name: 💬 Drop OTA install comment
|
||
uses: marocchino/sticky-pull-request-comment@5770ad5eb8f42dd2c4f34da00c94c5381e49af88 # v3.0.5
|
||
with:
|
||
header: pull-request-ota
|
||
message: |
|
||
The OTA deployment for this PR was successful! You may now apply it by either scanning the QR code or opening the deep link below in your browser:
|
||
|
||
<img src="https://bsky-qr.vercel.app?channel=pull-request-${{ github.event.pull_request.number }}&releaseVersion=${{ needs.publish-pr-ota.outputs.release-version }}&iosBuildNumber=${{ needs.publish-pr-ota.outputs.ios-build-number }}&androidBuildNumber=${{ needs.publish-pr-ota.outputs.android-build-number }}" width="300" height="300" alt="QR code for the PR OTA deployment">
|
||
|
||
`bluesky://intent/apply-ota?channel=pull-request-${{ github.event.pull_request.number }}&releaseVersion=${{ needs.publish-pr-ota.outputs.release-version }}&iosBuildNumber=${{ needs.publish-pr-ota.outputs.ios-build-number }}&androidBuildNumber=${{ needs.publish-pr-ota.outputs.android-build-number }}`
|