Skip to content

Toolchain check (bun run doctor)

bun run doctor (scripts/doctor.js) checks the tools this project needs, prints one row per tool — status, the version found, the version expected — and, for every row that is not ok, the exact install command (Homebrew on macOS; Linux where it differs). It is also the first init step (--skip-doctor to skip). No network access except eas whoami.

CheckExpected (source)RequiredLane it unlocks
Bun>= 1.2.0bun.lock is a text lockfile (saveTextLockfile in bunfig.toml)yeseverything
Lockfilesonly bun.lock; warns on package-lock.json / yarn.lock / pnpm-lock.yamlBun-only installs
Nodemajor of .node-version (22); older is MISSING, newer only warnsyesscripts, lefthook, eas-cli
git>= 2.28yeseverything
lefthook hooks.git/hooks/pre-commit written by lefthook (bunx lefthook install, runs on prepare)pre-commit / commit-msg / pre-push hooks
EAS CLImajor of eas-cli in package.json, resolved via bun run eas --versionEAS build / update / workflows, env:pull
EAS loginbun run eas whoami --non-interactive succeeds, or EXPO_TOKEN is sete2e:build, env:pull, devices:*
GitHub CLI + authgh installed, gh auth status logged inrepo:settings:*
Maestro>= EXPECTED.maestroMin; CI pins EXPECTED.maestroPinned; ~/.maestro/bin oke2e:web, e2e:ios, e2e:android
Xcode>= 16.0 + at least one iOS simulator; macOS only (skip elsewhere)iOS lane
Android SDKANDROID_SDK_ROOT (or ANDROID_HOME) exists, adb and emulator resolvableAndroid lane
JavaJDK >= 17 — what the Maestro CLI and @expo/repack-app's build-tools needMaestro, Android repack

Version floors live once, in EXPECTED at the top of scripts/doctor.js — this table names the key rather than repeating the number. The Maestro pin (EXPECTED.maestroPinned) is the same value as maestro_version: in .eas/workflows/*.yml and MAESTRO_VERSION: in .github/workflows/ci.yml; the Maestro custom manager in .github/renovate.json5 bumps all three in one PR, so never edit one of them alone.

Statuses: ok, warn (missing / incompatible / logged out, but only an optional lane is affected), MISSING (a required tool — Bun, Node, git — is absent or incompatible), skip (not applicable on this platform, or depends on a row that is not ok).

Flags and exit codes:

InvocationExit 1 when
bun run doctora required tool is MISSING; warnings exit 0
bun run doctor --strictany MISSING or warn (CI, #56); skip never fails
bun run doctor --jsonsame rules; prints { rows, summary } instead of the table

An unknown flag exits 2 (the repo-wide usage code — docs/conventions.md → Scripts parse arguments and exit the same way).

The expected versions live in one EXPECTED constant at the top of scripts/doctor.js, each with the reason for the number; scripts/__tests__/doctor.test.ts drives every check with a fake run() so the tests never touch a real binary.