|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Rebuild and submit the production store app on EAS when its fingerprint changes. |
| 3 | +# |
| 4 | +# 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. |
| 20 | +# |
| 21 | +# Usage: production-build.sh <ios|android> |
| 22 | +# Env: EXPO_TOKEN EAS auth (build + submit scope) |
| 23 | +# AWS_PREVIEW_BUCKET the couchers-dev-assets bucket (holds the markers) |
| 24 | +# 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). |
| 26 | +set -euo pipefail |
| 27 | + |
| 28 | +PLATFORM="${1:?usage: production-build.sh <ios|android>}" |
| 29 | +case "$PLATFORM" in |
| 30 | + ios | android) ;; |
| 31 | + *) |
| 32 | + echo "platform must be ios or android (got $PLATFORM)" >&2 |
| 33 | + exit 1 |
| 34 | + ;; |
| 35 | +esac |
| 36 | + |
| 37 | +MARKER="s3://${AWS_PREVIEW_BUCKET}/production-builds/${PLATFORM}.fingerprint" |
| 38 | + |
| 39 | +# Read a single JSON field from stdin with node (no jq in the node:22 image). |
| 40 | +json_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"; } |
| 41 | + |
| 42 | +# Fingerprint of the working tree — the same value EAS stamps as runtimeVersion |
| 43 | +# and the OTA publish job bakes into the manifest. APP_VARIANT=production (set by |
| 44 | +# the job) selects the production bundle id / scheme / signed updates config. |
| 45 | +CURRENT="$(npx expo-updates fingerprint:generate --platform "$PLATFORM" 2>/dev/null | json_field '.hash')" |
| 46 | +echo "current $PLATFORM fingerprint: $CURRENT" |
| 47 | + |
| 48 | +PREVIOUS="$(aws s3 cp "$MARKER" - 2>/dev/null || true)" |
| 49 | +echo "last-built $PLATFORM fingerprint: ${PREVIOUS:-<none>}" |
| 50 | + |
| 51 | +if [ -n "$PREVIOUS" ] && [ "$PREVIOUS" = "$CURRENT" ]; then |
| 52 | + echo "Fingerprint unchanged for $PLATFORM — the live production build still matches, skipping." |
| 53 | + exit 0 |
| 54 | +fi |
| 55 | + |
| 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." |
0 commit comments