Lift the pre-release freeze — releases resume and the plugin contract goes live #82
@@ -30,12 +30,9 @@ jobs:
|
|||||||
# authorship on the tip, so the author checks are reliable. Level = highest `Release-Bump:` trailer;
|
# authorship on the tip, so the author checks are reliable. Level = highest `Release-Bump:` trailer;
|
||||||
# pre-1.0 shifts down (auto-release/next-version.ts). Tag-only — release.yml promotes the
|
# pre-1.0 shifts down (auto-release/next-version.ts). Tag-only — release.yml promotes the
|
||||||
# already-built image; pushed with renovate-bot's PAT so release.yml fires (the built-in token won't).
|
# already-built image; pushed with renovate-bot's PAT so release.yml fires (the built-in token won't).
|
||||||
# Off until the Actions variable AUTO_RELEASE is set to 'true': Plainpages is pre-announcement and
|
|
||||||
# deliberately carries no tags, so an automated bump would only invent a version nobody consumes.
|
|
||||||
auto-release:
|
auto-release:
|
||||||
runs-on: docker-host
|
runs-on: docker-host
|
||||||
needs: renovate
|
needs: renovate
|
||||||
if: vars.AUTO_RELEASE == 'true'
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v7.0.1
|
- uses: actions/checkout@v7.0.1
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -105,16 +105,15 @@ Revisit only if the stated reason stops holding.
|
|||||||
`REVOKE CONNECT` from `bootstrap`. `REVOKE` only *warns* when the caller doesn't own the database,
|
`REVOKE CONNECT` from `bootstrap`. `REVOKE` only *warns* when the caller doesn't own the database,
|
||||||
so under the least-privilege provisioning account the README recommends it would report success
|
so under the least-privilege provisioning account the README recommends it would report success
|
||||||
while changing nothing, and it hard-fails whenever `PLUGIN_DB_ADMIN_URL` names a server with no
|
while changing nothing, and it hard-fails whenever `PLUGIN_DB_ADMIN_URL` names a server with no
|
||||||
`kratos`. A volume created before that file gained the revokes keeps the default grant;
|
`kratos`. It runs only on **first init**, so a revoke added to it later never reaches a volume that
|
||||||
`docker compose down -v` is the dev remedy. **Valid while pre-release, with no deployed volumes.**
|
already exists — `docker compose down -v` is the dev remedy, a deployed install needs a migration.
|
||||||
- **`bootstrap.ts` stays under `src/auth/`** even though it now provisions plugin databases as well
|
- **`bootstrap.ts` stays under `src/auth/`** even though it now provisions plugin databases as well
|
||||||
as seeding Ory. It is the one-shot service's entrypoint, not an auth module; moving it to
|
as seeding Ory. It is the one-shot service's entrypoint, not an auth module; moving it to
|
||||||
`src/bootstrap.ts` would edit `compose.yml`, five e2e compose files and `src/compose.test.ts` for a
|
`src/bootstrap.ts` would edit `compose.yml`, five e2e compose files and `src/compose.test.ts` for a
|
||||||
rename. Reconsider when a third seeding concern lands.
|
rename. Reconsider when a third seeding concern lands.
|
||||||
- **`BootContext.storage` keeps all six credential fields, and there is no `onShutdown` hook.** While
|
- **`BootContext.storage` keeps all six credential fields, and there is no `onShutdown` hook.** Adding
|
||||||
`HOST_API_VERSION` is frozen both are free to revisit; after the freeze, adding is compatible and
|
to the context costs a minor bump and removing one a major, so the shape errs small elsewhere. Pools
|
||||||
removing is not, so the shape errs small elsewhere. Pools handed to a plugin are reaped on process
|
handed to a plugin are reaped on process exit — revisit if a plugin ever needs an orderly drain.
|
||||||
exit — revisit if a plugin ever needs an orderly drain. **Valid while the freeze holds.**
|
|
||||||
- **`config/` is still a plain dir — no `package.json` of its own**, or `#menu-config` resolves
|
- **`config/` is still a plain dir — no `package.json` of its own**, or `#menu-config` resolves
|
||||||
against that instead and boot fails loud. An operator's menu override has no use for
|
against that instead and boot fails loud. An operator's menu override has no use for
|
||||||
dependencies; if that changes, it needs the same package treatment.
|
dependencies; if that changes, it needs the same package treatment.
|
||||||
@@ -300,19 +299,6 @@ Revisit only if the stated reason stops holding.
|
|||||||
file between login and logout. Same class: concurrent runs share the workspace dir, so ci.sh's
|
file between login and logout. Same class: concurrent runs share the workspace dir, so ci.sh's
|
||||||
web-image build races another run's container creation on the `<project>-web` tag. Accepted for a
|
web-image build races another run's container creation on the `<project>-web` tag. Accepted for a
|
||||||
single-maintainer cadence; serialize with a workflow `concurrency` group if it ever bites.
|
single-maintainer cadence; serialize with a workflow `concurrency` group if it ever bites.
|
||||||
- **Plainpages is pre-announcement: no tags, no releases.** `auto-release` is gated behind the
|
|
||||||
`AUTO_RELEASE` Actions variable (unset ⇒ skipped, the fail-safe direction on every unknown-`vars`
|
|
||||||
path) — a version only communicates to consumers and there are none. Two couplings:
|
|
||||||
`registry-cleanup` keeps a hash image only while its commit is a branch head *or* release-tagged, so
|
|
||||||
with zero tags a hand-cut tag must sit on `main`'s tip; and `mirror.yml` pushes tags with `--prune`
|
|
||||||
(its `fetch-tags: true` is load-bearing), so a tag or Release created on GitHub is swept away and
|
|
||||||
releases are cut on Gitea only. Valid until the maintainer says Plainpages is ready to show people.
|
|
||||||
- **A stricter manifest rule breaks already-copied plugins**, and while `HOST_API_VERSION` is frozen
|
|
||||||
the failure names a symptom rather than the cause — `checkApiVersion` would refuse a stale plugin by
|
|
||||||
*version*, but only once the freeze lifts. Until then a stricter rule ships with a README →
|
|
||||||
Upgrading entry and a re-copy hint in the discovery error. Fail-loud stays right either way: the
|
|
||||||
alternative is a route gating on a name nobody can be granted, i.e. a permanent silent 403.
|
|
||||||
**Valid while `HOST_API_VERSION` stays frozen.**
|
|
||||||
|
|
||||||
## Docker only — no host tooling
|
## Docker only — no host tooling
|
||||||
|
|
||||||
@@ -361,15 +347,15 @@ one-time setup. A file-map or table row gets a clause, not a paragraph.
|
|||||||
- Pin all dependencies and Docker images to exact, human-readable **semantic versions** — never
|
- Pin all dependencies and Docker images to exact, human-readable **semantic versions** — never
|
||||||
ranges (`^`, `~`) and never digests. npm deps via `.npmrc` (`save-exact=true`) + `npm ci`; images
|
ranges (`^`, `~`) and never digests. npm deps via `.npmrc` (`save-exact=true`) + `npm ci`; images
|
||||||
by tag.
|
by tag.
|
||||||
- **`HOST_API_VERSION` is frozen at 1.0.0 until the first external install**, even for additive
|
- **`HOST_API_VERSION` is a live promise as of the v0.1.0 release** (the app's version and the
|
||||||
contract changes. With no third-party plugin in the wild a bump can only produce noise. The
|
contract's move independently). Bump it with every contract change, per the table in README →
|
||||||
promotion trigger is the first external plugin — from then on follow the versioning table in
|
Contract versioning: major on a breaking one, minor on an additive one. **The contract surface
|
||||||
README → Contract versioning. **The frozen surface includes `views/partials/*.ejs`**: the view
|
includes `views/partials/*.ejs`** — the view resolver makes every core partial an `include()` root
|
||||||
resolver makes every core partial an `include()` root for a plugin's views, so their option names
|
for a plugin's views, so their option names and emitted markup are author-visible. Know the hole
|
||||||
and emitted markup are author-visible. Know the hole that leaves — discovery fails loud on a bad
|
that leaves: discovery fails loud on a bad `apiVersion`, but `include("menu", { open: true })`
|
||||||
`apiVersion`, but `include("menu", { open: true })` silently ignores a dropped option. Promotion
|
silently ignores a dropped option, so the partial vocabulary is a surface the version check cannot
|
||||||
must cover the partial vocabulary too.
|
police for you.
|
||||||
- **The frozen surface also includes the packaging promises** (README → Plugin dependencies): the
|
- **The contract surface also includes the packaging promises** (README → Plugin dependencies): the
|
||||||
barrel is ambient at `/node_modules` with nothing for a plugin to declare, `"type": "module"` is
|
barrel is ambient at `/node_modules` with nothing for a plugin to declare, `"type": "module"` is
|
||||||
mandatory, and the host neither upgrades nor dedupes a plugin's dependencies. Same hole as the
|
mandatory, and the host neither upgrades nor dedupes a plugin's dependencies. Same hole as the
|
||||||
partials — move the publish point, rename the package or start hoisting and every installed plugin
|
partials — move the publish point, rename the package or start hoisting and every installed plugin
|
||||||
@@ -377,9 +363,9 @@ one-time setup. A file-map or table row gets a clause, not a paragraph.
|
|||||||
build-time dedupe for baked images stays open, module-instance sharing stays unpromised.
|
build-time dedupe for baked images stays open, module-instance sharing stays unpromised.
|
||||||
- **Publishing `@plainpages/plugin-api` to a registry is deferred, not rejected.** Today it is
|
- **Publishing `@plainpages/plugin-api` to a registry is deferred, not rejected.** Today it is
|
||||||
`private` and shaped as a shim — `index.ts` re-exports `../src/…`, so `npm pack` would ship a
|
`private` and shaped as a shim — `index.ts` re-exports `../src/…`, so `npm pack` would ship a
|
||||||
broken tree. The trigger is the same as the freeze's: the first external plugin, which is also the
|
broken tree. The trigger is the first plugin author outside this repo — the first who cannot
|
||||||
first author who cannot typecheck against a mounted host tree. Whoever does it must first make the
|
typecheck against a mounted host tree. Whoever does it must first make the artifact self-contained
|
||||||
artifact self-contained (types-only `.d.ts`, or move the barrel into `plugin-api/`).
|
(types-only `.d.ts`, or move the barrel into `plugin-api/`).
|
||||||
- A plugin's `apiVersion` is a **hand-written literal** semver — the host version it was built
|
- A plugin's `apiVersion` is a **hand-written literal** semver — the host version it was built
|
||||||
against — bumped by hand on rebuild, **never** the host's `HOST_API_VERSION` constant. Importing
|
against — bumped by hand on rebuild, **never** the host's `HOST_API_VERSION` constant. Importing
|
||||||
the constant makes every plugin always equal the host, so `checkApiVersion` can never fire.
|
the constant makes every plugin always equal the host, so `checkApiVersion` can never fire.
|
||||||
|
|||||||
+3
-3
@@ -22,7 +22,7 @@ so there is nothing to clone. In an empty directory, save this as `compose.yml`:
|
|||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
image: larvit/plainpages:0.0.2
|
image: larvit/plainpages:0.1.0
|
||||||
ports:
|
ports:
|
||||||
- "3000:3000"
|
- "3000:3000"
|
||||||
environment:
|
environment:
|
||||||
@@ -41,7 +41,7 @@ services:
|
|||||||
|
|
||||||
# One-shot, idempotent seed: signing key if absent + the admin@plainpages.local / admin user.
|
# One-shot, idempotent seed: signing key if absent + the admin@plainpages.local / admin user.
|
||||||
bootstrap:
|
bootstrap:
|
||||||
image: larvit/plainpages:0.0.2
|
image: larvit/plainpages:0.1.0
|
||||||
command: node src/auth/bootstrap.ts
|
command: node src/auth/bootstrap.ts
|
||||||
depends_on:
|
depends_on:
|
||||||
kratos:
|
kratos:
|
||||||
@@ -143,7 +143,7 @@ volumes:
|
|||||||
Extract the Ory config the image ships, then start:
|
Extract the Ory config the image ships, then start:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker run --rm larvit/plainpages:0.0.2 tar -cf - ory | tar -xf -
|
docker run --rm larvit/plainpages:0.1.0 tar -cf - ory | tar -xf -
|
||||||
mkdir -p plugins
|
mkdir -p plugins
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1375,7 +1375,7 @@ Gitea Actions (`.gitea/workflows/`) runs the pipeline; the test job runs
|
|||||||
| `release.yml` | push of a `vX.Y.Z` tag | re-tag that commit's image as `X.Y.Z`, `X.Y`, `X`, `latest`; sync those tags to Docker Hub |
|
| `release.yml` | push of a `vX.Y.Z` tag | re-tag that commit's image as `X.Y.Z`, `X.Y`, `X`, `latest`; sync those tags to Docker Hub |
|
||||||
| `mirror.yml` | push to `main` or any tag, or manual | force-push `main` + tags (pruning deleted ones) to the [GitHub mirror](https://github.com/larvit/plainpages) |
|
| `mirror.yml` | push to `main` or any tag, or manual | force-push `main` + tags (pruning deleted ones) to the [GitHub mirror](https://github.com/larvit/plainpages) |
|
||||||
| `registry-cleanup.yml` | nightly cron, or manual | delete registry images that are neither release-tagged nor a branch head |
|
| `registry-cleanup.yml` | nightly cron, or manual | delete registry images that are neither release-tagged nor a branch head |
|
||||||
| `renovate.yml` | nightly cron, or manual | open dependency-update PRs, automerge them once the gate is green; the release-tag job only runs when `AUTO_RELEASE` is `true` |
|
| `renovate.yml` | nightly cron, or manual | open dependency-update PRs, automerge them once the gate is green, then cut a release tag for what merged |
|
||||||
|
|
||||||
`main` is not re-tested on push — its commits are meant to arrive already green from a
|
`main` is not re-tested on push — its commits are meant to arrive already green from a
|
||||||
gated branch, so the status check to gate a merge on is `CI / full-gate (push)`.
|
gated branch, so the status check to gate a merge on is `CI / full-gate (push)`.
|
||||||
@@ -1417,15 +1417,9 @@ images, and the Playwright runner + its browser image — and every bump keeps t
|
|||||||
exact. Each PR runs the normal gate on its `renovate/*` branch and automerges once
|
exact. Each PR runs the normal gate on its `renovate/*` branch and automerges once
|
||||||
`CI / full-gate (push)` is green; only a red gate needs a human.
|
`CI / full-gate (push)` is green; only a red gate needs a human.
|
||||||
|
|
||||||
**Releases are paused.** Plainpages is pre-announcement: the repository carries **no tags**, so
|
**Auto-release on dependency updates** — a second job in `renovate.yml` (`auto-release`) cuts **one**
|
||||||
neither `release.yml` nor Docker Hub has a version to promote. Turn releasing back on by setting the
|
`vX.Y.Z` tag per run covering the renovate-bot commits merged to `main` since the last tag, and
|
||||||
Actions variable `AUTO_RELEASE` to `true`, or cut a `vX.Y.Z` tag by hand **on `main`'s tip** — with
|
**skips** when the tip isn't a Renovate commit or nothing new merged.
|
||||||
nothing tagged the nightly cleanup keeps only branch-head images, so an older commit's image is
|
|
||||||
already gone and `release.yml` would fail loud.
|
|
||||||
|
|
||||||
**Auto-release on dependency updates** — a second job in `renovate.yml` (`auto-release`, gated on
|
|
||||||
`AUTO_RELEASE`) cuts **one** `vX.Y.Z` tag per run covering the renovate-bot commits merged to `main`
|
|
||||||
since the last tag, and **skips** when the tip isn't a Renovate commit or nothing new merged.
|
|
||||||
Renovate stamps each commit with a `Release-Bump: <updateType>` trailer and
|
Renovate stamps each commit with a `Release-Bump: <updateType>` trailer and
|
||||||
[`auto-release/next-version.ts`](auto-release/next-version.ts) turns the highest one into the next
|
[`auto-release/next-version.ts`](auto-release/next-version.ts) turns the highest one into the next
|
||||||
version — pre-1.0 it never auto-crosses into `1.0.0`. It is **tag-only**: the tag hands off to
|
version — pre-1.0 it never auto-crosses into `1.0.0`. It is **tag-only**: the tag hands off to
|
||||||
@@ -1502,9 +1496,9 @@ docker compose up -d --build
|
|||||||
```
|
```
|
||||||
|
|
||||||
Do the same for any other folder you copied out of `examples/`. A plugin you wrote yourself needs the
|
Do the same for any other folder you copied out of `examples/`. A plugin you wrote yourself needs the
|
||||||
manifest change the error names. Once [`HOST_API_VERSION`](#contract-versioning) starts moving a
|
manifest change the error names. A host contract change big enough to move
|
||||||
stale plugin will be refused by **version** instead; it is frozen at `1.0.0` until the first external
|
[`HOST_API_VERSION`](#contract-versioning) shows up earlier and more precisely — discovery refuses the
|
||||||
plugin exists, so for now the error names the rule it tripped.
|
plugin by **version** before any rule gets a chance to trip.
|
||||||
|
|
||||||
Two paths in the checkout are load-bearing and must stay clear of root-owned leftovers:
|
Two paths in the checkout are load-bearing and must stay clear of root-owned leftovers:
|
||||||
`node_modules/` must not exist (deps live at `/node_modules`, and anything at `/app/node_modules`
|
`node_modules/` must not exist (deps live at `/node_modules`, and anything at `/app/node_modules`
|
||||||
|
|||||||
Reference in New Issue
Block a user