Detail Phase 4 scope; split into 4a (Linux now) / 4b (macOS) / 4c (Windows)
Honest scoping: macOS and Windows packaging carry hard operational
prerequisites (self-hosted runners, Apple Developer + Authenticode certs)
that can't be solved from a Linux dev machine. Phase 4a delivers the full
Linux pipeline now; 4b and 4c wait until those prerequisites land.
4a sub-commits:
1. Bundled-mode startup (auto-detected: no BRIDGE_URL → spawn child,
per-session secret, first-launch migrations into XDG data dir)
2. AppImage recipe (packaging/linux/build-appimage.sh + make appimage)
3. Linux release.yml on v* tags (Gitea Release + SHA256SUMS + appcast)
4. AppImageUpdate + BackendConnection.checkForUpdates()
5. Performance-smoke harness + 4a phase closure
The framework code stays platform-agnostic — only the packaging layer
is per-OS. 4b / 4c entries get filled into PLAN.md when their runners
and certs become available.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
53
PLAN.md
53
PLAN.md
@@ -751,6 +751,59 @@ Phase 3 turns the framework from "the smallest reactive resource" into "a real a
|
|||||||
- Provision the macOS self-hosted runner before this phase starts — it gates the macOS build.
|
- Provision the macOS self-hosted runner before this phase starts — it gates the macOS build.
|
||||||
- Document the build pipeline and the runner topology.
|
- Document the build pipeline and the runner topology.
|
||||||
|
|
||||||
|
#### Phase 4 detailed scope
|
||||||
|
|
||||||
|
Phase 4 is genuinely big — bundled-mode startup is a host-architecture change, and the per-OS packaging trifecta carries operational dependencies (Apple Developer cert + notarization for macOS, Authenticode + a Windows runner for Windows) that can't be solved from a Linux dev machine. **Phase 4 is split into three sub-phases — only 4a (Linux) ships now**; 4b (macOS) and 4c (Windows) wait until their runners and credentials exist.
|
||||||
|
|
||||||
|
**Sub-phase split:**
|
||||||
|
|
||||||
|
| Sub-phase | Platform | Hard prerequisites |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| **4a** | Linux AppImage + bundled mode + Linux release CI + AppImageUpdate + perf-smoke harness | none (covered in this dev environment) |
|
||||||
|
| **4b** | macOS `.app` + `.dmg` + Sparkle 2 + notarization | self-hosted macOS runner, Apple Developer cert ($99/yr) |
|
||||||
|
| **4c** | Windows NSIS + WinSparkle + Authenticode | self-hosted Windows runner, code-signing cert |
|
||||||
|
|
||||||
|
Sub-phases 4b and 4c are scoped in their own `Phase 4b` / `Phase 4c` entries in this section once their prerequisites are met. The framework code stays portable — bundled-mode plumbing in 4a is platform-agnostic, only the packaging layer is platform-specific.
|
||||||
|
|
||||||
|
**Phase 4a sub-commits (each ends runnable):**
|
||||||
|
|
||||||
|
1. **Bundled-mode startup in `BackendConnection`.** Mode is auto-detected: `BRIDGE_URL` env set → dev mode (today's behaviour). `BRIDGE_URL` unset → bundled mode, where the host:
|
||||||
|
- resolves the user app data dir per OS (`$XDG_DATA_HOME/php-qml-app` on Linux),
|
||||||
|
- ensures `var/data.sqlite`, `var/cache/`, `var/log/` exist there,
|
||||||
|
- generates a per-session 32-byte random secret and writes it to a child-process env var,
|
||||||
|
- spawns `bin/frankenphp` next to the host binary (overridable via `BRIDGE_FRANKENPHP_BIN`),
|
||||||
|
- waits for `/healthz`,
|
||||||
|
- on first-ever launch, runs `bin/console doctrine:migrations:migrate -n` against the user's DB before opening the SSE connection.
|
||||||
|
|
||||||
|
The token-rotation signal already wired in §3 *Edge cases* fires when the supervisor restarts the child mid-session; subsequent commits exercise it. Skeleton + example pick up bundled mode by default with no config when run outside dev mode.
|
||||||
|
2. **AppImage recipe.** `packaging/linux/build-appimage.sh` script that produces a single-file `.AppImage`:
|
||||||
|
- `cmake --install` the host into a staging dir,
|
||||||
|
- copy the bundled `frankenphp` binary + the Symfony app tree (`composer install --no-dev`) into the AppDir,
|
||||||
|
- run `linuxdeployqt` to gather Qt runtime,
|
||||||
|
- run `appimagetool` to seal it.
|
||||||
|
|
||||||
|
The example app gets a target `make appimage` that invokes the script with the example's bits. Hard-coded versions for `linuxdeployqt` and `appimagetool` (downloaded into a tools dir, gitignored).
|
||||||
|
3. **Linux release CI.** `.gitea/workflows/release.yml` triggered by `v*` tags. Matrix initially has only Linux (macOS/Windows added when their sub-phases land). Builds the AppImage, signs `SHA256SUMS` with the GPG release key, uploads everything to a Gitea Release. CI's `quality` workflow stays as-is.
|
||||||
|
4. **AppImageUpdate + appcast.** `latest.json` published alongside the release, describing the version + URL + sha256. The host links against `libappimageupdate` and exposes `BackendConnection.checkForUpdates()` (no-op in dev mode). User triggers manually via menu (Phase 5 will polish to a periodic check).
|
||||||
|
5. **Performance-smoke harness + phase closure.** A CI job that runs the example app's bundled binary headlessly (offscreen QPA), asserts cold-start ≤ 2 s, idle memory ≤ 200 MB, list-render ≤ 250 ms (PLAN.md §11). Numbers reported per-build. PLAN.md updated to mark 4a closed.
|
||||||
|
|
||||||
|
**Out of scope for 4a (deferred to 4b / 4c / Phase 5):**
|
||||||
|
|
||||||
|
- macOS `.app` bundle, codesign, notarization, Sparkle 2 integration.
|
||||||
|
- Windows NSIS, Authenticode, WinSparkle integration.
|
||||||
|
- Multi-arch (Linux ARM64 / Windows ARM) — wait for user demand.
|
||||||
|
- `make:bridge:event`, `make:bridge:read-model` — Phase 3.x.
|
||||||
|
- `qmltestrunner`-driven QML unit tests — Phase 3.x or Phase 5.
|
||||||
|
|
||||||
|
**Done criteria for 4a:**
|
||||||
|
|
||||||
|
- `make appimage` produces a runnable single-file `.AppImage` of the todo example.
|
||||||
|
- The AppImage launches without any `BRIDGE_URL` configured, spawns its embedded FrankenPHP, runs first-launch migrations into `~/.local/share/php-qml-todo/var/data.sqlite`, and shows the todo UI.
|
||||||
|
- Killing the bundled FrankenPHP from outside the AppImage triggers the supervisor restart in `BackendConnection`; `tokenRotated` fires; the QML side recovers.
|
||||||
|
- A `v*` tag pushes a Linux AppImage + signed `SHA256SUMS` + appcast to a Gitea Release.
|
||||||
|
- `BackendConnection.checkForUpdates()` invoked from the menu finds a newer release and updates in place.
|
||||||
|
- The performance-smoke harness reports cold-start / memory / render-time numbers within budget on every release build.
|
||||||
|
|
||||||
### Phase 5 — DX polish
|
### Phase 5 — DX polish
|
||||||
|
|
||||||
- Project skeleton via Composer / a small CLI to scaffold a new app.
|
- Project skeleton via Composer / a small CLI to scaffold a new app.
|
||||||
|
|||||||
Reference in New Issue
Block a user