Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
3fa458a
build: create script for PGO builds
chrispader Aug 4, 2026
3295d38
build: configure PGO build
chrispader Aug 4, 2026
e1747b5
build: add `verify-instrumented` tool
chrispader Aug 4, 2026
96518ea
chore: update RN patch
chrispader Aug 4, 2026
27c7035
chore: add `.pgo` dir to `.gitignore`
chrispader Aug 4, 2026
e8d1104
fix: PGO merging
chrispader Aug 4, 2026
2b114fd
build: add `install-instrumented` command
chrispader Aug 4, 2026
9322dba
Update README.md
chrispader Aug 4, 2026
47f9d5b
Update android-local-proof.sh
chrispader Aug 4, 2026
6ba45d6
build: create record-startups command
chrispader Aug 4, 2026
62808e8
fix: prevent startup profile merge scope error
chrispader Aug 4, 2026
0da014a
build: add dedicated PGO build variants
chrispader Aug 4, 2026
ea4b7fb
Wait for native app-ready signal during PGO startup recording
chrispader Aug 4, 2026
151f4f1
chore: improve script
chrispader Aug 4, 2026
1921255
chore: add PGO benchmarking suite
chrispader Aug 4, 2026
13df28a
build: add dedicated PGO build variants
chrispader Aug 4, 2026
f906d1a
Merge branch '@chrispader/perf/pgo' into @chrispader/perf/pgo-startup…
chrispader Aug 4, 2026
7bf73a0
revert: separate build variants for PGO
chrispader Aug 4, 2026
4ce4248
chore: move apk outputs to `.pgo` folder
chrispader Aug 4, 2026
c267adb
chore: archive release build
chrispader Aug 4, 2026
43425ff
chore: copy generated release file instead of moving it
chrispader Aug 4, 2026
aa96634
chore: extend PGO benchmark suite
chrispader Aug 4, 2026
dfceb96
chore: reformat benchmark output
chrispader Aug 4, 2026
e25759f
refactor: migrate script to TypeScript
chrispader Aug 5, 2026
04f20ae
refactor: use CLI tool in the PGO tool
chrispader Aug 5, 2026
7f56cfb
Merge branch 'main' into @chrispader/perf/pgo
chrispader Aug 5, 2026
c68accc
feat: implement iOS PGO
chrispader Aug 5, 2026
fa69456
fix: ios optimized build
chrispader Aug 5, 2026
5ddd179
fix: benchmark release first
chrispader Aug 5, 2026
22a19cd
feat: allow defining device
chrispader Aug 6, 2026
d35b7f5
Update README.md
chrispader Aug 6, 2026
b143c0f
perf: merge main and refresh native PGO workflow
chrispader Sep 21, 2026
9befe34
chore: allow native PGO terminology in spelling checks
chrispader Sep 21, 2026
169f09f
docs: clarify PGO scenario isolation and artifact matching
chrispader Sep 21, 2026
66a65c1
fix: support Xcode 27 benchmark process discovery
chrispader Sep 21, 2026
f44192d
chore: merge latest main into PGO work
chrispader Sep 21, 2026
d94d768
docs: clarify iOS PGO profile coverage
chrispader Sep 21, 2026
2beedd0
Merge remote-tracking branch 'origin/main' into @chrispader/perf/pgo
chrispader Sep 22, 2026
72c97bd
Merge remote-tracking branch 'origin/main' into @chrispader/perf/pgo
chrispader Sep 22, 2026
c4bd27d
refactor: share PGO script utilities
chrispader Sep 22, 2026
6aa1031
fix: allow partial iOS PGO profiles
chrispader Sep 23, 2026
f6978f1
perf: automate native PGO journeys
chrispader Sep 23, 2026
a9cae54
fix: match iOS PGO tab status labels
chrispader Sep 23, 2026
394ac25
fix: complete iOS PGO journey on physical device
chrispader Sep 23, 2026
b7b4105
fix(pgo): handle mobile navigation tree edge cases
chrispader Sep 23, 2026
9ea4140
perf: prepare release CI for native PGO training
chrispader Sep 23, 2026
7673df9
perf(pgo): benchmark short signed-in journeys
chrispader Sep 24, 2026
21f2d6c
perf(pgo): add heavy report benchmark
chrispader Sep 24, 2026
e83fa48
fix(pgo): reset heavy benchmark navigation between runs
chrispader Sep 25, 2026
d0cfd6a
fix(pgo): wait for navigation after Android relaunch
chrispader Sep 25, 2026
89be151
chore(pgo): merge benchmark updates into CI branch
chrispader Sep 28, 2026
f9b823c
chore(pgo): merge main into PGO branch
chrispader Sep 28, 2026
6924908
chore(pgo): merge updated PGO base into CI branch
chrispader Sep 28, 2026
796b32d
ci(pgo): benchmark release builds on trusted PR pushes
chrispader Sep 28, 2026
1e4edf5
chore(pgo): merge main for iOS verification
chrispader Oct 12, 2026
e2cf575
chore(pgo): merge latest App main
chrispader Oct 12, 2026
329077c
chore(pgo): merge latest main into previous CI branch
chrispader Oct 12, 2026
437eff0
chore(pgo): merge latest main into CI preview
chrispader Oct 12, 2026
13943b7
chore(pgo): refresh latest App main
chrispader Oct 12, 2026
f902797
chore(pgo): refresh main in previous CI
chrispader Oct 12, 2026
15cc8b8
chore(pgo): refresh main in CI preview
chrispader Oct 12, 2026
69a8d67
fix(pgo): align iOS IR profiling and Rock builds
chrispader Oct 12, 2026
5d3f3d6
chore(pgo): sync iOS foundation into legacy CI
chrispader Oct 12, 2026
f2c96cc
fix(pgo): validate iOS IR profiles and release proof IDs
chrispader Oct 12, 2026
aa79a6b
fix(ci): verify sealed iOS PGO proof before artifact upload
chrispader Oct 12, 2026
01c6b96
chore(pgo): sync verified iOS fixes into CI preview
chrispader Oct 12, 2026
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
87 changes: 84 additions & 3 deletions .github/workflows/buildAndroid.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ on:
description: Force a full native build, bypassing Rock remote cache
type: string
default: 'false'
run-pgo:
description: Train and use a native PGO profile for a Release build
type: boolean
default: false
benchmark-pgo:
description: Benchmark paired local builds after the PGO Release build
type: boolean
default: false

outputs:
VERSION_CODE:
Expand All @@ -51,7 +59,7 @@ on:
jobs:
build:
name: Build Android app
runs-on: blacksmith-32vcpu-ubuntu-2404
runs-on: ${{ inputs.run-pgo && 'pgo-android' || 'blacksmith-32vcpu-ubuntu-2404' }}
env:
PULL_REQUEST_NUMBER: ${{ inputs.pull-request-number }}
outputs:
Expand All @@ -62,6 +70,12 @@ jobs:
SOURCEMAP_FILENAME: index.android.bundle.map
PROGUARD_MAPPING_FILENAME: mapping.txt
steps:
- name: Validate PGO build inputs
if: ${{ (inputs.run-pgo && inputs.variant != 'Release') || (inputs.benchmark-pgo && !inputs.run-pgo) }}
run: |
echo '::error::PGO training requires a Release build, and benchmarking requires PGO training.'
exit 1

- name: Checkout
uses: useblacksmith/checkout@0647fdbab2614a5eb86d2971070e325060d91056 # v1.7.0
with:
Expand Down Expand Up @@ -160,13 +174,40 @@ jobs:
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1

- name: Train Android PGO on the signed-in physical device
if: ${{ inputs.run-pgo }}
env:
SENTRY_DISABLE_AUTO_UPLOAD: 'true'
SENTRY_DISABLE_NATIVE_DEBUG_UPLOAD: 'true'
run: |
set -euo pipefail
: "${PGO_ANDROID_DEVICE_ID:?Set PGO_ANDROID_DEVICE_ID on the device runner}"
: "${PGO_JOURNEY_FIXTURE:?Set PGO_JOURNEY_FIXTURE to an owner-approved heavy-account fixture}"
test -s "$PGO_JOURNEY_FIXTURE"
command -v agent-device
test "$(agent-device --version)" = '0.20.6'
test -d "${ANDROID_NDK_HOME:?Set ANDROID_NDK_HOME to the pinned NDK}"
./node_modules/.bin/bun scripts/pgo/pgo.ts android build-instrumented --device "$PGO_ANDROID_DEVICE_ID"
./node_modules/.bin/bun scripts/pgo/pgo.ts android verify-instrumented --device "$PGO_ANDROID_DEVICE_ID"
./node_modules/.bin/bun scripts/pgo/pgo.ts android install-instrumented --device "$PGO_ANDROID_DEVICE_ID"
batch_id="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
./node_modules/.bin/bun scripts/pgo/journey.ts android --fixture "$PGO_JOURNEY_FIXTURE" --device "$PGO_ANDROID_DEVICE_ID" --preflight --batch-id "${batch_id}-preflight"
./node_modules/.bin/bun scripts/pgo/journey.ts android --fixture "$PGO_JOURNEY_FIXTURE" --device "$PGO_ANDROID_DEVICE_ID" --runs 3 --collect --batch-id "$batch_id"

- name: Validate Android training profile
id: preparePgo
if: ${{ inputs.run-pgo }}
run: ./node_modules/.bin/bun scripts/pgo/ciProfile.ts prepare android "${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"

- name: Rock Remote Build - Android
uses: callstackincubator/android@4cedf4d9b5c167452c96fe67233577e0fde9a025
env:
GITHUB_TOKEN: ${{ github.token }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
IS_HYBRID_APP: true
FORCE_NATIVE_BUILD: ${{ inputs.force-native-build == 'true' && github.run_id || '' }}
FORCE_NATIVE_BUILD: ${{ inputs.run-pgo && format('{0}-{1}', github.run_id, github.run_attempt) || inputs.force-native-build == 'true' && github.run_id || '' }}
EXPENSIFY_PGO_MODE: ${{ inputs.run-pgo && 'use' || 'off' }}
PGO_PROFILE_SHA256: ${{ steps.preparePgo.outputs.PROFILE_SHA256 }}
with:
variant: ${{ inputs.variant }}
aab: ${{ inputs.variant == 'Release' }}
Expand All @@ -182,7 +223,7 @@ jobs:
keystore-key-password: ${{ steps.load-credentials.outputs.ANDROID_UPLOAD_KEY_PASSWORD }}
keystore-path: '../tools/buildtools/upload-key.keystore'
comment-bot: false
rock-build-extra-params: ${{ inputs.variant == 'Adhoc' && '--extra-params "-PreactNativeArchitectures=arm64-v8a,x86_64 --profile"' || '--extra-params "--profile"' }}
rock-build-extra-params: ${{ inputs.run-pgo && format('--local --extra-params "-PpgoMode=use -PpgoProfile={0} -PpatchedArtifacts.forceBuildFromSource=true --no-build-cache --rerun-tasks --profile"', steps.preparePgo.outputs.PROFILE_PATH) || inputs.variant == 'Adhoc' && '--extra-params "-PreactNativeArchitectures=arm64-v8a,x86_64 --profile"' || '--extra-params "--profile"' }}
custom-identifier: ${{ steps.computeIdentifier.outputs.IDENTIFIER }}
validate-elf-alignment: ${{ inputs.variant != 'Release' && 'true' || 'false' }}

Expand Down Expand Up @@ -251,6 +292,18 @@ jobs:
name: ${{ inputs.artifact-prefix }}androidBuild-artifact
path: /tmp/android-artifacts/*.aab

- name: Seal Android PGO release proof
if: ${{ inputs.run-pgo }}
run: ./node_modules/.bin/bun scripts/pgo/ciProfile.ts seal android "$(jq -r .version package.json)" "/tmp/android-artifacts/${{ steps.collectArtifacts.outputs.AAB_FILENAME }}" /tmp/android-artifacts/pgo-proof-android.json

- name: Upload Android PGO release proof
if: ${{ inputs.run-pgo }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: ${{ inputs.artifact-prefix }}pgo-proof-android
path: /tmp/android-artifacts/pgo-proof-android.json
if-no-files-found: error

- name: Upload Android sourcemap artifact
# v6
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
Expand Down Expand Up @@ -303,3 +356,31 @@ jobs:
with:
name: ${{ inputs.artifact-prefix }}android-apk-artifact
path: Expensify.apk

- name: Benchmark paired Android PGO builds
if: ${{ inputs.benchmark-pgo }}
env:
SENTRY_DISABLE_AUTO_UPLOAD: 'true'
SENTRY_DISABLE_NATIVE_DEBUG_UPLOAD: 'true'
run: |
set -euo pipefail
scripts/pgo/pgo.ts android build-release --device "$PGO_ANDROID_DEVICE_ID"
scripts/pgo/pgo.ts android build-optimized --device "$PGO_ANDROID_DEVICE_ID" --profile "${{ steps.preparePgo.outputs.PROFILE_PATH }}"
for order in release-first optimized-first; do
batch="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${order}"
scripts/pgo/pgo.ts android benchmark-journey 10 30 --device "$PGO_ANDROID_DEVICE_ID" --fixture "$PGO_JOURNEY_FIXTURE" --benchmark-order "$order" --benchmark-batch "$batch"
scripts/pgo/pgo.ts android benchmark-heavy-journey 10 30 --device "$PGO_ANDROID_DEVICE_ID" --fixture "$PGO_JOURNEY_FIXTURE" --benchmark-order "$order" --benchmark-batch "$batch"
done
for manifest in .pgo/android/benchmarks/*/"${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"-*/run.json; do
jq '{platform, runs, order, scenario, sourceRevision, mobileRevision, artifactSHA256}' "$manifest" > "${manifest%.json}-proof.json"
done

- name: Upload Android PGO benchmark samples
if: ${{ inputs.benchmark-pgo }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: ${{ inputs.artifact-prefix }}pgo-benchmarks-android
path: |
.pgo/android/benchmarks/*/${{ github.run_id }}-${{ github.run_attempt }}-*/*.csv
.pgo/android/benchmarks/*/${{ github.run_id }}-${{ github.run_attempt }}-*/run-proof.json
if-no-files-found: error
111 changes: 108 additions & 3 deletions .github/workflows/buildIOS.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ on:
description: Force a full native build, bypassing Rock remote cache
type: string
default: 'false'
run-pgo:
description: Train and use a native PGO profile for a Release build
type: boolean
default: false
benchmark-pgo:
description: Benchmark paired local builds after the PGO Release build
type: boolean
default: false
download-dsyms:
description: Download the prebuilt react-native dSYMs so Sentry can symbolicate its native frames. Off by default - it adds two large downloads per build.
type: boolean
Expand All @@ -52,7 +60,7 @@ on:
jobs:
build:
name: Build iOS HybridApp
runs-on: blacksmith-12vcpu-macos-latest
runs-on: ${{ inputs.run-pgo && 'pgo-ios' || 'blacksmith-12vcpu-macos-latest' }}
env:
PULL_REQUEST_NUMBER: ${{ inputs.pull-request-number }}
RCT_SYMBOLICATE_PREBUILT_FRAMEWORKS: ${{ inputs.download-dsyms && '1' || '0' }}
Expand All @@ -63,6 +71,12 @@ jobs:
DSYM_FILENAME: ${{ steps.set-ipa-filename.outputs.DSYM_FILENAME }}
SOURCEMAP_FILENAME: main.jsbundle.map
steps:
- name: Validate PGO build inputs
if: ${{ (inputs.run-pgo && inputs.variant != 'Release') || (inputs.benchmark-pgo && !inputs.run-pgo) }}
run: |
echo '::error::PGO training requires a Release build, and benchmarking requires PGO training.'
exit 1

- name: Checkout
# Upstream checkout on macOS - Blacksmith's git-mirror cache can't work there (see workflows/README.md)
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Expand Down Expand Up @@ -118,6 +132,7 @@ jobs:
run: bundle install

- name: Resolve react-native artifact version
if: ${{ inputs.variant != 'Release' }}
id: resolve-rn-artifacts
env:
GITHUB_TOKEN: ${{ github.token }}
Expand All @@ -131,13 +146,15 @@ jobs:
echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT"

- name: Cache react-native artifacts
if: ${{ inputs.variant != 'Release' }}
# v5.0.1
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb
with:
path: ~/Library/Caches/Expensify/react-native-artifacts
key: ${{ runner.os }}-rn-artifacts-react-hybrid-${{ steps.resolve-rn-artifacts.outputs.VERSION }}-dsyms-${{ inputs.download-dsyms }}

- name: Cache Pod dependencies
if: ${{ inputs.variant != 'Release' }}
# v5.0.1
uses: actions/cache@9255dc7a253b0ccc959486e2bca901246202afeb
id: pods-cache
Expand All @@ -151,7 +168,7 @@ jobs:

- name: Install cocoapods
uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 # v4
if: steps.pods-cache.outputs.cache-hit != 'true' || steps.compare-podfile-and-manifest.outputs.IS_PODFILE_SAME_AS_MANIFEST != 'true' || steps.setup-node.outputs.cache-hit != 'true'
if: ${{ inputs.variant != 'Release' && (steps.pods-cache.outputs.cache-hit != 'true' || steps.compare-podfile-and-manifest.outputs.IS_PODFILE_SAME_AS_MANIFEST != 'true' || steps.setup-node.outputs.cache-hit != 'true') }}
with:
timeout_minutes: 10
max_attempts: 5
Expand Down Expand Up @@ -241,6 +258,48 @@ jobs:
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1

- name: Train iOS PGO on the signed-in physical device
if: ${{ inputs.run-pgo }}
env:
SENTRY_DISABLE_AUTO_UPLOAD: 'true'
SENTRY_DISABLE_NATIVE_DEBUG_UPLOAD: 'true'
run: |
set -euo pipefail
: "${PGO_IOS_DEVICE_ID:?Set PGO_IOS_DEVICE_ID on the device runner}"
: "${IOS_DEVELOPMENT_TEAM:?Install development signing and set IOS_DEVELOPMENT_TEAM on the runner}"
: "${PGO_JOURNEY_FIXTURE:?Set PGO_JOURNEY_FIXTURE to an owner-approved heavy-account fixture}"
test -s "$PGO_JOURNEY_FIXTURE"
command -v agent-device
test "$(agent-device --version)" = '0.20.6'
./node_modules/.bin/bun scripts/pgo/pgo.ts ios build-instrumented --device "$PGO_IOS_DEVICE_ID"
./node_modules/.bin/bun scripts/pgo/pgo.ts ios verify-instrumented --device "$PGO_IOS_DEVICE_ID"
./node_modules/.bin/bun scripts/pgo/pgo.ts ios install-instrumented --device "$PGO_IOS_DEVICE_ID"
batch_id="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
./node_modules/.bin/bun scripts/pgo/journey.ts ios --fixture "$PGO_JOURNEY_FIXTURE" --device "$PGO_IOS_DEVICE_ID" --preflight --batch-id "${batch_id}-preflight"
./node_modules/.bin/bun scripts/pgo/journey.ts ios --fixture "$PGO_JOURNEY_FIXTURE" --device "$PGO_IOS_DEVICE_ID" --runs 3 --collect --batch-id "$batch_id"

- name: Validate iOS training profile
id: preparePgo
if: ${{ inputs.run-pgo }}
run: ./node_modules/.bin/bun scripts/pgo/ciProfile.ts prepare ios "${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"

- name: Prepare source pods for the optimized archive
if: ${{ inputs.run-pgo }}
run: |
set -euo pipefail
profile="${GITHUB_WORKSPACE}/.pgo/ios/arm64/newdot.profdata"
{
echo 'BUILD_RN_FROM_SOURCE=1'
echo 'RCT_USE_RN_DEP=0'
echo 'RCT_USE_PREBUILT_RNCORE=0'
echo 'RCT_BUILD_HERMES_FROM_SOURCE=true'
echo 'EXPENSIFY_PGO_MODE=use'
echo "EXPENSIFY_PGO_PROFILE=$profile"
} >> "$GITHUB_ENV"
cd Mobile-Expensify/iOS
BUILD_RN_FROM_SOURCE=1 RCT_USE_RN_DEP=0 RCT_USE_PREBUILT_RNCORE=0 RCT_BUILD_HERMES_FROM_SOURCE=true EXPENSIFY_PGO_MODE=use EXPENSIFY_PGO_PROFILE="$profile" bundle exec pod install --silent
rm -rf Pods/hermes-engine/build

- name: Prepare provisioning profiles JSON
id: prepare-profiles
run: |
Expand All @@ -257,7 +316,8 @@ jobs:
GITHUB_TOKEN: ${{ github.token }}
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
IS_HYBRID_APP: true
FORCE_NATIVE_BUILD: ${{ inputs.force-native-build == 'true' && github.run_id || '' }}
FORCE_NATIVE_BUILD: ${{ inputs.run-pgo && format('{0}-{1}', github.run_id, github.run_attempt) || inputs.force-native-build == 'true' && github.run_id || '' }}
PGO_PROFILE_SHA256: ${{ steps.preparePgo.outputs.PROFILE_SHA256 }}
with:
destination: device
re-sign: true
Expand All @@ -268,6 +328,7 @@ jobs:
provisioning-profiles: ${{ steps.prepare-profiles.outputs.PROFILES }}
comment-bot: false
custom-identifier: ${{ steps.computeIdentifier.outputs.IDENTIFIER }}
rock-build-extra-params: ${{ inputs.run-pgo && format('--local --extra-params "-xcconfig {0}/.pgo/ios/arm64/release.xcconfig"', github.workspace) || '' }}

- name: Set artifact URL output
id: set-artifact-url
Expand All @@ -287,6 +348,22 @@ jobs:
} >> "$GITHUB_OUTPUT"
fi

- name: Seal iOS PGO release proof
if: ${{ inputs.run-pgo }}
run: ./node_modules/.bin/bun scripts/pgo/ciProfile.ts seal ios "$(jq -r .version package.json)" ".rock/cache/ios/export/${{ steps.set-ipa-filename.outputs.IPA_FILENAME }}" pgo-proof-ios.json

- name: Verify sealed iOS PGO release proof
if: ${{ inputs.variant == 'Release' }}
run: ./node_modules/.bin/bun scripts/pgo/ciProfile.ts verify ios "$(jq -r .version package.json)" pgo-proof-ios.json ".rock/cache/ios/export/${{ steps.set-ipa-filename.outputs.IPA_FILENAME }}"

- name: Upload iOS PGO release proof
if: ${{ inputs.run-pgo }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: ${{ inputs.artifact-prefix }}pgo-proof-ios
path: pgo-proof-ios.json
if-no-files-found: error

- name: Find and upload IPA artifact
# v6
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f
Expand Down Expand Up @@ -332,3 +409,31 @@ jobs:
with:
name: ${{ inputs.artifact-prefix }}ios-sourcemap-artifact
path: Mobile-Expensify/main.jsbundle.map

- name: Benchmark paired iOS PGO builds
if: ${{ inputs.benchmark-pgo }}
env:
SENTRY_DISABLE_AUTO_UPLOAD: 'true'
SENTRY_DISABLE_NATIVE_DEBUG_UPLOAD: 'true'
run: |
set -euo pipefail
scripts/pgo/pgo.ts ios build-release --device "$PGO_IOS_DEVICE_ID"
scripts/pgo/pgo.ts ios build-optimized --device "$PGO_IOS_DEVICE_ID" --profile "${{ steps.preparePgo.outputs.PROFILE_PATH }}"
for order in release-first optimized-first; do
batch="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-${order}"
scripts/pgo/pgo.ts ios benchmark-journey 10 30 --device "$PGO_IOS_DEVICE_ID" --fixture "$PGO_JOURNEY_FIXTURE" --benchmark-order "$order" --benchmark-batch "$batch"
scripts/pgo/pgo.ts ios benchmark-heavy-journey 10 30 --device "$PGO_IOS_DEVICE_ID" --fixture "$PGO_JOURNEY_FIXTURE" --benchmark-order "$order" --benchmark-batch "$batch"
done
for manifest in .pgo/ios/benchmarks/*/"${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"-*/run.json; do
jq '{platform, runs, order, scenario, sourceRevision, mobileRevision, artifactSHA256}' "$manifest" > "${manifest%.json}-proof.json"
done

- name: Upload iOS PGO benchmark samples
if: ${{ inputs.benchmark-pgo }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: ${{ inputs.artifact-prefix }}pgo-benchmarks-ios
path: |
.pgo/ios/benchmarks/*/${{ github.run_id }}-${{ github.run_attempt }}-*/*.csv
.pgo/ios/benchmarks/*/${{ github.run_id }}-${{ github.run_attempt }}-*/run-proof.json
if-no-files-found: error
2 changes: 2 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ jobs:
with:
ref: ${{ needs.prep.outputs.DEPLOY_SHA }}
variant: Release
run-pgo: true
secrets: inherit

androidUploadGooglePlay:
Expand Down Expand Up @@ -434,6 +435,7 @@ jobs:
with:
ref: ${{ needs.prep.outputs.DEPLOY_SHA }}
variant: Release
run-pgo: true
download-dsyms: true
secrets: inherit

Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/pgoPreview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Verify PGO release build and benchmarks

on:
pull_request:
types: [opened, synchronize, reopened]
branches: [main]

permissions:
contents: read

concurrency:
group: pgo-preview-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
# The signed-in device and release credentials are available only to a reviewed branch in Expensify/App.
android:
name: Train, build, and benchmark Android PGO
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == '@chrispader/perf/pgo-ci-preview' }}
cache-mode: read
uses: ./.github/workflows/buildAndroid.yml
with:
ref: ${{ github.event.pull_request.head.sha }}
variant: Release
run-pgo: true
benchmark-pgo: true
artifact-prefix: preview-
secrets: inherit

ios:
name: Train, build, and benchmark iOS PGO
if: ${{ github.event.pull_request.head.repo.full_name == github.repository && github.event.pull_request.head.ref == '@chrispader/perf/pgo-ci-preview' }}
cache-mode: read
uses: ./.github/workflows/buildIOS.yml
with:
ref: ${{ github.event.pull_request.head.sha }}
variant: Release
run-pgo: true
benchmark-pgo: true
artifact-prefix: preview-
download-dsyms: true
secrets: inherit
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ config/certificatePinning/roots/
# Rspack build profiling output (RSPACK_PROFILE=OVERVIEW|ALL)
.rspack-profile-*/

# Local native PGO profiles
.pgo/

# Expo
.expo
dist/
Expand Down
Loading
Loading