Verify the stable API and exact Capacitor iOS CORS contract before signing device-debug IPAs, with regression coverage and troubleshooting guidance.
401 lines
18 KiB
Markdown
401 lines
18 KiB
Markdown
# Cable-Connected iPhone Debug IPA Runbook
|
|
|
|
This runbook covers development-signed iOS builds installed from an Ubuntu
|
|
workstation over USB. It is separate from the public App Store release path in
|
|
`docs/app-store-release.md`.
|
|
|
|
The device build is deliberately a second app:
|
|
|
|
- Debug bundle ID: `io.truckwash.app.debug`
|
|
- Debug display name: `Truck Wash Debug`
|
|
- Production bundle ID: `io.truckwash.app`
|
|
- Capacitor/Android app ID: `io.truckwash.twa`
|
|
- API: `https://api-v2.truckwash.io/master/api`
|
|
|
|
Installing or uninstalling the debug app must not replace or remove the
|
|
production app. The debug workflow builds the Vue application in production
|
|
mode against the stable API; it does not use Vite's development `/api` default
|
|
or a live-reload server.
|
|
|
|
## What Ubuntu Can And Cannot Do
|
|
|
|
The current Ubuntu workstation already has `usbmuxd`, the libimobiledevice
|
|
utilities, and `ideviceinstaller`. The current iPhone has previously been
|
|
trusted and paired. Run the repository doctor before every install because the
|
|
phone can still be locked, trust can be reset, or Developer Mode can be off.
|
|
|
|
This workflow supports:
|
|
|
|
- Inspecting pairing, activation, lock, Developer Mode, and install-service
|
|
readiness.
|
|
- Installing and upgrading a valid development-signed IPA.
|
|
- Reading filtered device syslog and copying crash reports.
|
|
|
|
Apple does not provide Xcode, LLDB device debugging, or Safari Web Inspector on
|
|
Linux. `CAPACITOR_DEBUG` and `get-task-allow` make the IPA suitable for a
|
|
development device, but they do not create an official Linux LLDB or WebKit
|
|
debugger. Use a physical Mac with Xcode/Safari for breakpoints or Web Inspector.
|
|
Use TestFlight or a physical Mac if a new or beta iOS release is incompatible
|
|
with libimobiledevice; never weaken device security or signing validation as a
|
|
workaround.
|
|
|
|
## One-Time iPhone Preparation
|
|
|
|
1. Connect the iPhone directly with a data-capable USB cable.
|
|
2. Unlock the phone and keep it awake. Tap **Trust** if iOS asks whether to
|
|
trust this computer, then enter the device passcode.
|
|
3. On iOS 16 or newer, open **Settings -> Privacy & Security -> Developer
|
|
Mode**, turn Developer Mode on, and accept the restart. iOS 15 does not have
|
|
this setting and the helper does not require it there.
|
|
4. After the restart, unlock the phone, confirm **Turn On** in the Developer
|
|
Mode prompt, and enter the passcode again.
|
|
5. Reconnect the cable and run the doctor described below.
|
|
|
|
Developer Mode is an iOS security control and cannot be bypassed from Ubuntu.
|
|
If the Developer Mode setting is absent, connect the phone once to a physical
|
|
Mac and use Apple's supported Xcode or Apple Configurator device preparation,
|
|
then return to Ubuntu after the phone has restarted and Developer Mode is on.
|
|
|
|
Trust, pairing, and Developer Mode can be cleared by device resets, iOS updates,
|
|
or privacy/location resets. Repeat these steps if the doctor reports that the
|
|
previously working device is no longer ready.
|
|
|
|
## Apple Developer Setup
|
|
|
|
This requires the paid Truck Wash ApS Apple Developer team and a user permitted
|
|
to manage certificates, identifiers, and devices.
|
|
|
|
### Register the device and debug App ID
|
|
|
|
1. Connect and unlock the iPhone, then get its UDID locally with
|
|
`idevice_id -l`. Treat the full UDID as sensitive operational data: do not
|
|
commit it or paste it into ordinary build logs.
|
|
2. In Apple Developer **Certificates, Identifiers & Profiles -> Devices**, add
|
|
the iPhone using that UDID.
|
|
3. Under **Identifiers**, create an explicit App ID for
|
|
`io.truckwash.app.debug`.
|
|
4. Enable only capabilities required by the current Xcode project. Do not copy
|
|
unrelated production entitlements into the debug App ID.
|
|
|
|
### Create the certificate and development profile
|
|
|
|
1. Create a dedicated **Apple Development** certificate for CI device-debug
|
|
signing. Keep its private key under the team's normal credential controls.
|
|
2. Export the certificate and private key together as a password-protected
|
|
`.p12` file.
|
|
3. Create an **iOS App Development** provisioning profile that selects:
|
|
- App ID `io.truckwash.app.debug`
|
|
- The dedicated Apple Development certificate
|
|
- Every approved physical test iPhone, including the cable-connected device
|
|
4. Download the `.mobileprovision` file.
|
|
5. Confirm the profile has not expired, includes the intended device UDIDs, and
|
|
grants `get-task-allow=true`. An App Store or ad-hoc profile is not valid for
|
|
this workflow.
|
|
|
|
Base64-encode both files without line wrapping before adding them to GitHub. On
|
|
Ubuntu, for example:
|
|
|
|
```sh
|
|
base64 -w 0 TruckWashDebug.p12 > TruckWashDebug.p12.base64
|
|
base64 -w 0 TruckWashDebug.mobileprovision > TruckWashDebug.mobileprovision.base64
|
|
```
|
|
|
|
Store the encoded values in GitHub immediately, verify one successful build,
|
|
then securely remove the local `.p12`, profile, encoded copies, CSR, and any
|
|
other private-key intermediates that are no longer required. Never commit
|
|
signing files or their encoded contents.
|
|
|
|
## GitHub Environment And Dispatch Approval
|
|
|
|
Create a repository environment named `mobile-device-debug`. Store the debug
|
|
signing configuration only in that environment. Required environment reviewers
|
|
are not available for this private repository's current GitHub plan, so the
|
|
manual `workflow_dispatch` inputs are the signing approval boundary.
|
|
|
|
Restrict the environment's custom deployment branches to the exact `master`
|
|
branch. The checked-in workflow also refuses any other workflow ref. This keeps
|
|
signing secrets behind the reviewed workflow on `master`, while `source_ref`
|
|
can still select a separately inspected same-repository commit to build.
|
|
|
|
Add these environment variables exactly:
|
|
|
|
- `APPLE_TEAM_ID`
|
|
- `IOS_DEBUG_BUNDLE_ID=io.truckwash.app.debug`
|
|
- `IOS_DEBUG_API_URL=https://api-v2.truckwash.io/master/api`
|
|
|
|
Add these environment secrets exactly:
|
|
|
|
- `IOS_DEBUG_CERTIFICATE_BASE64`: base64 of the password-protected `.p12`
|
|
- `IOS_DEBUG_CERTIFICATE_PASSWORD`: password used to export the `.p12`
|
|
- `IOS_DEBUG_PROVISION_PROFILE_BASE64`: base64 of the development
|
|
`.mobileprovision`
|
|
- `IOS_DEBUG_ALLOWED_UDIDS`: newline-delimited UDIDs for every device that the
|
|
profile is expected to contain
|
|
|
|
The workflow generates and masks a new random password for its temporary macOS
|
|
keychain on every run. Do not create or store an
|
|
`IOS_DEBUG_KEYCHAIN_PASSWORD` secret.
|
|
|
|
Do not reuse the `mobile-store-production` distribution secrets. The debug job
|
|
must use an Apple Development certificate and iOS App Development profile; the
|
|
existing `io.truckwash.app` App Store workflow remains unchanged.
|
|
|
|
The person dispatching a run must inspect the intended commit first. Do not
|
|
dispatch when:
|
|
|
|
- The exact 40-character SHA is not the branch, tag, or commit intended.
|
|
- The source comes from a fork or another repository.
|
|
- The requested change is not appropriate to sign for a physical device.
|
|
- The signing profile is expired or no longer covers the intended device.
|
|
|
|
The workflow independently resolves `source_ref` inside this repository and
|
|
requires it to equal `expected_sha`. It also requires the exact typed
|
|
confirmation `SIGN IOS DEBUG IPA`. A missing/mismatched SHA or confirmation
|
|
stops the unprivileged resolver before the environment signing secrets are used.
|
|
|
|
## Build And Download A Debug IPA
|
|
|
|
1. Open **Actions -> iOS Device Debug IPA -> Run workflow** and keep **Use
|
|
workflow from** set to `master`.
|
|
2. Inspect the intended commit and copy its complete 40-character SHA.
|
|
3. Enter `source_ref`. It may be a branch, tag, or commit in this repository and
|
|
defaults to `master`.
|
|
4. Enter the complete SHA as `expected_sha` and enter the exact confirmation
|
|
`SIGN IOS DEBUG IPA`. Submitting these inputs is approval to sign that source.
|
|
5. The resolver pins `source_ref` inside this repository and verifies it equals
|
|
`expected_sha`. A mismatch stops the run before signing.
|
|
6. Wait for the signed macOS job to finish. It builds a Debug archive against
|
|
`https://api-v2.truckwash.io/master/api`, exports it with method
|
|
`development`, validates the embedded profile and app identity, and never
|
|
uploads the result to App Store Connect.
|
|
7. Download the `truck-wash-debug-<version>-<12-character-SHA>` GitHub Actions
|
|
artifact for the run. Keep its same-prefix `.ipa`, `.dSYM.zip`,
|
|
`manifest.json`, and `SHA256SUMS` together in one directory.
|
|
8. From that directory, verify the download before connecting it to a device:
|
|
|
|
```sh
|
|
sha256sum --check SHA256SUMS
|
|
```
|
|
|
|
Do not install an artifact after a checksum failure. The manifest records the
|
|
source ref and SHA, build/run numbers, bundle identity, stable API, minimum iOS,
|
|
Xcode/Capacitor versions, signing method, and provisioning-profile expiration.
|
|
It intentionally does not contain device UDIDs or secrets.
|
|
|
|
Artifacts are retained for seven days. Keep the zipped dSYM with any crash
|
|
report from that build so a Mac/Xcode crash-symbolication path remains
|
|
available.
|
|
|
|
## Ubuntu Device Commands
|
|
|
|
Run commands from the repository root. The npm interface is:
|
|
|
|
```sh
|
|
npm run mobile:ios:device -- <command>
|
|
```
|
|
|
|
If the npm wrapper is unavailable, use the equivalent direct entrypoint:
|
|
|
|
```sh
|
|
node scripts/mobile/ios-device.mjs <command>
|
|
```
|
|
|
|
The helper uses USB devices only. With one connected iPhone, omit `--udid`.
|
|
With multiple devices connected, provide `--udid ID`; the command fails instead
|
|
of guessing. Normal output redacts full UDIDs.
|
|
|
|
### Check readiness
|
|
|
|
Unlock the phone and run:
|
|
|
|
```sh
|
|
npm run mobile:ios:device -- doctor
|
|
```
|
|
|
|
The doctor verifies required host commands, USB discovery, pairing, activation,
|
|
unlocked state, Developer Mode, and installation-proxy access. Resolve every
|
|
reported failure before attempting an install.
|
|
|
|
For a specific connected device:
|
|
|
|
```sh
|
|
npm run mobile:ios:device -- doctor --udid DEVICE_UDID
|
|
```
|
|
|
|
### Install or upgrade
|
|
|
|
Keep the downloaded artifact files together and run:
|
|
|
|
```sh
|
|
npm run mobile:ios:device -- install ./truck-wash-debug-VERSION-SHA.ipa --manifest ./manifest.json
|
|
```
|
|
|
|
The helper requires and verifies `SHA256SUMS` and the complete workflow
|
|
manifest, then inspects the IPA and its embedded profile. It rejects missing or
|
|
mismatched artifact metadata, the wrong repository/source/API/bundle/executable,
|
|
an App Store/ad-hoc or expired profile, `get-task-allow=false`, a profile
|
|
missing the connected UDID, or an invalid app payload before calling
|
|
`ideviceinstaller`.
|
|
|
|
If `io.truckwash.app.debug` is absent, the helper installs it. If it is already
|
|
present, the helper upgrades it and confirms the resulting version/build on the
|
|
phone. It never uninstalls or replaces `io.truckwash.app`.
|
|
|
|
Launch **Truck Wash Debug** manually from the iPhone Home Screen. Keep the phone
|
|
online for the first launch so iOS can perform Apple's PPQ validation for the
|
|
provisioning profile. A firewall, DNS filter, VPN, or captive portal that
|
|
blocks Apple's validation service can prevent a correctly signed development
|
|
app from opening.
|
|
|
|
### Collect filtered logs
|
|
|
|
Start logging, then reproduce the issue on the phone:
|
|
|
|
```sh
|
|
npm run mobile:ios:device -- logs
|
|
npm run mobile:ios:device -- logs --output ./truck-wash-debug.log
|
|
```
|
|
|
|
The debug executable is deliberately named `TruckWashDebug`, distinct from the
|
|
production executable. The helper verifies that exact name and filters
|
|
`idevicesyslog` output for it. It streams child-tool output through UDID
|
|
redaction; `--output` files are written by the helper with mode `0600` after
|
|
redaction. Logs should make it possible to correlate the app with its source SHA
|
|
and stable API target without exposing signing secrets or full device IDs.
|
|
|
|
### Copy crash reports
|
|
|
|
Create a destination directory and copy reports from the phone:
|
|
|
|
```sh
|
|
mkdir -p ./ios-crashes
|
|
npm run mobile:ios:device -- crashes ./ios-crashes
|
|
```
|
|
|
|
Crash retrieval always keeps the original reports on the iPhone. Preserve the
|
|
matching IPA manifest and dSYM with each report.
|
|
|
|
### Remove only the debug app
|
|
|
|
Uninstall requires the exact debug bundle ID as typed confirmation:
|
|
|
|
```sh
|
|
npm run mobile:ios:device -- uninstall --confirm io.truckwash.app.debug
|
|
```
|
|
|
|
The helper refuses to remove the production bundle or any other bundle ID.
|
|
|
|
## Adding Devices And Renewing Signing
|
|
|
|
A provisioning profile is a snapshot. Registering another iPhone in Apple
|
|
Developer does not update an already downloaded profile.
|
|
|
|
When adding a device:
|
|
|
|
1. Obtain its UDID locally and register it in the Apple Developer portal.
|
|
2. Regenerate the `io.truckwash.app.debug` iOS App Development profile with the
|
|
new and existing approved devices selected.
|
|
3. Replace `IOS_DEBUG_PROVISION_PROFILE_BASE64`.
|
|
4. Add the UDID to the newline-delimited `IOS_DEBUG_ALLOWED_UDIDS` secret.
|
|
5. Dispatch a new build; an existing IPA does not gain access to the new device.
|
|
|
|
Monitor the profile expiration recorded in each artifact manifest and the Apple
|
|
Development certificate expiration in the portal. Before either expires,
|
|
create/renew the signing material, regenerate the profile, replace the affected
|
|
GitHub secrets, and prove the result with a new build and real-device install.
|
|
Revoked or expired signing material invalidates later installation and can stop
|
|
an already installed development build from launching.
|
|
|
|
## Troubleshooting
|
|
|
|
### No device, device locked, or installation proxy unavailable
|
|
|
|
- Use a direct data-capable cable and avoid an unreliable hub.
|
|
- Unlock the iPhone, keep its screen awake, reconnect it, and rerun `doctor`.
|
|
- Close other tools that may be exclusively interacting with the device.
|
|
- Do not repeatedly retry installation while the doctor reports a lock/service
|
|
failure.
|
|
|
|
### Pairing or trust failure
|
|
|
|
- Unlock the phone and accept the Trust prompt.
|
|
- If no prompt appears and `doctor` reports invalid pairing, use the explicit
|
|
repair guidance printed by the helper, reconnect, and confirm trust again.
|
|
- Device privacy resets and some iOS updates require a new trust decision.
|
|
|
|
### Developer Mode is disabled or absent on iOS 16 or newer
|
|
|
|
- Enable it under **Settings -> Privacy & Security -> Developer Mode**, restart,
|
|
and confirm after the reboot.
|
|
- If the switch is absent, use a Mac with Xcode or Apple Configurator for
|
|
Apple's supported one-time preparation. There is no Ubuntu bypass.
|
|
|
|
### IPA, profile, certificate, or UDID mismatch
|
|
|
|
- Confirm the IPA is from **iOS Device Debug IPA**, not **Mobile Store Artifacts**.
|
|
- Check `manifest.json` for `io.truckwash.app.debug`, development signing, the
|
|
intended source SHA, and a future profile expiration.
|
|
- Regenerate the development profile when a device was added, a certificate was
|
|
replaced, or the profile expired; then replace the GitHub secret and rebuild.
|
|
- Never suppress the helper's profile, entitlement, bundle, or checksum checks.
|
|
|
|
### App installs but will not launch
|
|
|
|
- Keep the phone online for Apple's initial PPQ validation.
|
|
- Check whether VPN, DNS, firewall, captive-portal, or device-management policy
|
|
blocks Apple developer-app verification.
|
|
- Confirm Developer Mode is still on and the certificate/profile has not expired
|
|
or been revoked.
|
|
- Collect syslog and crash reports before reinstalling so evidence is preserved.
|
|
|
|
### App reports `No response was received`
|
|
|
|
- Keep the API base set to `https://api-v2.truckwash.io/master/api`. The bare
|
|
`https://api-v2.truckwash.io` host is the public gateway, not the application
|
|
API base.
|
|
- Confirm `https://api-v2.truckwash.io/master/api/ping` responds before
|
|
investigating the device or app.
|
|
- Capacitor serves bundled iOS content from `capacitor://localhost`. The stable
|
|
API must return `Access-Control-Allow-Origin: capacitor://localhost` for that
|
|
exact origin, including authenticated preflight requests.
|
|
- The signing workflow checks API reachability and this CORS contract before
|
|
compiling or signing. If it fails, deploy the backend CORS policy fix before
|
|
dispatching another IPA; do not replace the API URL or use an unsupported
|
|
HTTP/HTTPS `iosScheme` workaround.
|
|
- An already-built IPA starts using a corrected server-side CORS policy without
|
|
modification. Build and install a higher version when recording a verified
|
|
device-test result for the fix.
|
|
|
|
### iOS beta or new major iOS version breaks device tools
|
|
|
|
- Record the device model, exact iOS version, helper error, source SHA, and IPA
|
|
checksum.
|
|
- Update libimobiledevice only through a trusted package/source and rerun the
|
|
doctor. Do not install arbitrary device images or disable signing checks.
|
|
- If compatibility remains broken, distribute through TestFlight or install and
|
|
debug from a physical Mac with a compatible Xcode version.
|
|
|
|
## Real-Device Acceptance Checklist
|
|
|
|
For the first setup, after signing changes, and after major iOS upgrades:
|
|
|
|
- `doctor` passes while the phone is unlocked.
|
|
- `SHA256SUMS` verifies and the manifest identifies the intended immutable SHA.
|
|
- **Truck Wash Debug** installs as `io.truckwash.app.debug` while the production
|
|
app and its data remain unchanged.
|
|
- Authentication, camera/QR permission, and foreground-location behavior work.
|
|
- Logs show the expected build/source context and stable API target.
|
|
- The debug app still launches and reaches the API after the cable is removed.
|
|
- A higher-numbered IPA upgrades the debug app without clearing its local state.
|
|
- Crash reports are copied without being deleted from the phone.
|
|
- The test record includes artifact checksum, source SHA, device model, iOS
|
|
version, outcome, and any residual iOS/libimobiledevice compatibility risk.
|
|
|
|
## References
|
|
|
|
- [Apple: enable Developer Mode on a device](https://developer.apple.com/documentation/xcode/enabling-developer-mode-on-a-device)
|
|
- [Apple: run an app on a physical device](https://developer.apple.com/documentation/Xcode/running-your-app-on-simulated-or-physical-devices)
|
|
- [Apple: register a single device](https://developer.apple.com/help/account/devices/register-a-single-device/)
|
|
- [Apple: create a development provisioning profile](https://developer.apple.com/help/account/provisioning-profiles/create-a-development-provisioning-profile/)
|
|
- [libimobiledevice project](https://github.com/libimobiledevice/libimobiledevice)
|