Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions .github/workflows/native-build-smoke.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Native Build Smoke

on:
workflow_dispatch:
pull_request:
paths:
- .github/workflows/native-build-smoke.yml
- android/**
- capacitor.config.ts
- package.json
- package-lock.json
- public/**
- scripts/ios-init.sh
- src/native/**
- src/pages/mobile/**
- src/styles/mobile.css
- src/styles/mobile-ios.css

jobs:
native-build-smoke:
name: iOS bootstrap + Android smoke
runs-on: macos-15
timeout-minutes: 60
permissions:
contents: read

steps:
- uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "20"
cache: npm

- name: Setup Java 17
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "17"

- name: Capture native toolchain versions
shell: bash
run: |
set -euo pipefail
mkdir -p artifacts
{
echo "Node $(node -v)"
echo "npm $(npm -v)"
xcodebuild -version
echo "CocoaPods $(pod --version)"
java -version
echo "ANDROID_HOME=${ANDROID_HOME:-unset}"
ls "$ANDROID_HOME/platforms" | grep "android-35"
ls "$ANDROID_HOME/build-tools" | grep "35.0"
} 2>&1 | tee artifacts/toolchain.log

- name: Bootstrap iOS workspace
shell: bash
run: |
set -euo pipefail
mkdir -p artifacts
npm run ios:init 2>&1 | tee artifacts/ios-init.log

- name: Build iOS simulator target
shell: bash
run: |
set -euo pipefail
mkdir -p artifacts
xcodebuild \
-workspace ios/App/App.xcworkspace \
-scheme App \
-configuration Debug \
-sdk iphonesimulator \
-destination "generic/platform=iOS Simulator" \
-derivedDataPath ios/App/build \
CODE_SIGNING_ALLOWED=NO \
build 2>&1 | tee artifacts/ios-build.log | xcbeautify

- name: Assemble Android debug APK
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Decouple Android smoke from iOS step failures

Keep Android validation independent from the iOS bootstrap in this workflow: as written, a non-zero exit from npm run ios:init stops the entire native-build-smoke job before either Android assemble step runs, so Android regressions are never exercised whenever iOS is broken. In practice this defeats the lane’s stated goal of collecting smoke evidence for both platforms in a single run.

Useful? React with 👍 / 👎.

shell: bash
run: |
set -euo pipefail
mkdir -p artifacts
npm run android:assemble:debug 2>&1 | tee artifacts/android-debug.log

- name: Assemble Android release APK
shell: bash
run: |
set -euo pipefail
mkdir -p artifacts
npm run android:assemble:release 2>&1 | tee artifacts/android-release.log

- name: Upload native smoke artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: native-build-smoke-${{ github.run_id }}
if-no-files-found: warn
path: |
artifacts/*.log
android/app/build/outputs/**
ios/App/build/**
101 changes: 34 additions & 67 deletions docs/2026-05-24-ery-71-native-packaging-validation.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,48 @@
# ERY-71 Native Packaging & Self-Host Distribution Validation Note
# ERY-71 Native Packaging Validation

Date: 2026-05-24
Owner: Engineer
Issue: [ERY-71](/ERY/issues/ERY-71)
Parent: [ERY-55](/ERY/issues/ERY-55)
Branch: `engineer/ery-71-native-packaging` (worktree off `17575bb`)
## Lane decision

This note replaces reliance on the old umbrella PR description as the validation
record for the native branch. It lists exactly what changed, what was verified
here, and what is explicitly blocked on build tooling.
- Selected validation lane: GitHub Actions macOS 15 (`.github/workflows/native-build-smoke.yml`)
- Why: the current validation host only has Command Line Tools; it does not have `Xcode.app`, CocoaPods, Java, `sdkmanager`, or `adb`, so it cannot satisfy the iOS/Android acceptance checks locally.
- Scope of the lane: pin Node 20 + Java 17 on GitHub's macOS 15 runner, run `npm run ios:init`, build the generated iOS workspace for the simulator, then run `npm run android:assemble:debug` and `npm run android:assemble:release`, and upload logs/artifacts for review.

## Changes in this branch
## Local host evidence captured on 2026-05-24

| Deliverable | Change | File(s) |
|---|---|---|
| ML Kit offline barcode | Wired the previously-dead `mlkitBarcodeDependencies` placeholder into a real `com.google.mlkit.vision.DEPENDENCIES` meta-data so Google Play fetches the model at install time, not first scan. Documented the GMS caveat + bundled-model path for air-gapped fleets (claim narrowed honestly). | `android/app/src/main/AndroidManifest.xml`, `android/app/build.gradle`, `docs/ANDROID.md` |
| LAN self-host distribution | Removed the fabricated sample-IP RFC1918 enumeration (false coverage, no CIDR match). Release builds are HTTPS-only; dev/live-reload cleartext is confined to `debug-overrides`; plain-HTTP LAN hosts are supported per-host via a documented custom-build template or TLS fronting (least privilege). | `android/app/src/main/res/xml/network_security_config.xml`, `docs/ANDROID.md`, `docs/IOS.md` |
| Version alignment | `android/app/build.gradle` versionName `0.5.1 → 0.5.2`, README badge + status line `0.5.1 → 0.5.2`. Now consistent with `package.json` (0.5.2) and `CHANGELOG` (0.5.2). | `android/app/build.gradle`, `README.md` |
| Check | Result | Evidence |
| --- | --- | --- |
| `xcodebuild -version` | FAIL | active developer dir is `/Library/Developer/CommandLineTools`; `Xcode.app` is not installed |
| `pod --version` | FAIL | `pod: command not found` |
| `java -version` | FAIL | no Java runtime present |
| `sdkmanager --version` | FAIL | `sdkmanager: command not found` |
| `adb version` | FAIL | `adb: command not found` |
| `npm run build` | PASS | build succeeds after explicitly restoring the missing darwin-arm64 Rollup + SWC native bindings that `npm ci` skipped on this Apple Silicon host |
| `npm run ios:init` on this host | FAIL | after the Rollup/SWC repair, the script reaches `npx cap add ios` and then stops at the real blocker: `CocoaPods is not installed` |
| `ios/` workspace produced locally | FAIL | `npx cap add ios` aborted before creating `ios/` because CocoaPods is absent on this host |

Note on target version: surfaces are aligned to the current canonical `0.5.2`.
The coordinated bump to `0.6.0` (package.json + gradle versionName + versionCode
increment + README + a new CHANGELOG section) is a release-tagging step under
[ERY-55](/ERY/issues/ERY-55) and should land atomically at the release cut, not
piecemeal here. Flagged for CTO decision.
## Native validation status

## What was verified here (pass)
First macOS lane run: GitHub Actions run [`26370608416`](https://github.com/SheetMetalConnect/eryxon-flow/actions/runs/26370608416) on `macos-15` (PR [#598](https://github.com/SheetMetalConnect/eryxon-flow/pull/598), `pull_request` trigger), 2026-05-24. Conclusion: **failure** at iOS bootstrap; downstream steps skipped. Logs/artifacts: `native-build-smoke-26370608416`.

- **XML well-formedness** — `network_security_config.xml` and
`AndroidManifest.xml` parse cleanly (`python3 xml.dom.minidom`). PASS.
- **Placeholder wiring** — `mlkitBarcodeDependencies` is now both defined
(build.gradle) and consumed (`${mlkitBarcodeDependencies}` in the manifest
meta-data). Confirmed by grep. PASS.
- **Version consistency** — package.json / build.gradle / README / CHANGELOG all
report `0.5.2`. PASS.
- **Least-privilege review** — release artifacts now have zero blanket cleartext
exception; cleartext is debug-only or one explicit operator-chosen host. PASS.
| Check | Status | Evidence |
| --- | --- | --- |
| macOS toolchain present (Xcode, CocoaPods, Java 17, Android SDK API 35) | PASS | "Capture native toolchain versions" step succeeded — confirms the lane is correctly provisioned |
| `npm run ios:init` (web build + `npx cap add ios`) | PARTIAL | web `vite build` PASS, `npx cap add ios` PASS (Xcode project + assets generated), then `pod install` FAILED |
| iOS `pod install` (CocoaPods dependency resolution) | FAIL | `GoogleMLKit/BarcodeScanning (= 7.0.0)` — pulled transitively by `@capacitor-mlkit/barcode-scanning` 7.5.0 — "required a higher minimum deployment target" than the Capacitor-generated Podfile's iOS target. Root cause below. |
| iOS simulator build of `ios/App/App.xcworkspace` | SKIPPED | iOS step failed before a workspace existed |
| `npm run android:assemble:debug` | SKIPPED | job halts on first failed step; Android never ran this run |
| `npm run android:assemble:release` | SKIPPED | job halts on first failed step; Android never ran this run |

## What is BLOCKED on build tooling (fail / not-run)
## Root cause + proposed fix (iOS)

This host has **Command Line Tools only (no Xcode.app), no CocoaPods, no JDK, no
Android SDK**. Probe output:
- `@capacitor-mlkit/barcode-scanning@7.5.0` depends on `GoogleMLKit/BarcodeScanning = 7.0.0`, which requires a higher minimum iOS deployment target than the default Capacitor Podfile emits.
- Fix: pin the generated iOS deployment target high enough for GoogleMLKit 7.0.0 (iOS 15.5+). Because `ios/` is not committed and `npx cap add ios` runs `pod install` during `add`, the target must be injected before pod resolution — e.g. set a Capacitor iOS `platform`/deployment-target config or generate the Podfile with `platform :ios, '15.5'` (+ a `post_install` target override) prior to `cap add ios`. This is a native-packaging change owned by ERY-71 follow-up, not the smoke-lane itself.

```
$ xcode-select -p -> /Library/Developer/CommandLineTools
$ xcodebuild -version -> error: requires Xcode (only CLT present)
$ pod --version -> pod: command not found
$ java -version -> Unable to locate a Java Runtime
$ echo $ANDROID_HOME -> (empty)
```
## Workflow follow-up

Consequently the following could not be executed and remain unproven:
- The iOS failure masked Android validation (steps skipped). Split iOS and Android into independent jobs (or `if: always()` gating) so a failure in one still produces evidence for the other on the same run.

1. **iOS workspace bootstrap** — `npm run ios:init` (`npx cap add ios` + `pod
install`) needs Xcode 15+ and CocoaPods. The `ios/` workspace therefore still
does not exist as a checked-in/validated artifact. NOT RUN — blocked on
toolchain.
2. **Android build smoke** — `npm run android:assemble:debug` / `:release`
(gradle) needs JDK 17 + Android SDK (API 35). The ML Kit meta-data and the new
network-security config are correct by inspection and parse cleanly, but have
not been proven to compile/package/merge in a real build. NOT RUN — blocked on
toolchain.
## Remaining Apple-specific gaps

## Remaining Apple-specific blockers for TestFlight/App Store
## Remaining Apple-specific gaps

Already enumerated in `docs/IOS.md` ("Remaining iOS-only gaps") and unchanged by
this branch — they require a Mac with Xcode + an Apple Developer account:

1. APNs auth key + `PushNotifications.register()` wiring.
2. Universal Links / Associated Domains (`applinks:app.eryxon.eu` entitlement +
`/.well-known/apple-app-site-association`).
3. Splash screen assets + 1024×1024 app icon set.
4. `Info.plist` localizations (`CFBundleLocalizations = [en, nl, de]`).

## Recommended next steps

- CTO: approve commit+push of `engineer/ery-71-native-packaging`.
- Provision a native-build lane (local Mac with Xcode 15+/CocoaPods and JDK 17 +
Android SDK, or a CI macOS runner) so iOS bootstrap and the Android build smoke
can produce pass/fail evidence. Tracked as the ERY-71 build-validation follow-up.
- At the v0.6 release cut, perform the coordinated `0.6.0` version bump atomically.
- The pre-TestFlight items remain explicitly tracked in [docs/IOS.md](/Users/vanenkhuizen/Documents/GitHub/products/eryxon-flow/docs/IOS.md): APNs wiring, Associated Domains / Universal Links, splash/icon assets, and App Store metadata polish.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use a repository-relative link for iOS gap tracking

Replace this absolute macOS filesystem path with a repo-relative link (for example docs/IOS.md or ./IOS.md from this folder). As written, /Users/vanenkhuizen/... only works on one machine and resolves to a broken path in GitHub, so readers cannot reach the referenced iOS blocker list during validation review.

Useful? React with 👍 / 👎.

28 changes: 28 additions & 0 deletions scripts/ios-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,33 @@ if ! command -v node >/dev/null 2>&1; then
exit 1
fi

ensure_native_build_bindings() {
require_binding() {
local package_name="$1"
node -e "require(require.resolve(process.argv[1]));" "$package_name"
}

# npm ci intermittently skips native optional packages on Apple Silicon.
# Vite then fails before Capacitor even runs because Rollup and SWC cannot
# load their darwin-arm64 bindings. Repair them once here so native
# bootstrap stays deterministic on fresh machines and CI runners.
if [[ "$(uname -s)" == "Darwin" && "$(uname -m)" == "arm64" ]]; then
if ! require_binding "@rollup/rollup-darwin-arm64" >/dev/null 2>&1 \
|| ! require_binding "@swc/core-darwin-arm64" >/dev/null 2>&1; then
local rollup_version
local swc_version
rollup_version="$(node -p "require('./node_modules/rollup/package.json').version")"
swc_version="$(node -p "require('./node_modules/@swc/core/package.json').version")"
echo "▶ Repairing darwin-arm64 native bindings for Rollup and SWC..."
npm install --no-save \
"@rollup/rollup-darwin-arm64@${rollup_version}" \
"@swc/core-darwin-arm64@${swc_version}"
require_binding "@rollup/rollup-darwin-arm64" >/dev/null
require_binding "@swc/core-darwin-arm64" >/dev/null
fi
fi
}

if [[ "${OSTYPE:-}" != darwin* ]]; then
echo "⚠ Capacitor's iOS toolchain only runs on macOS — this script will" >&2
echo " configure the project but you must finish 'cap add ios' on a Mac." >&2
Expand All @@ -25,6 +52,7 @@ if [[ -f package-lock.json ]]; then
else
npm install
fi
ensure_native_build_bindings

echo "▶ Building production web bundle..."
npm run build
Expand Down
Loading