diff --git a/AGENTS.md b/AGENTS.md index 7d08e76..0410c47 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -347,6 +347,11 @@ 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 ranges (`^`, `~`) and never digests. npm deps via `.npmrc` (`save-exact=true`) + `npm ci`; images by tag. +- **Touching dependencies means revisiting `renovate.json`.** `Release-Bump` is an *allowlist* — only + the root `package.json`'s runtime deps, the `Dockerfile` base and `compose.yml`'s services carry the + trailer, so a dependency added anywhere else never escalates the release version and nothing fails to + say so. A new manifest, compose file, custom manager or dep type is a decision: can it reach a + running Plainpages? If yes it needs a rule; if no, record nothing and let it ride the next patch. - **`HOST_API_VERSION` is a live promise as of the v0.1.0 release** (the app's version and the contract's move independently). Bump it with every contract change, per the table in README → Contract versioning: major on a breaking one, minor on an additive one. **The contract surface diff --git a/README.md b/README.md index 6a09cde..51f9d64 100644 --- a/README.md +++ b/README.md @@ -1419,10 +1419,13 @@ exact. Each PR runs the normal gate on its `renovate/*` branch and automerges on **Auto-release on dependency updates** — a second job in `renovate.yml` (`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: ` trailer and +**skips** when the tip isn't a Renovate commit or nothing new merged. Renovate stamps a +`Release-Bump: ` trailer onto the updates that reach a running Plainpages — the root +`package.json`'s runtime dependencies, the image base, and `compose.yml`'s services — and [`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`. `updateType` rates the *dependency's* own jump, +so the trailer is an allowlist in [`renovate.json`](renovate.json): a devDependency, E2E or CI-only +bump carries none and rides the next patch release instead of escalating it. It is **tag-only**: the tag hands off to `release.yml`, and is pushed with renovate-bot's PAT so that workflow actually fires (a tag pushed by the built-in Actions token wouldn't trigger it). `HOST_API_VERSION` is never touched here. diff --git a/auto-release/next-version.test.ts b/auto-release/next-version.test.ts index 950ec88..c49b41f 100644 --- a/auto-release/next-version.test.ts +++ b/auto-release/next-version.test.ts @@ -35,6 +35,8 @@ test("nextVersion at/after 1.0.0: literal semver", () => { assert.equal(nextVersion("v1.2.3", "major"), "v2.0.0"); assert.equal(nextVersion("v1.2.3", "minor"), "v1.3.0"); assert.equal(nextVersion("v1.2.3", "patch"), "v1.2.4"); + // the whole chain: a major dependency bump releases a major host, once the 0.x shift-down is gone + assert.equal(nextVersion("v1.2.3", maxLevel(["patch", "major"])), "v2.0.0"); }); test("nextVersion rejects a tag that is not vX.Y.Z", () => { diff --git a/e2e-tests/package-lock.json b/e2e-tests/package-lock.json index d458f47..18eb3a8 100644 --- a/e2e-tests/package-lock.json +++ b/e2e-tests/package-lock.json @@ -1,12 +1,10 @@ { "name": "plainpages-e2e", - "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "plainpages-e2e", - "version": "0.1.0", "devDependencies": { "@playwright/test": "1.62.1" } diff --git a/e2e-tests/package.json b/e2e-tests/package.json index ea57bb6..83bb4d5 100644 --- a/e2e-tests/package.json +++ b/e2e-tests/package.json @@ -1,6 +1,5 @@ { "name": "plainpages-e2e", - "version": "0.1.0", "private": true, "description": "Playwright E2E: design-system parity (visual), auth refresh, OAuth2 login/consent, and the full browser flow (login/menu/CRUD/plugin/logout).", "type": "module", diff --git a/package-lock.json b/package-lock.json index fe90016..0137f04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,10 @@ { "name": "plainpages", - "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "plainpages", - "version": "0.1.0", "dependencies": { "@larvit/log": "2.3.0", "ejs": "6.0.1", diff --git a/package.json b/package.json index 8227ccd..322423e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,5 @@ { "name": "plainpages", - "version": "0.1.0", "private": true, "type": "module", "engines": { diff --git a/renovate.json b/renovate.json index e3366e4..3494f80 100644 --- a/renovate.json +++ b/renovate.json @@ -4,8 +4,32 @@ "description": "ignorePaths overrides config:recommended's :ignoreModulesAndTests, which ignores **/examples/** — an example plugin's dependencies get update PRs like any other manifest here", "ignorePaths": ["**/node_modules/**"], "automerge": true, - "commitBody": "Release-Bump: {{{updateType}}}", "packageRules": [ + { + "description": "The host's own runtime deps. Release-Bump is opt-in per surface (README → CI/CD) because updateType rates the dependency's own jump, not its effect here — unscoped, the bot's self-update bumped the product", + "matchDepTypes": ["dependencies"], + "matchFileNames": ["package.json"], + "matchManagers": ["npm"], + "commitBody": "Release-Bump: {{{updateType}}}" + }, + { + "description": "The shipped image's base — e2e-tests/Dockerfile is test-only", + "matchFileNames": ["Dockerfile"], + "matchManagers": ["dockerfile"], + "commitBody": "Release-Bump: {{{updateType}}}" + }, + { + "description": "The production topology — compose.override.yml is dev, e2e-tests/compose.*.yml are test", + "matchFileNames": ["compose.yml"], + "matchManagers": ["docker-compose"], + "commitBody": "Release-Bump: {{{updateType}}}" + }, + { + "description": "node is pinned to one version across Dockerfile, dev, E2E and CI, so Renovate moves them in a single branch whose commitBody would otherwise depend on upgrade order — the Dockerfile copy ships, so any node bump is a product change", + "matchDatasources": ["docker"], + "matchPackageNames": ["node"], + "commitBody": "Release-Bump: {{{updateType}}}" + }, { "description": "Ory services share one release train - update kratos, keto and hydra together", "matchDatasources": ["docker"],