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
9 changes: 7 additions & 2 deletions .github/workflows/fork-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ jobs:
fi
echo "deploy=${deploy}" >>"${GITHUB_OUTPUT}"

dispatch_mobile_ota:
name: Dispatch Mobile OTA
dispatch_mobile_releases:
name: Dispatch Mobile Releases
needs: [check, test, mobile_native_static_analysis, release_smoke, deployment_scope]
if: |
github.event_name == 'workflow_dispatch' &&
Expand All @@ -209,3 +209,8 @@ jobs:
-f platform=all \
-f sha="$GITHUB_SHA" \
-f message="Integration ${GITHUB_SHA}"
gh workflow run mobile-eas-development.yml \
--repo "$GITHUB_REPOSITORY" \
--ref fork/integration \
-f platform=ios \
-f sha="$GITHUB_SHA"
119 changes: 119 additions & 0 deletions .github/workflows/mobile-eas-development.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
name: Mobile EAS Development

# Keep the installable development client current without rebuilding it for
# JavaScript-only changes. Expo Fingerprint reuses a compatible native build
# and publishes an OTA update; native changes produce a new internal build.
on:
workflow_dispatch:
inputs:
platform:
description: "Target platform"
required: true
type: choice
default: ios
options:
- ios
- android
- all
sha:
description: "Exact fork/integration SHA (blank uses its current tip)"
required: false
type: string

concurrency:
group: mobile-eas-development
cancel-in-progress: false

jobs:
development:
name: EAS Development
runs-on: ubuntu-24.04
permissions:
contents: read
env:
APP_VARIANT: development
NODE_OPTIONS: --max-old-space-size=8192
MOBILE_VERSION_POLICY: fingerprint
T3CODE_MOBILE_EAS_PROJECT_ID: ${{ vars.T3CODE_MOBILE_EAS_PROJECT_ID }}
T3CODE_MOBILE_EXPO_OWNER: ${{ vars.T3CODE_MOBILE_EXPO_OWNER }}
T3CODE_MOBILE_IOS_BUNDLE_IDENTIFIER: ${{ vars.T3CODE_MOBILE_IOS_BUNDLE_IDENTIFIER }}
T3CODE_MOBILE_IOS_TEAM_ID: ${{ vars.T3CODE_MOBILE_IOS_TEAM_ID }}
steps:
- id: expo-token
name: Check for EXPO_TOKEN
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
run: |
if [ -n "$EXPO_TOKEN" ]; then
echo "present=true" >> "$GITHUB_OUTPUT"
else
echo "present=false" >> "$GITHUB_OUTPUT"
echo "EXPO_TOKEN is not available; skipping EAS development job."
fi

- name: Checkout
if: steps.expo-token.outputs.present == 'true'
uses: actions/checkout@v6
with:
ref: fork/integration
fetch-depth: 0

- name: Resolve approved integration source
if: steps.expo-token.outputs.present == 'true'
env:
REQUESTED_SHA: ${{ inputs.sha }}
run: |
integration_sha="$(git rev-parse HEAD)"
target_sha="${REQUESTED_SHA:-$integration_sha}"
if [[ ! "$target_sha" =~ ^[0-9a-f]{40}$ ]]; then
echo "sha must be an exact 40-character commit SHA" >&2
exit 1
fi
git fetch origin "$target_sha"
git merge-base --is-ancestor "$target_sha" "$integration_sha"
git checkout --detach "$target_sha"
test "$(git rev-parse HEAD)" = "$target_sha"

- name: Setup Vite+
if: steps.expo-token.outputs.present == 'true'
uses: voidzero-dev/setup-vp@v1
with:
node-version-file: package.json
cache: true
run-install: true

- name: Expose pnpm
if: steps.expo-token.outputs.present == 'true'
run: |
pnpm_version="$(node --print "require('./package.json').packageManager.split('@').pop()")"
vp_pnpm_bin="$HOME/.vite-plus/package_manager/pnpm/$pnpm_version/pnpm/bin"
echo "$vp_pnpm_bin" >> "$GITHUB_PATH"
"$vp_pnpm_bin/pnpm" --version

- name: Setup EAS
if: steps.expo-token.outputs.present == 'true'
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}
packager: npm

- name: Pull development environment variables
if: steps.expo-token.outputs.present == 'true'
working-directory: apps/mobile
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
run: eas env:pull development --non-interactive

- name: Publish compatible update or development build
if: steps.expo-token.outputs.present == 'true'
uses: expo/expo-github-action/continuous-deploy-fingerprint@main
env:
EXPO_TOKEN: ${{ secrets.EXPO_TOKEN }}
with:
profile: development
branch: development
platform: ${{ inputs.platform }}
environment: development
working-directory: apps/mobile
github-token: ${{ secrets.GITHUB_TOKEN }}
1 change: 1 addition & 0 deletions apps/mobile/eas.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"NODE_OPTIONS": "--max-old-space-size=4096"
},
"channel": "development",
"environment": "development",
"developmentClient": true,
"distribution": "internal"
},
Expand Down
7 changes: 7 additions & 0 deletions docs/fork-stack.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ commit. Unknown paths are runtime-affecting by default. This preserves safe depl
contains mixed changes or a new source directory appears, while avoiding fleet rebuilds and mobile
OTA updates for tests, snapshots, documentation, agent instructions, and GitHub-only metadata.

Runtime-affecting integrations also publish both mobile release tracks from the exact tested SHA.
The production track receives an OTA update. The development track uses Expo Fingerprint: it
publishes an OTA update when a compatible development client already exists, or creates a new
internal iOS development build when native inputs changed. Manual runs of
`Mobile EAS Development` may target iOS, Android, or both; automatic integration publishing targets
iOS.

The manifest contains the permanent `fork/tim`, `fork/candidates`, and `fork/changes` PRs. The
synchronizer rebases that provenance chain onto the latest upstream `main` and rebuilds
`fork/integration`. Other open repository PRs are ignored. Temporary state is retained after a
Expand Down
Loading