From 26a2b3771bab1aeee522f66afee8807885e922ce Mon Sep 17 00:00:00 2001 From: magdev Date: Sat, 2 May 2026 21:30:08 +0200 Subject: [PATCH] =?UTF-8?q?Phase=205=20sub-commit=204:=20release=20readine?= =?UTF-8?q?ss=20=E2=80=94=20README=20+=20CHANGELOG=20+=20status=20line?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README.md rewritten to reflect actual onboarding (clone → php-qml-init → make dev / make appimage) instead of the planning-stage placeholder. Phase status checklist now reflects 0–4a green and 5 in progress. CHANGELOG.md created at repo root following Keep-a-Changelog conventions, with a v0.1.0 entry that summarises Phases 0–4a plus the Phase 5 polish work (DevConsole, php-qml-init, editor configs, hot-reload docs). Date is TBD; tagging is the user's call. PLAN.md gains a Status banner so it's obvious at a glance which phase the implementation tracks against the design. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 47 +++++++++++++++++++++++++ PLAN.md | 2 ++ README.md | 96 +++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 125 insertions(+), 20 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a6d866e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,47 @@ +# Changelog + +All notable changes to this project are documented here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Pre-v1.0.0, minor bumps may break public API. + +## [Unreleased] + +### Added + +- (none yet — next changes land here) + +## [0.1.0] — TBD + +First public preview. Phases 0 through 4a in PLAN.md are complete plus the Phase 5 DX-polish sub-commits. Linux is the only packaged target; macOS and Windows are deferred to 4b / 4c. Tagging is the user's call (release CI runs on `v*` tags). + +### Added + +- **Process-pair architecture.** Qt/QML host owns rendering; bundled FrankenPHP child runs the Symfony app in worker mode. They communicate via local HTTP and Mercure SSE. +- **Symfony bundle (`php-qml/bridge`).** `BridgeBundle` wires Doctrine subscriber, `ModelPublisher`, `bridge:doctor` console command, and the `#[BridgeResource]` attribute so app code stays idiomatic Symfony. +- **Qt module (`PhpQml.Bridge`).** `BackendConnection` (lifecycle + Update Semantics state machine: Connecting / Online / Reconnecting / Offline), `RestClient`, `MercureClient`, `ReactiveListModel`, `ReactiveObject`, `AppShell`, `SingleInstance` (QLocalServer-backed lock with launch-arg forwarding), `DevConsole`. +- **Update Semantics.** Optimistic mutations with `Idempotency-Key` round-tripped to Mercure as `correlationKey`; in-flight `pending` role; offline overlay + reconnecting banner via `AppShell`. +- **Headline makers** (`symfony/maker-bundle`): + - `make:bridge:resource ` — entity (`#[BridgeResource]` + UUIDv7 by default, `--int-id` for auto-increment), CRUD controller, starter `List.qml`. + - `make:bridge:command ` — controller stub for non-CRUD endpoints. + - `make:bridge:window ` — second-window QML scaffold. +- **Skeleton application** (`framework/skeleton`) — minimal reference app exercised by every CI job. +- **POC todo app** (`examples/todo`) — full list UI, multi-window mirror, mark-all-done command, end-to-end test of multi-window coherence and crash-recovery. +- **bundled mode.** When `BRIDGE_URL` is unset (typical AppImage case), `BackendConnection` spawns the embedded FrankenPHP, generates a per-session bearer token, runs first-launch migrations into `~/.local/share//var/data.sqlite`, and supervises the child with `prctl(PR_SET_PDEATHSIG, SIGTERM)` for cleanup safety. +- **Linux AppImage packaging.** `packaging/linux/build-appimage.sh` + `make appimage` produce a single ~150 MB binary (Qt + Symfony + FrankenPHP + AppImageUpdate sidecar). +- **AppImageUpdate auto-update.** Embedded `update-info` ELF section points at the canonical Gitea Releases URL. `BackendConnection.checkForUpdates()` / `applyUpdate()` invoke the bundled sidecar. +- **Release CI** (`.gitea/workflows/release.yml`). Triggers on `v*` tags. Builds the AppImage, runs `tests/perfsmoke.sh` against PLAN.md §11 budgets (bundle ≤ 200 MB, cold start ≤ 4 s on shared CI runners, idle RSS ≤ 200 MB), generates zsync metadata + `latest.json` appcast + `SHA256SUMS`, optionally GPG-signs them, and uploads everything to the Gitea Release. +- **Quality CI** (`.gitea/workflows/ci.yml`). PHPStan + php-cs-fixer (check) + PHPUnit + qmllint + maker snapshot test + bridge-integration test (HTTP/SSE round-trip + crash-recover) on every push to `main`. +- **DX polish (Phase 5):** + - `DevConsole.qml` — opt-in window into the bundled FrankenPHP child's merged stdout/stderr; 500-line ring buffer; `Ctrl+`` toggles it in skeleton + todo example. + - `bin/php-qml-init ` — bash scaffolder. Copies `framework/skeleton/`, rewrites identifiers, repoints the path-composer-repo and CMake `add_subdirectory(framework/qml)` reference, runs `composer install` and migrations. `--vendor` produces a portable copy. + - `.vscode/launch.json` + `tasks.json` + `settings.json` and `.idea/runConfigurations/` shipped with skeleton and todo example. + - Hot-reload story documented end-to-end (FrankenPHP `--watch`, Qt Creator Reload, `qmlls` live preview). + +### Notes + +- Tooling versions enforced: PHP 8.4+, Symfony 8, Doctrine ORM 3, Qt 6.5+, FrankenPHP 1.12.2. +- The bundle ships without `composer.lock` (it's a library); the skeleton and the todo example carry their own. +- License: TBD before tagging. Qt is shipped under LGPL — see PLAN.md §12 for relinkability obligations. + +[Unreleased]: https://gitea.example//php-qml/compare/v0.1.0...HEAD +[0.1.0]: https://gitea.example//php-qml/releases/tag/v0.1.0 diff --git a/PLAN.md b/PLAN.md index 95a03f1..3e12ba8 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,5 +1,7 @@ # php-qml — Plan for a Symfony/FrankenPHP + Qt/QML Desktop Framework +> **Status (2026-05):** Phases 0–4a complete, Phase 5 (DX polish) in progress. v0.1.0 release prep underway. Tagging is user-driven. + ## 1. Vision A small, opinionated framework that lets PHP developers ship native desktop applications with a Qt/QML frontend and a modern Symfony backend, packaged as a single distributable per OS. diff --git a/README.md b/README.md index af67d4b..49c294c 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ A framework for building native desktop applications with a Symfony / FrankenPHP ## Status -**Planning stage.** The architectural design lives in [PLAN.md](PLAN.md). No implementation exists yet — first code lands in Phase 0 (a throwaway transport spike). See the [roadmap](PLAN.md#13-roadmap-to-poc). +**Phase 5 / pre-v0.1.0.** Phases 0–4a are merged: working framework skeleton, reactive models, the headline `make:bridge:*` makers, a real POC (`examples/todo`), Linux AppImage packaging, AppImageUpdate auto-update, performance smoke harness, release CI on `v*` tags. Full design lives in [PLAN.md](PLAN.md) and the per-version log in [CHANGELOG.md](CHANGELOG.md). + +What's not done yet: macOS and Windows packaging (Phase 4b/4c). ## What it is @@ -24,47 +26,101 @@ If you've watched php-gtk and php-qt go quiet, that is the failure mode this pro ## Tech stack -- **Backend:** PHP 8.x, Symfony, Doctrine ORM, FrankenPHP (worker mode), Mercure -- **Frontend:** Qt 6 LTS, QML, C++ plugin where required +- **Backend:** PHP 8.4+, Symfony 8, Doctrine ORM 3, FrankenPHP (worker mode), Mercure +- **Frontend:** Qt 6.5+, QML, C++ plugin where required - **Build:** CMake, Composer - **CI:** Gitea Actions, Gitea Releases -- **Targets:** Linux (AppImage), macOS (`.app` + `.dmg`), Windows (NSIS / MSIX) +- **Packaging:** Linux AppImage today; macOS (`.app` + `.dmg`) and Windows (NSIS / MSIX) on the roadmap -## Getting started +## Quick start -Nothing to run yet. Once Phase 0 lands: +Prerequisites: Qt 6.5+ dev packages, CMake, gcc-c++, PHP 8.4+, Composer, [FrankenPHP](https://frankenphp.dev/) on PATH. -1. Clone the repository and check out the `dev` branch. -2. Install Qt 6 and PHP 8.x; the FrankenPHP runtime is fetched at build time. -3. `task dev` — starts FrankenPHP in watch mode and launches the Qt host pointed at it. +```bash +git clone https://gitea.example//php-qml +cd php-qml -Detailed setup will be documented alongside the framework skeleton in Phase 1. +# 1) Scaffold a fresh app (auto-detects this checkout as the framework) +./bin/php-qml-init my-app + +# 2) Boot it +cd my-app +make doctor # bridge:doctor — readiness check +make dev # FrankenPHP --watch + Qt host +``` + +`make dev` opens the Qt window, connection state flips to **Online**, and a Ping button round-trips through `/api/ping` and back via Mercure SSE. + +Add a reactive resource — entity + REST controller + QML snippet — with one maker: + +```bash +cd my-app/symfony +bin/console make:bridge:resource Todo +bin/console make:migration && bin/console doctrine:migrations:migrate -n +``` + +The QML side gets a `TodoList.qml` whose `ReactiveListModel` does an initial GET, subscribes to `app://model/todo`, and applies diffs as Mercure pushes them. There is no handwritten cross-side glue. + +For a non-trivial example with a multi-window test, crash-recovery test, and AppImage packaging, see [`examples/todo/`](examples/todo/README.md). + +## Packaging (Linux) + +```bash +cd examples/todo +FRANKENPHP=/path/to/frankenphp make appimage +./build/Todo-x86_64.AppImage +``` + +`make appimage` bundles Qt, the Symfony app, the FrankenPHP binary, and the AppImageUpdate sidecar into one `~150 MB` AppImage that auto-detects bundled mode (no `BRIDGE_URL` set), spawns its own FrankenPHP child, generates a per-session bearer token, and runs first-launch migrations. CI builds this on every `v*` tag and uploads to Gitea Releases with a `latest.json` appcast and zsync metadata for in-place updates. See [PLAN.md §13 Phase 4a](PLAN.md#phase-4a) for the full pipeline. ## Project structure -See [PLAN.md §9](PLAN.md#9-project-layout) for the intended layout. The repo currently contains only `PLAN.md` and this README. +```text +bin/ + php-qml-init # bash scaffolder — copies the skeleton into a new project +framework/ + php/ # the Symfony bundle (php-qml/bridge) + qml/ # the Qt module (PhpQml.Bridge): BackendConnection, ReactiveListModel, … + skeleton/ # reference application — what php-qml-init copies +examples/ + todo/ # POC todo app exercising every primitive +packaging/linux/ # build-appimage.sh + AppImageUpdate sidecar wiring +.gitea/workflows/ # ci.yml (PR gate) + release.yml (v* tags) +PLAN.md # design source of truth +CHANGELOG.md # release log +``` + +See [PLAN.md §9](PLAN.md#9-project-layout) for the conceptual layout. ## Roadmap Six phases, each ending with something runnable. Detail in [PLAN.md §13](PLAN.md#13-roadmap-to-poc). -- **Phase 0** — throwaway spike, prove transport on Linux. -- **Phase 1** — framework skeleton, dev mode, single-instance lock, CI quality gate. -- **Phase 2** — reactive models, update semantics, headline maker (`make:bridge:resource`). -- **Phase 3** — POC todo application generated via the makers; testing infrastructure. -- **Phase 4** — bundled mode, per-OS packaging, release CI, auto-update. -- **Phase 5** — DX polish. +- **Phase 0** ✅ throwaway spike — prove transport on Linux. +- **Phase 1** ✅ framework skeleton, dev mode, single-instance lock, CI quality gate. +- **Phase 2** ✅ reactive models, update semantics, headline maker (`make:bridge:resource`). +- **Phase 3** ✅ POC todo application generated via the makers; testing infrastructure. +- **Phase 4a** ✅ bundled mode, Linux AppImage, release CI, AppImageUpdate auto-update, performance smoke. +- **Phase 4b/4c** ⏳ macOS and Windows packaging. +- **Phase 5** 🚧 DX polish (in progress) — dev console, init script, hot-reload docs, v0.1.0 prep. ## Contributing Active development happens on the `dev` branch; `main` only carries release commits. Pull requests target `dev`. -A `CONTRIBUTING.md` will be added with the framework skeleton in Phase 1. +Quality gate that CI runs: + +```bash +cd framework/php && composer quality # PHPStan + cs-fixer (check) + PHPUnit +cd examples/todo && make quality # adds qmllint + integration test +``` + +A dedicated `CONTRIBUTING.md` will arrive alongside Phase 5's wrap-up. ## Versioning -Semantic Versioning — `MAJOR.MINOR.BUGFIX`. MAJOR for breaking changes, MINOR for backwards-compatible features, BUGFIX for backwards-compatible fixes. +[Semantic Versioning](https://semver.org/) — `MAJOR.MINOR.BUGFIX`. MAJOR for breaking changes, MINOR for backwards-compatible features, BUGFIX for backwards-compatible fixes. Pre-v1.0.0 minor bumps may break public API. ## License -To be decided before the first release. The framework's own code will be permissively licensed; note that Qt is shipped under LGPL and that carries obligations for distributors — see [PLAN.md §12](PLAN.md#12-open-questions-and-risks) (Qt LGPL relinkability). +To be decided before v0.1.0 is tagged. The framework's own code will be permissively licensed; note that Qt is shipped under LGPL and that carries obligations for distributors — see [PLAN.md §12](PLAN.md#12-open-questions-and-risks) (Qt LGPL relinkability).