diff --git a/README.md b/README.md index 91866b7..ebe5110 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ folder under `plugins/` goes live after a restart. Create `plugins/hello/plugin. import { definePlugin } from "@plainpages/plugin-api"; export default definePlugin({ - apiVersion: "0.2.0", + apiVersion: "0.1.0", nav: [{ href: "/hello", id: "hello", label: "Hello", public: true }], routes: [ { method: "GET", path: "/", public: true, handler: () => ({ html: "

Hello from my plugin

" }) }, @@ -348,7 +348,7 @@ import { definePlugin } from "@plainpages/plugin-api"; import { listThings, createThings } from "./handlers.ts"; export default definePlugin({ - apiVersion: "0.2.0", // semver string of the host contract this plugin was built against (see Versioning) + apiVersion: "0.1.0", // semver string of the host contract this plugin was built against (see Versioning) // Nav fragment, merged into the global menu and permission-filtered per user. // `icon` is a Lucide icon by its sprite id (src/ui/icons.ts). @@ -468,7 +468,7 @@ import { definePlugin } from "@plainpages/plugin-api"; import { landing, board } from "./pages.ts"; export default definePlugin({ - apiVersion: "0.2.0", + apiVersion: "0.1.0", home: landing, // owns "/" — the public front page dashboard: board, // owns "/dashboard" — the post-login app home }); @@ -751,7 +751,7 @@ import { definePlugin } from "@plainpages/plugin-api"; let sql: ReturnType; export default definePlugin({ - apiVersion: "0.2.0", + apiVersion: "0.1.0", storage: true, hooks: { onBoot: async (boot) => { @@ -1513,21 +1513,6 @@ box. The web app waits for Kratos + Keto healthy *and* the bootstrap to finish b ## Upgrading -### 0.1.x → 0.2.0: `apiVersion` now names the Plainpages release - -`0.1.0` shipped a host reporting contract version `1.0.0` — a second number that tracked the plugin -contract separately from the release. There is only one number now, and it is the release: a host on -`0.2.0` reports `0.2.0`. Every plugin must say so, or discovery refuses it by version and **aborts -boot**: - -```ts -// plugins//plugin.ts -apiVersion: "0.2.0", // was "1.0.0" -``` - -Re-copy anything taken from `examples/` (below) to pick this up. Nothing else in the contract -changed, so a plugin that boots after the edit needs no further work. - **Re-copy your drop-in plugins.** Anything under `plugins/` is *your* copy — the host never updates it. A plugin copied from `examples/` is still the old one after you pull, and the host may have tightened a manifest rule since. Discovery fails loud at boot rather than running a plugin it can't diff --git a/examples/plugins/admin/plugin.ts b/examples/plugins/admin/plugin.ts index ec19a8b..ece09d0 100644 --- a/examples/plugins/admin/plugin.ts +++ b/examples/plugins/admin/plugin.ts @@ -26,7 +26,7 @@ const groups = on("groups"); const clients = on("oauth2-clients"); export default definePlugin({ - apiVersion: "0.2.0", // the host contract this was built against — a literal, never HOST_API_VERSION + apiVersion: "0.1.0", // the host contract this was built against — a literal, never HOST_API_VERSION nav: [ADMIN_NAV], diff --git a/examples/plugins/scheduling/plugin.ts b/examples/plugins/scheduling/plugin.ts index 2bf86eb..9b3cb1a 100644 --- a/examples/plugins/scheduling/plugin.ts +++ b/examples/plugins/scheduling/plugin.ts @@ -11,7 +11,7 @@ const upstreamUrl = process.env["SCHEDULING_UPSTREAM"] ?? "http://shifts-upstrea const upstream = createUpstream(upstreamUrl); export default definePlugin({ - apiVersion: "0.2.0", // the host contract this was built against — a literal, never HOST_API_VERSION + apiVersion: "0.1.0", // the host contract this was built against — a literal, never HOST_API_VERSION // onBoot runs after discovery, before the server listens: validate the plugin's own config so a // typo'd SCHEDULING_UPSTREAM fails the boot loudly instead of degrading every request later. diff --git a/release-tooling/contract-version.test.ts b/release-tooling/contract-version.test.ts index c48a994..8f3d780 100644 --- a/release-tooling/contract-version.test.ts +++ b/release-tooling/contract-version.test.ts @@ -12,7 +12,7 @@ test("readHostApiVersion pulls the constant out of the real source, and returns test("bumping HOST_API_VERSION is a deliberate act, so pin the shipped value", () => { // Not a substitute for the release gate — this test cannot see a tag. It is the tripwire that // makes an accidental edit fail here rather than at release time. - assert.equal(readHostApiVersion(readFileSync("src/plugin-host/plugin.ts", "utf8")), "0.2.0"); + assert.equal(readHostApiVersion(readFileSync("src/plugin-host/plugin.ts", "utf8")), "0.1.0"); }); test("every author-facing apiVersion sample matches the shipped contract", () => { @@ -38,22 +38,21 @@ test("every author-facing apiVersion sample matches the shipped contract", () => }); test("checkTagMatchesContract: major.minor must agree, patch may lag", () => { - assert.equal(checkTagMatchesContract("v0.2.0", "0.2.0").ok, true); - assert.equal(checkTagMatchesContract("v0.2.7", "0.2.0").ok, true); // auto-release cut patches - assert.equal(checkTagMatchesContract("0.2.0", "0.2.0").ok, true); // bare tag, no v - assert.equal(checkTagMatchesContract("v0.3.0", "0.2.0").ok, false); // plugin-visible, needs a bump - assert.equal(checkTagMatchesContract("v0.1.0", "0.2.0").ok, false); // the previously released line - assert.equal(checkTagMatchesContract("v1.0.0", "0.2.0").ok, false); + assert.equal(checkTagMatchesContract("v0.1.0", "0.1.0").ok, true); + assert.equal(checkTagMatchesContract("v0.1.7", "0.1.0").ok, true); // auto-release cut patches + assert.equal(checkTagMatchesContract("0.1.0", "0.1.0").ok, true); // bare tag, no v + assert.equal(checkTagMatchesContract("v0.2.0", "0.1.0").ok, false); // plugin-visible, needs a bump + assert.equal(checkTagMatchesContract("v1.0.0", "0.1.0").ok, false); }); test("checkTagMatchesContract names what to fix rather than just failing", () => { - const res = checkTagMatchesContract("v0.3.0", "0.2.0"); + const res = checkTagMatchesContract("v0.2.0", "0.1.0"); assert.equal(res.ok, false); - assert.match(res.ok === false ? res.error : "", /HOST_API_VERSION to 0\.3\.0/); + assert.match(res.ok === false ? res.error : "", /HOST_API_VERSION to 0\.2\.0/); }); test("checkTagMatchesContract rejects junk on either side without throwing", () => { - assert.equal(checkTagMatchesContract("v0.2.0", null).ok, false); // constant not found - assert.equal(checkTagMatchesContract("nope", "0.2.0").ok, false); - assert.equal(checkTagMatchesContract("v0.2.0", "1.0").ok, false); + assert.equal(checkTagMatchesContract("v0.1.0", null).ok, false); // constant not found + assert.equal(checkTagMatchesContract("nope", "0.1.0").ok, false); + assert.equal(checkTagMatchesContract("v0.1.0", "1.0").ok, false); }); diff --git a/release-tooling/dockerhub-overview.md.tmpl b/release-tooling/dockerhub-overview.md.tmpl index 51a9047..6921772 100644 --- a/release-tooling/dockerhub-overview.md.tmpl +++ b/release-tooling/dockerhub-overview.md.tmpl @@ -182,7 +182,7 @@ into the app. Create `plugins/hello/plugin.ts`: import { definePlugin } from "@plainpages/plugin-api"; export default definePlugin({ - apiVersion: "0.2.0", + apiVersion: "0.1.0", nav: [{ href: "/hello", id: "hello", label: "Hello", public: true }], routes: [ { method: "GET", path: "/", public: true, handler: () => ({ html: "

Hello from my plugin

" }) }, diff --git a/src/plugin-host/plugin.test.ts b/src/plugin-host/plugin.test.ts index 9ddc473..6fde120 100644 --- a/src/plugin-host/plugin.test.ts +++ b/src/plugin-host/plugin.test.ts @@ -20,7 +20,7 @@ import { AUTH_FLOWS } from "../auth/flow-view.ts"; // HOST_API_VERSION would always equal the host and defeat the check. No `id`/`basePath` — the // host derives both from the plugin's folder name. const scheduling: PluginManifest = definePlugin({ - apiVersion: "0.2.0", + apiVersion: "0.1.0", hooks: { onBoot: () => {} }, nav: [{ children: [{ href: "/scheduling/shifts", id: "scheduling:shifts", label: "Shifts", permission: "scheduling:read" }], diff --git a/src/plugin-host/plugin.ts b/src/plugin-host/plugin.ts index 7f94d16..493d03c 100644 --- a/src/plugin-host/plugin.ts +++ b/src/plugin-host/plugin.ts @@ -11,7 +11,7 @@ import type { StorageCredentials } from "./storage.ts"; // The Plainpages release this contract ships in — one version, not a second one to track. Its // major.minor must equal the release tag's; `release.yml` refuses to promote a tag that disagrees. // The patch digit may lag, since checkApiVersion ignores patch and auto-release cuts patches itself. -export const HOST_API_VERSION = "0.2.0"; +export const HOST_API_VERSION = "0.1.0"; export type HttpMethod = "DELETE" | "GET" | "HEAD" | "PATCH" | "POST" | "PUT"; diff --git a/views/index.ejs b/views/index.ejs index 12ed83a..118c8e2 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -14,7 +14,7 @@

${t("dashboard.starter.intro")}

${t("dashboard.starter.replace")}

export default definePlugin({
-  apiVersion: "0.2.0",
+  apiVersion: "0.1.0",
   // view names plugins/<id>/views/<view>.ejs, rendered in this same shell
   dashboard: (ctx) => ({ view: "dashboard", data: { /* … */ } }),
 });