Skip to content

doctor names each missing prerequisite and how to install it #211

Description

@V3RON

Scope

Simlock needs tools it does not install: Xcode for iOS, and for Android a JDK and the SDK's cmdline-tools, emulator and platform-tools packages. When one is missing, the platform is unavailable and the operator gets at most Android SDK missing or incomplete; searched: <paths> in the daemon log. Nothing says which part is missing or how to get it.

After this task simlock doctor names each missing prerequisite, per platform, with the command or step that installs it. It checks the machine again on every run. Simlock still installs none of them.

Checked for iOS:

  • Xcode is installed and selected.
  • The Xcode license is accepted.
  • Xcode's first-launch setup is done.

Checked for Android:

  • An Android SDK exists at one of the searched paths. When none does, that is one finding, not one per tool.
  • cmdline-tools (sdkmanager and avdmanager).
  • emulator.
  • platform-tools.
  • A JDK that the SDK tools can run on.

Technical spec

File references are to main at b5f2222. Find code by symbol name.

Modules touched

  • src/core/driver.ts: two new types, both opaque to the core.
    • MissingPrerequisite { prerequisite: string; message: string; remedy: string }. prerequisite is a short kebab-case id the driver module owns. message says what is missing. remedy is the command or step that fixes it.
    • PrerequisiteCheck { platform: Platform; check(): Promise<readonly MissingPrerequisite[]> }. Read-only: it never installs, downloads, or writes. Every process it starts is bounded. It rejects when it cannot tell.
  • src/drivers/ios/prerequisites.ts (new): iosPrerequisites({ processRunner }) returns a PrerequisiteCheck. Each command has a 15-second timeout.
    1. xcodebuild -version. A non-zero exit whose stderr matches requires Xcode, or a missing executable: xcode, with the remedy "install Xcode, then sudo xcode-select -s /Applications/Xcode.app". Stop here; the next two need Xcode.
    2. xcodebuild -license check. Non-zero exit: xcode-license, remedy sudo xcodebuild -license accept.
    3. xcodebuild -checkFirstLaunchStatus. Non-zero exit: xcode-first-launch, remedy sudo xcodebuild -runFirstLaunch.
      A timeout, or a failure of step 1 that is not "no Xcode", rejects.
  • src/drivers/android/prerequisites.ts (new): androidPrerequisites({ env, homeDirectory, filesystem, processRunner }) returns a PrerequisiteCheck. It uses the same root list and the same per-tool path functions as discoverSdk and sdkPathsAt; move those into a shared file in the driver module so one function decides where a tool is (architecture rule 10).
    1. No searched root exists as a directory: one finding android-sdk, whose message lists the searched paths and whose remedy names Android Studio or the command-line tools download, and ANDROID_HOME. Stop here.
    2. Otherwise take the first root that exists, and report each of:
      • android-cmdline-tools when no complete tool bin is found. Remedy: the command-line tools download page at developer.android.com. sdkmanager cannot install itself.
      • android-emulator when <root>/emulator/emulator is missing. Remedy: sdkmanager --install emulator.
      • android-platform-tools when <root>/platform-tools/adb is missing. Remedy: sdkmanager --install platform-tools.
      • android-jdk when cmdline-tools are there and sdkmanager --version exits non-zero. Timeout 30 seconds. The message carries the first line of stderr. Remedy: install a JDK and set JAVA_HOME. A timeout rejects; it is not reported as missing.
  • src/core/doctor.ts:
    • DoctorFinding gains { kind: "prerequisite-missing"; platform; prerequisite; message; remedy }.
    • DoctorOptions gains prerequisiteChecks?: readonly PrerequisiteCheck[] and runningPlatforms?: () => readonly Platform[].
    • DoctorReconcileOptions gains prerequisites?: boolean, default false. Startup convergence leaves it off. doctor.run sets it.
    • When on, run every check. Map each result to a finding. A check that rejects is logged and yields nothing, as advisories() does.
    • A platform whose check returns nothing, that is not in runningPlatforms(), and that has no entry in driverRejections: one finding with prerequisite: "daemon-restart", saying the prerequisites are present but the platform was not started, and the remedy from DRIVER_RETRY_REMEDY.
    • A finding for a platform that is not running gets that same restart sentence appended to its remedy.
    • --fix never acts on the kind. It is left out of doctor.reconciled's driftFindings, like driver-advisory.
  • src/contract/schemas.ts: doctorFindingSchema gains the new member, field for field.
  • src/daemon/main.ts: build the checks in the composition root: iOS only when hostPlatform is darwin, Android always. Pass them, and the list of platforms that have a driver, to Doctor. With SIMLOCK_DRIVERS_MODULE set, use the module's optional prerequisiteChecks export, or none.
  • src/daemon/dispatcher.ts: the doctor.run handler passes prerequisites: true.
  • src/cli/index.ts: next to writeDriverAdvisoryWarnings, print one stderr line per finding: Missing [<platform>] <prerequisite>: <message> <remedy>. Stdout keeps the JSON report unchanged in shape. The exit code stays 0.
  • e2e/fake-driver/: the module exports prerequisiteChecks, read from the driver script, so a flow can script a missing prerequisite.
  • Docs: docs/CLI.md (a "Prerequisites" list per platform that says Simlock does not install them; the new finding kind under doctor), docs/CLIENT.md if it lists finding kinds, docs/internal/ARCHITECTURE.md (where the checks live and why they are not on the driver object).

Contract and event changes

  • doctor.run's output gains the finding kind prerequisite-missing. No protocol change: doctor.run is refused on a gateway and never relayed.
  • No event change. doctor.reconciled does not carry the new kind.

Rules in play

  • architecture.md rule 1 and 2 (the core carries prerequisite, message and remedy unread; every tool name and path stays in the driver module), rule 3 (a new driver gets checks by exporting one, with no core change), rule 9 (processes and files through ProcessRunner and Filesystem), rule 10 (one function locates an SDK tool, for discovery and for the check), rule 11 (every command bounded).
  • safety.md rule 4: a check never installs or downloads, and --fix never does either.
  • documentation.md rule 3: a remedy names a command or a web page, never a file in this repo.
  • testing.md rules 1 to 4.

Tests

  • With every prerequisite present and both platforms running, doctor.run reports no prerequisite-missing finding.
  • The Android check reports android-emulator with the sdkmanager --install emulator remedy when only the emulator binary is missing.
  • The Android check reports android-platform-tools when only adb is missing.
  • The Android check reports android-cmdline-tools when no tool bin is complete, and does not run sdkmanager.
  • The Android check reports one android-sdk finding that lists the searched paths when no root exists, and no per-tool finding.
  • The Android check reports android-jdk with the first stderr line when sdkmanager --version fails.
  • The Android check rejects when sdkmanager --version times out, and reports nothing as missing.
  • The iOS check reports xcode when xcodebuild says it requires Xcode, and runs neither later command.
  • The iOS check reports xcode-license when the license check exits non-zero.
  • The iOS check reports xcode-first-launch when the first-launch check exits non-zero.
  • Neither check starts a process other than the commands named above.
  • A check that rejects yields no finding and does not fail doctor.run.
  • On a host that is not macOS, no iOS check is built.
  • A platform with nothing missing and no running driver gets one daemon-restart finding.
  • A platform with a driver rejection gets no daemon-restart finding.
  • A finding for a platform that is not running tells the operator to restart the daemon.
  • doctor.run with fix: true reports the same prerequisite findings and starts no installer.
  • Startup convergence runs no prerequisite check.
  • doctor.reconciled does not carry a prerequisite-missing finding.
  • The CLI prints one stderr line per prerequisite finding and exits 0.

Done when

  • On a machine whose SDK has no emulator package, simlock doctor says the Android emulator package is missing and prints the sdkmanager command that installs it.
  • With no cmdline-tools, it says so and points at Android's command-line tools download, since sdkmanager cannot install itself.
  • With no Android SDK at any searched path, it reports one finding that lists the paths.
  • With no Xcode selected, it says so and names xcode-select.
  • With every prerequisite present, it reports none of these findings.
  • After a missing package is installed, the next simlock doctor no longer reports it, without a daemon restart.
  • The findings are in simlock doctor --json.
  • doctor --fix never installs a prerequisite.
  • A platform that cannot run on the host, such as iOS off macOS, reports no missing prerequisite.
  • docs/CLI.md lists the prerequisites per platform and says Simlock does not install them.
  • e2e with the fake driver: a scripted missing prerequisite shows in simlock doctor output and on stderr.
  • pnpm check is green.

Out of scope

  • Installing any prerequisite.
  • Choosing between several installed Xcode versions.
  • Hardware acceleration checks on Linux hosts.
  • Android SDK licenses. A download that meets an unaccepted license already fails with a message that names the config key and the command.
  • Reporting prerequisites in status or in a gateway's worker view.

Depends on

Approval

  • Approved for delivery

Written by an agent.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

task:readyAn agent may implement it.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions