11#! /usr/bin/env bash
2- # Rebuild and submit the production store app on EAS when its fingerprint changes.
2+ # Build the production store app on EAS when its fingerprint changes (build phase).
3+ # Submitting the build to the stores happens separately in production-submit.sh
4+ # (deploy phase). We emit the EAS build id and the fingerprint to a dotenv artifact
5+ # so the submit job knows what to ship and which fingerprint to record on success.
36#
47# A signed OTA only applies to an installed build whose runtimeVersion (the Expo
5- # fingerprint) matches it. So when a native change moves the production
6- # fingerprint — a new native module, an app.config.js change, or an Expo SDK bump
7- # — we need a fresh store build carrying it before an OTA on that fingerprint can
8- # land; pure JS/TS changes ship over the air. We record the last-built fingerprint
9- # per platform in S3 and compare; on a change we build and auto-submit, recording
10- # the marker only once that succeeds so a failed build is retried.
11- #
12- # iOS: eas build --auto-submit -> App Store Connect. The build lands in
13- # TestFlight and as an available build; it is NOT submitted for App
14- # Store review and no release notes are changed — do that by hand.
15- # Android: eas build --auto-submit -> the Play *internal* testing track (the
16- # `production` submit profile sets track: internal). Promote to the
17- # public production track by hand in the Play Console.
18- #
19- # Neither platform makes anything public on its own.
8+ # fingerprint) matches it, so when a native change moves the production fingerprint
9+ # we cut a fresh store build; pure JS/TS changes ship over the air. We compare the
10+ # fingerprint against the last-submitted marker in S3 and skip the build (leaving
11+ # EAS_BUILD_ID empty) when it is unchanged.
2012#
2113# Usage: production-build.sh <ios|android>
22- # Env: EXPO_TOKEN EAS auth (build + submit scope)
14+ # Env: EXPO_TOKEN EAS auth (build scope)
2315# AWS_PREVIEW_BUCKET the couchers-dev-assets bucket (holds the markers)
2416# plus the AWS creds the aws CLI reads from the environment
25- # Run from app/mobile (eas.json, the project, and node_modules must be present ).
17+ # Run from app/mobile. Writes native-build-<platform>.env (a dotenv artifact ).
2618set -euo pipefail
2719
2820PLATFORM=" ${1:? usage: production-build.sh <ios|android>} "
@@ -35,6 +27,7 @@ case "$PLATFORM" in
3527esac
3628
3729MARKER=" s3://${AWS_PREVIEW_BUCKET} /production-builds/${PLATFORM} .fingerprint"
30+ DOTENV=" native-build-${PLATFORM} .env"
3831
3932# Read a single JSON field from stdin with node (no jq in the node:22 image).
4033json_field () { node -e ' let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{const j=JSON.parse(s);process.stdout.write(String(eval("j"+process.argv[1])??""))})' " $1 " ; }
@@ -43,22 +36,26 @@ json_field() { node -e 'let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{
4336# and the OTA publish job bakes into the manifest. APP_VARIANT=production (set by
4437# the job) selects the production bundle id / scheme / signed updates config.
4538CURRENT=" $( npx expo-updates fingerprint:generate --platform " $PLATFORM " 2> /dev/null | json_field ' .hash' ) "
46- echo " current $PLATFORM fingerprint: $CURRENT "
39+ echo " current $PLATFORM fingerprint: $CURRENT "
40+
41+ # The submit job records the marker once it succeeds, so hand it the fingerprint.
42+ echo " EAS_FINGERPRINT=$CURRENT " > " $DOTENV "
4743
4844PREVIOUS=" $( aws s3 cp " $MARKER " - 2> /dev/null || true) "
49- echo " last-built $PLATFORM fingerprint: ${PREVIOUS:- <none>} "
45+ echo " last-submitted $PLATFORM fingerprint: ${PREVIOUS:- <none>} "
5046
5147if [ -n " $PREVIOUS " ] && [ " $PREVIOUS " = " $CURRENT " ]; then
52- echo " Fingerprint unchanged for $PLATFORM — the live production build still matches, skipping."
48+ echo " Fingerprint unchanged for $PLATFORM — the live production build still matches, skipping build."
49+ echo " EAS_BUILD_ID=" >> " $DOTENV "
5350 exit 0
5451fi
5552
56- echo " Fingerprint changed for $PLATFORM — building and submitting a new production app."
57- # --auto-submit uses the submit profile named like the build profile (production):
58- # iOS -> App Store Connect, Android -> the internal track.
59- eas build --platform " $PLATFORM " --profile production --auto-submit --non-interactive
60-
61- # Record the new fingerprint only after a successful build+submit, so a failure is
62- # retried on the next pipeline instead of being marked done.
63- printf ' %s ' " $CURRENT " | aws s3 cp - " $MARKER "
64- echo " Recorded $PLATFORM fingerprint $CURRENT — production build is up to date. "
53+ echo " Fingerprint changed for $PLATFORM — building a new production app on EAS ."
54+ BUILD_JSON= " $( eas build --platform " $PLATFORM " -- profile production --non-interactive --json ) "
55+ BUILD_ID= " $( printf ' %s ' " $BUILD_JSON " | json_field ' [0].id ' ) "
56+ [ -n " $BUILD_ID " ] || {
57+ echo " could not determine the EAS build id from the build output " >&2
58+ exit 1
59+ }
60+ echo " EAS_BUILD_ID= $BUILD_ID " >> " $DOTENV "
61+ echo " Built $PLATFORM production app: $BUILD_ID "
0 commit comments