docs: refresh README + docs/ for v0.2.0
The README still framed the project as "Phase 5 / pre-v0.1.0" and the docs predated the v0.2.0 surface (typed BridgeOp, public service interfaces, port negotiation, pre-migration auto-backup, bridge:export, periodic auto-update, two new makers, qmltestrunner). Bring them in line with what's actually shipped, and add badges (release, license, PHP, Symfony, Qt, FrankenPHP, CI, platform) to the README so the status is legible at a glance. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,22 @@ if (!explicitUrl.isEmpty()) {
|
||||
|
||||
Bundled-mode init runs after the QApplication event loop is up so `QStandardPaths` and `QProcess` work correctly.
|
||||
|
||||
## Port negotiation
|
||||
|
||||
Bundled mode does not hardcode a TCP port. On every spawn the host:
|
||||
|
||||
1. Binds a `QTcpServer` to `QHostAddress::LocalHost` port 0 (kernel picks a free ephemeral port).
|
||||
2. Captures `serverPort()`, then closes the probe socket.
|
||||
3. Hands the chosen port to FrankenPHP via the `PORT` env var.
|
||||
|
||||
The bundled `Caddyfile` reads `{$PORT:8765}`, so it picks up whatever the host negotiated and falls back to `8765` only when the env is unset (i.e. dev mode without an override).
|
||||
|
||||
The chosen port is also written to `~/.local/share/<app>/var/bridge.port` on every launch. External tools (a debug helper, a `curl /healthz` from a script) can read the file instead of grepping Qt's log for the address.
|
||||
|
||||
For reproducible test harnesses, pin the port via the `BRIDGE_PORT=<n>` env var. Both `examples/todo/tests/bundled-supervisor.sh` and `tests/perfsmoke.sh` set this so multiple harnesses can run side by side without contending. When `BRIDGE_PORT` is set the negotiation step is skipped.
|
||||
|
||||
Why negotiate at all? Two installed php-qml apps used to race for `8765` on first launch — whichever lost went Offline. Negotiation eliminates the collision class; the kernel guarantees uniqueness within the host.
|
||||
|
||||
## Resolving the FrankenPHP child
|
||||
|
||||
Bundled mode needs three things on disk near the host binary:
|
||||
@@ -86,6 +102,20 @@ env.insert("DATABASE_URL", databaseUrl()); // sqlite:///<userdata>/var/data.sq
|
||||
|
||||
If migrations fail or time out, bundled mode goes Offline before spawning. Apps that want to handle a corrupt DB gracefully can detect this via `BackendConnection.error` and show a "reset database?" UI.
|
||||
|
||||
### Pre-migration auto-backup
|
||||
|
||||
Before invoking `doctrine:migrations:migrate`, the supervisor copies `var/data.sqlite` to `var/data.sqlite.<unix-timestamp>.bak` and keeps the 5 most recent backups. SQLite has no transactional DDL — a half-applied migration can corrupt the database with no rollback path. The backup is cheap insurance against that.
|
||||
|
||||
- Skipped on first launch (no DB exists yet).
|
||||
- Failure to copy logs a warning and continues — a missing safety net is not a reason to refuse to boot.
|
||||
- Bundled mode only. Dev-mode users own `symfony/var/data.sqlite` themselves.
|
||||
|
||||
If migration corrupts the DB, the user's recovery is `cp var/data.sqlite.<latest>.bak var/data.sqlite` from the data directory; the next launch boots against the rolled-back schema (and a future migration retry succeeds against the original state).
|
||||
|
||||
### Database export
|
||||
|
||||
Apps can offer a "Save a copy of my data" button by calling [`BackendConnection.exportDatabase(path)`](qml-api.md#exportdatabase) (Q_INVOKABLE) — typically paired with `Qt.labs.platform.FileDialog`. The same operation is available as `bin/console bridge:export <destination>` for CLI use. Both read the source path from `DATABASE_URL` so they work in dev mode and bundled mode unchanged.
|
||||
|
||||
## Supervisor
|
||||
|
||||
The supervisor is `BackendConnection::onChildFinished()` plus a retry counter:
|
||||
@@ -124,7 +154,7 @@ m_child->setChildProcessModifier([] {
|
||||
});
|
||||
```
|
||||
|
||||
Without this, a host crash leaves an orphan FrankenPHP process holding port 8765, and the *next* launch can't bind.
|
||||
Without this, a host crash leaves an orphan FrankenPHP process holding the negotiated port (and consuming the user's data files); the *next* launch finds no parent to connect back to but the orphan still races for resources.
|
||||
|
||||
`PR_SET_PDEATHSIG` only works on Linux. macOS and Windows builds will use platform-equivalents in their respective phases (see PLAN.md §4b/§4c).
|
||||
|
||||
@@ -134,7 +164,7 @@ Same as dev mode: `GET /healthz` every 5 s, 2 s timeout, 30 s threshold for Offl
|
||||
|
||||
## Auto-update
|
||||
|
||||
Bundled mode also wires up the AppImageUpdate sidecar — see [Linux packaging §auto-update](packaging-linux.md#auto-update). Three QML signals carry the result:
|
||||
Bundled mode wires up the AppImageUpdate sidecar — see [Linux packaging §auto-update](packaging-linux.md#auto-update). Three QML signals carry the result:
|
||||
|
||||
```qml
|
||||
Connections {
|
||||
@@ -150,6 +180,17 @@ Button { text: "Update"; onClicked: BackendConnection.applyUpdate() }
|
||||
|
||||
Both methods are no-ops in dev mode — they emit `updateCheckFailed("update checks are bundled-mode only")` so QML can treat them uniformly.
|
||||
|
||||
### Periodic check
|
||||
|
||||
The supervisor arms an automatic poll on the first `Online` transition: a launch-time check 10 s after the backend is ready, then a recurring check every 6 hours. PLAN.md §11 *Auto-update* asked for "check on launch and once per N hours; offer install on next restart, never auto-restart" — the poll surfaces `updatesAvailable()` so apps can show a banner; `applyUpdate()` is still the explicit install trigger and there is no auto-restart.
|
||||
|
||||
| Env var | Default | Effect |
|
||||
| --- | --- | --- |
|
||||
| `BRIDGE_AUTO_UPDATE_DISABLE` | unset | Set to `1` to disable the periodic poll. The Q_INVOKABLE `checkForUpdates()` / `applyUpdate()` still work. |
|
||||
| `BRIDGE_AUTO_UPDATE_PERIOD_MIN` | `360` (6 h) | Override the period in minutes. |
|
||||
|
||||
Dev mode skips the periodic check entirely.
|
||||
|
||||
## Single-instance lock
|
||||
|
||||
The host uses `SingleInstance` (a QLocalServer-backed lock) regardless of mode:
|
||||
|
||||
Reference in New Issue
Block a user