Skip to content

ci(eas): remote build numbers; build iOS and Android in separate jobs - #191

Open
objectstack-fleet[bot] wants to merge 1 commit into
mainfrom
claude/issue-186-eas-config
Open

objectstack-fleet[bot] wants to merge 1 commit into
mainfrom
claude/issue-186-eas-config

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Refs #186. Part of #186: rulings 2 and 3 of batch #3 land here. Ruling 1 (SENTRY_AUTH_TOKEN in the EAS environment) is a maintainer action with no code, so the merge leaves the card open.

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: 构建号(iOS buildNumber / Android versionCode)改由 EAS 服务器保管(eas.json 的 cli.appVersionSource: "remote");eas-build.yml 拆成 iOS、Android 两个独立任务。
  • 为什么改: 以前每次推送 main 先建 iOS,iOS 因缺 Apple 签名失败就把 Android 一起跳过(run 41603160327)。现在推送 main 只花一次 Android 构建额度,iOS 只在手动触发时构建,失败也拖不住 Android。
  • 风险与回滚: 只动 CI 配置、eas.json 和文档,App 代码不变;回滚即 revert 本 PR。构建号首次远程构建从 1 起(production 首次为 2),此后只增不减。
  • 先看: .github/workflows/eas-build.yml 两个 job 的 if: 行(决定谁在推送时跑);eas.json;docs/DEPLOYMENT.md。
  • 两分钟验证: 合并后打开 main 上第一次 EAS Build 运行:应只见 EAS Build Android (preview) 在跑、EAS Build iOS 显示 skipped;Android 日志里出现 versionCode 以 1 初始化的提示,且不再有 cli.appVersionSource 未设置的警告。
  • 需要你做的: 在 EAS 项目环境变量中设置 SENTRY_AUTH_TOKEN(裁决第 1 项,本 PR 不含),然后合并本 PR(workflow 是受管面,只能由你合并)。

What changes

  • eas.json: cli.appVersionSource: "remote". EAS servers own the iOS buildNumber and the Android versionCode; production keeps autoIncrement: true, which now increments the remote number.
  • .github/workflows/eas-build.yml: one job per platform, so one platform failing cannot skip the other.
    • android runs on every push to main (profile preview) and on workflow_dispatch with platform android or all.
    • ios runs on workflow_dispatch with platform ios or all only, until the EAS project has Apple signing credentials. A comment above its if: spells the one replacement line that also builds iOS on push.
    • Kept unchanged: the triggers and both inputs, the profiles, the permissions block, and the EXPO_TOKEN env mirror with a step-level guard in each job, so each job stays a green no-op without the secret.
  • docs/DEPLOYMENT.md: where build numbers live and how to read or set them, and what eas-build.yml builds on push.
  • .changeset/186-eas-config.md: patch, as the earlier EAS-config changeset (eas-project-link.md) was.

Not changed:

  • app.config.ts (nothing in it is dead): neither it nor app.json sets ios.buildNumber or android.versionCode.
  • docs/DEVICE-TESTING.md (still true): it says to open the latest preview Android build, which push still makes, and to run iOS builds by hand.
  • eas-update.yml and eas-submit.yml (no dependency): neither has a needs: or workflow_run on eas-build.yml.

How the remote build number starts (measured in eas-cli 27.5.0)

build/build/android/version.js (resolveRemoteVersionCodeAsync) and its iOS twin:

  • With no remote version yet, eas-cli takes the local one. This repo is a managed project (/android and /ios are gitignored), so the local value comes from the app config: getVersionCode(exp) is android.versionCode ?? 1, and getBuildNumber(exp) is ios.buildNumber or '1'.
  • Because a local value always exists, the "Remote versions are not configured … Use eas build:version:set" error cannot fire here, so no one-time seeding command is needed.
  • A preview build (no autoIncrement) initializes the remote number at 1 and reuses the latest number afterwards. A production build increments it, to 2 on the first run.
  • validateAppConfigForRemoteVersionSource rejects only runtimeVersion.policy: "nativeVersion". This app uses fingerprint, so it passes.

eas build:version:set is needed only if a store already holds a higher build number than EAS knows. The Android build in #185 was a preview APK, not a store upload.

Tests

The if: expressions walked by hand, with EXPO_TOKEN set:

Trigger ios job android job
push to main skipped (event_name is push) runs, profile preview
dispatch, platform all runs runs
dispatch, platform ios runs skipped
dispatch, platform android skipped runs

Without EXPO_TOKEN, every step in a job that runs is skipped, so the job is a green no-op as before.

Gate readings at dd9500a
  • node -e 'JSON.parse(…eas.json…)': exit 0.
  • yaml.safe_load(eas-build.yml): exit 0, jobs ['ios', 'android'].
  • actionlint 1.7.12 (PyPI actionlint-py):
    • eas-build.yml: exit 0.
    • All five workflows: exit 0.
    • Positive control: a scratch copy with secrets.EXPO_TOKEN in a job-level if: exits 1 with context "secrets" is not allowed here.
  • prettier --check eas.json .github/workflows/eas-build.yml: exit 0.
  • npx jest --ci __tests__/lib/app-config.test.ts (the config test): 3/3 passed, exit 0.

app.config.ts is unchanged, so tsc, ESLint and the full Jest suite are left to lint-typecheck.yml and ci.yml on this PR.

Acceptance notes

The card's Acceptance needs real EAS runs, so all three items are NOT MEASURED here:

  • "production reaches a signed artifact": waits on ruling 1 (SENTRY_AUTH_TOKEN).
  • "the appVersionSource warning is gone": visible in the first build after the merge.
  • "a push does not lose Android to iOS": visible in the first push run after the merge.

Verification

After the merge, open the first EAS Build run on main. Expect:

  • EAS Build Android (preview) runs.
  • EAS Build iOS (preview) shows as skipped.
  • The Android log shows No remote versions are configured for this project, versionCode will be initialized based on the value from the local project. and then Initialized versionCode with 1.
  • No The field "cli.appVersionSource" is not set warning.

Generated by Claude Code

eas.json sets cli.appVersionSource to remote, so EAS servers own iOS
buildNumber and Android versionCode and production's autoIncrement bumps
them there. eas-build.yml splits the platforms into two jobs: a push to
main builds Android only, and iOS runs on workflow_dispatch until Apple
signing credentials exist. DEPLOYMENT.md says what the push builds and
where build numbers live.

Claude-Session: https://claude.ai/code/session_01QSeKYnW1Xf6HDSWz1eGAqv
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv

Seat verdict: ACCEPT — Head-sha: dd9500afab4b8bdf2328a7375abdef34d4f4f42f

维护者速读

  • 改了什么: 构建号(iOS 的 buildNumber、安卓的 versionCode)改由 EAS 服务器保管;eas-build.yml 拆成 iOS、安卓两个独立任务。App 代码没动。
  • 为什么改: 以前每次合入 main 先建 iOS,iOS 缺 Apple 签名一失败,安卓也跟着被跳过,等于一次包都出不来。现在每次合入只建安卓,只花一份构建额度;iOS 改为手动触发,失败也拖不住安卓。
  • 风险与回滚: 只改了 CI 配置、eas.json 和文档,回滚就是 revert 本 PR。构建号从 1 开始,production 首次出包是 2,以后只增不减。要是应用商店里已经有更大的号,要用 eas build:version:set 调高(目前没有:feat(eas): link the app to the EAS project so CI can build it #185 出的是预览 APK,没上架)。
  • 先看: eas-build.yml 两个任务各自的 if: 行,它决定合入时谁跑。
  • 两分钟验证: 合入后打开 main 上第一次 EAS Build:EAS Build Android (preview) 在跑,EAS Build iOS 显示 skipped;安卓日志里有 versionCode 以 1 初始化的提示,不再有 cli.appVersionSource 未设置的警告。
  • 需要你做的: 审批并合并本 PR(workflow 归你合并)。另外,在 EAS 环境变量里配 SENTRY_AUTH_TOKEN(裁决第 1 项),不用等合并,第一次出正式包之前配好就行。

席位意见: 改动小,两项 CI 全绿。dev 用 actionlint 检查了全部 workflow,并加了一个必须报错的对照,确认工具真在查;四种触发方式都逐条推演过。dev 关于远程构建号如何起步的说法,席位对照 app.config.ts 核过。建议合并。合并后 #186 不关,转为等你配 Sentry 令牌。

席位核对

  1. Shape: draft → main. The first line is Refs #186, so the merge does not close the card (ruling 1 remains). Clause-②: no and Metadata-protocol: none are present. Patch changeset .changeset/186-eas-config.md. Four files, +70 −8, all inside claim 6755712894's surface.
  2. Premise, measured: on main at 8e4235f, eas.json had no cli.appVersionSource and eas-build.yml built both platforms as steps of one job. The seat re-read the dev's version-source claims: app.config.ts and app.json set neither ios.buildNumber nor android.versionCode, and runtimeVersion is { policy: "fingerprint" } (app.config.ts:54), not nativeVersion. So remote seeding takes the default 1 and no seeding command is owed.
  3. Gates: check-runs on head dd9500a: Lint & Type Check success, Code Quality success. mergeable_state is clean. Dev readings: actionlint exit 0 on all five workflows, positive control exit 1; JSON and YAML parse 0; prettier 0; the config test 3/3. The seat walked the job if: lines and agrees with the PR's table: push → Android only; dispatch all → both; ios → iOS; android → Android. The card's Acceptance needs real EAS runs and is NOT MEASURED.
  4. Accepted deviation: docs/DEPLOYMENT.md now names eas-build.yml in two CI bullets: the new one says what a push builds, and the old one says the three EAS workflows need EXPO_TOKEN. Both are true, so this is a nit, not a blocker. The PR's first line carries both Refs #186 and Part of #186; neither closes the card.
  5. Out of scope: none.
  6. Landing: the PR changes .github/workflows/**, and seat landing is suspended (EXPO_TOKEN is set; see [PM seat] repo:mobile — 🟢 os-justin #160). The seat marks the PR ready and requests review, and does NOT enable auto-merge. After the merge, the seat moves EAS: production builds fail on Sentry's upload, appVersionSource is unset, and an iOS failure stops the Android build on main #186 to pm:awaiting-maintainer with Maintainer-action: for ruling 1.

@objectstack-fleet objectstack-fleet Bot added the needs-user-decision Blocked on a maintainer decision — do not dispatch; decision cards only, never on a PR label Nov 10, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 10, 2026 01:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-user-decision Blocked on a maintainer decision — do not dispatch; decision cards only, never on a PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant