Commit Graph

7 Commits

Author SHA1 Message Date
340f2881d0 ci: run qmltestrunner with offscreen Qt platform
All checks were successful
CI / Quality (push) Successful in 5m31s
Release / Linux AppImage (push) Successful in 5m16s
Headless CI runner has no X display, so qmltest aborts loading the
xcb plugin. Set QT_QPA_PLATFORM=offscreen for the ctest invocation
in CI and in both Makefile qmltest targets so local headless runs
work too.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 21:13:35 +02:00
de4a14da36 v0.2.0 (13/N): qmltestrunner harness + CI wiring + close out v0.2.0 plan
Closes the testing-strategy row of PLAN.md §13 v0.2.0 and parks the
two remaining items with rationales.

Shipped:

- framework/qml/tests/{CMakeLists.txt, main.cpp, tst_smoke.qml}
  Qt Quick Test scaffold: QUICK_TEST_MAIN bootstrap + one smoke test
  proving the harness loads. New tests land as tst_<feature>.qml in
  the same dir; qmltestrunner auto-discovers them. Built only when
  -DBUILD_TESTING=ON (production AppImages stay clean).
- skeleton + example/todo Makefiles: `make qmltest` target invokes
  the configure → build → ctest dance. `make quality` now depends
  on qmltest.
- .gitea/workflows/ci.yml: `QML unit tests` step after qmllint in
  the Quality job. Out-of-tree build dir (build-tests) so the
  CTest run doesn't pollute the cached release build.

Verified locally: configure + build + ctest pass, both smoke
assertions pass, runs in 0.5s.

Closed in PLAN.md §13 v0.2.0 with rationale (no code change):

- Build-time Symfony cache warmup → moved to v0.3.0. The obvious
  approach (cache:warmup at build, copy at first launch) doesn't
  save any time because Symfony bakes absolute kernel.project_dir
  into the compiled cache, and the AppImage's FUSE mount path
  changes every launch — every cached path is stale on launch N+1.
  Doing it properly requires virtualising getProjectDir(), symlink
  fix-up, multi-app namespacing — its own minor's worth of design.
- ReactiveObject cursor pagination → closed N/A. ReactiveObject
  already has pending / invoke() / Idempotency-Key correlation /
  version-gap detection at parity with ReactiveListModel; the only
  feature it lacks is *pagination*, which is meaningless for a
  single-entity model.

That fully closes the v0.2.0 plan as documented. Remaining v0.2.0
items in PLAN.md §13 are the audit-ends already shipped earlier in
the cycle (interfaces / BridgeOp / BridgeBundleInfo / Maker DRY /
--with-dto / port negotiation / pre-migration backup / bridge:export
/ periodic auto-update / native-dialogs doc / event maker /
read-model maker / qmltestrunner) plus the two parked items
documented above. Ready to tag when the user gives the word.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 21:02:30 +02:00
341bcacafe skeleton: bring AppImage parity, scaffolded apps inherit the packaging flow
The v0.1.0 shakedown fixes for AppImage assembly (path-repo
symlink:false sed, writable-cache redirect) all landed in
examples/todo. The skeleton — which is what bin/php-qml-init copies
when scaffolding a new app — had no `appimage` target at all, so every
scaffolded app would have to either copy the example's Makefile by
hand or re-discover the same shakedown bugs.

Brings parity:

  - framework/skeleton/Makefile gains `staging-symfony` and `appimage`
    targets, mirroring the example's. Two new variables (BUNDLE_SRC,
    PACKAGING) parameterise the framework-tree paths so bin/php-qml-init
    can rewrite them at scaffold time without sed-touching the recipe.
  - framework/skeleton/packaging/skeleton.{desktop,png} added — minimum
    surface for the AppImage assembly to succeed without the user
    needing to author them.
  - framework/skeleton/Makefile's staging-symfony recipe handles both
    relative (framework default `../../php`) and absolute (post-scaffold)
    BUNDLE_SRC values via a case statement.
  - bin/php-qml-init renames packaging/skeleton.* → packaging/$NAME.*,
    rewrites the .desktop file's Name/Exec/Icon, and updates the
    Makefile's --app-name / --output / --desktop / --icon flags +
    BUNDLE_SRC + PACKAGING variables. For --vendor mode, framework's
    packaging/linux/ is also vendored to .bridge-packaging/ alongside
    the existing .bridge/ + .bridge-qml/.

Verified by scaffolding both modes:
  - non-vendored: BUNDLE_SRC + PACKAGING absolute paths
  - --vendor: BUNDLE_SRC=../.bridge, PACKAGING=.bridge-packaging,
    .bridge-packaging/ contains build-appimage.sh

Skeleton's `make quality` still green; staging-symfony works locally
(vendor/php-qml/bridge resolves to a real directory, not a symlink).

Closes the v0.1.1 follow-up "bin/php-qml-init parity" tracked in
PLAN.md §13.

Bundled drive-by: docs/makers.md picked up two markdownlint auto-fixes
(blank lines around lists) when the IDE saved.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 13:43:48 +02:00
adc0cdc11d Phase 3 sub-commit 5: maker-output snapshot test + phase closure
framework/php/tests/snapshot/ holds reference output for every shipped
maker (resource Todo, command MarkAllDone, window Todo). The
run.sh script:

  - git-archives the skeleton into a temp dir
  - composer-installs against the bundle's real path
  - removes the existing maker outputs so the regenerators don't bail
  - runs the three makers
  - diffs each generated file against the matching baseline

CI / make quality fail on any drift; if a template change is intended,
the baselines must be regenerated in the same commit. Wired into:

  - framework/skeleton/Makefile's `quality` target (local/dev runs)
  - .gitea/workflows/ci.yml (CI runs after qmllint)

Plus a few hardenings discovered while wiring this up:

  - The resource maker template now injects NormalizerInterface
    (not SerializerInterface — that interface lacks ::normalize()).
    All Todo controllers re-rendered to match.
  - The command maker template emits a $this->em->flush() so the
    injected EntityManager isn't a property.onlyWritten violation
    in PHPStan after the user fills in the body.
  - phpstan.neon and php-cs-fixer's Finder both exclude tests/snapshot
    so the baselines aren't auto-rewritten or analysed as live code.

CI workflow now also installs FrankenPHP, builds the todo example, and
runs the bridge-integration test from Phase 3 sub-commit 4.

Phase 3 done. Outstanding follow-ups (deferred per spec): the
qmltestrunner-driven QML unit tests, make:bridge:event,
make:bridge:read-model, ReactiveObject pagination.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 16:03:41 +02:00
7323b9affe Phase 1 sub-commit 7: CI quality job
Some checks failed
CI / Quality (push) Has been cancelled
PHPStan (level 6 + symfony extension) and PHP CS Fixer (Symfony +
PHP83Migration ruleset) configs at framework/php/. composer.json
exposes phpstan / cs:check / cs:fix / phpunit / quality scripts.
PHPStan-clean across the bundle; cs:check is happy after auto-fix
applied @Symfony idioms (yoda, leading-backslash JSON_*, blank-line
before return). Test mocks consolidated into a HubSpy helper to keep
PHPStan happy about by-ref captures.

Skeleton's Makefile target `quality` chains `composer quality` (in
framework/php/) with cmake's all_qmllint target. Local run is green —
11 tests / 32 assertions, no PHPStan errors, cs-fixer clean, qmllint
emits advisory warnings only.

Layout fix in skeleton's Main.qml: status-dot Rectangles inside
RowLayout now use Layout.preferredWidth/Height instead of width/height
to satisfy Quick.layout-positioning checks.

.gitea/workflows/ci.yml replaces the placeholder with a real `quality`
job: setup-php, composer install (cached), the four PHP checks, Qt 6
via install-qt-action (cached), QML module build, qmllint via the
all_qmllint CMake target. Workflow exists from this commit onward
even if a runner isn't provisioned yet.

bridge:doctor lost the Publisher dependency since it was only used as
a "service is wired" marker — the command being injectable already
proves that.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 02:15:06 +02:00
d671b26cac Phase 1 sub-commit 6: skeleton wiring — make dev runs end-to-end
All checks were successful
CI / Quality (push) Successful in 5s
Symfony app under framework/skeleton/symfony/: minimal bin/console,
public/index.php, MicroKernel-based src/Kernel.php, services.yaml,
framework/security/mercure config, and a demo App\Controller\PingController
that GETs /api/ping (returning JSON pong) and republishes the same
payload to the Mercure topic app://ping. composer.json uses a path
repository to symlink the bundle from ../../php so local edits are
picked up live.

QML app under framework/skeleton/qml/: top-level CMake that
add_subdirectory's framework/qml, a main.cpp that creates the Qt
process, runs SingleInstance.acquireOrForward before any QML loads,
exposes SingleInstance via context property, and loadFromModule's
Skeleton.Main. Main.qml uses BackendConnection / RestClient /
MercureClient from PhpQml.Bridge and renders status dots, a Ping
button, and an event log.

Caddyfile binds 127.0.0.1:8765, enables in-memory Mercure with a
256-bit dev JWT (matches symfony/.env, lcobucci/jwt requires this).
Makefile wraps build / dev / doctor / clean; scripts/dev.sh starts
FrankenPHP --watch and the Qt host together with explicit PID-based
teardown (process-group `kill 0` proved unreliable when frankenphp's
watch fork reparented).

Bug fixes uncovered in this sub-commit:
- SingleInstance.acquireOrForward: probe-first, then removeServer +
  retry-listen. The original loop-with-removeServer-after-failed-bind
  silently exited on stale sockets from prior runs.
- Main.qml: MercureClient does NOT inherit BackendConnection.token —
  Mercure subscribes anonymously in dev (Caddyfile), and forwarding
  the bridge bearer made it 401-loop.
- /api/ping was 500ing because the dev MERCURE_JWT_SECRET was 144 bits;
  bumped to 64-char (>=256 bit) to satisfy lcobucci/jwt.
- Linked the framework lib (php_qml_bridge) explicitly in addition to
  the QML plugin so SingleInstance.h resolves.
- Auto-generated config/reference.php gitignored.

Smoke verified offscreen: /healthz 200, /api/ping 200, 1 publish, 1
subscriber, zero 401s, clean shutdown with no zombies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 01:50:16 +02:00
9001386f92 Phase 1 sub-commit 1: scaffold framework, skeleton, CI
All checks were successful
CI / Quality (push) Successful in 48s
Stands up the directory structure Phase 1 fills in over subsequent
sub-commits: framework/php (Composer package php-qml/bridge),
framework/qml (Qt module placeholder), framework/skeleton (Caddyfile +
Makefile stubs), and .gitea/workflows/ci.yml. Root .gitignore covers
the build/composer/Symfony/Qt/CMake/IDE artefacts the rest of Phase 1
will produce. No bundle code, no Qt module sources, no working dev mode
yet — those land in sub-commits 2-7. Spike still in place.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:59:06 +02:00