1 Commits

Author SHA1 Message Date
renovate-bot 42ded73459 chore(deps): update playwright to v1.61.1
renovate/artifacts Artifact file update failure
CI / full-gate (push) Failing after 45s
2026-07-05 07:02:11 +00:00
14 changed files with 15 additions and 156 deletions
+1 -1
View File
@@ -19,4 +19,4 @@ jobs:
run: |
docker run --rm -v "$PWD:/repo" -w /repo \
-e REGISTRY_TOKEN -e REGISTRY_USER -e REPO_TOKEN -e REPOSITORY -e SERVER_URL \
node:24.18.0-alpine3.24 node registry-cleanup/cleanup.ts
node:24.16.0-alpine3.24 node registry-cleanup/cleanup.ts
+1 -38
View File
@@ -19,41 +19,4 @@ jobs:
-e RENOVATE_PLATFORM=gitea \
-e RENOVATE_REPOSITORIES=${{ github.repository }} \
-e RENOVATE_TOKEN \
renovate/renovate:43.252.1
# After the renovate job, cut ONE tag covering the renovate-bot commits merged to main since the
# last tag (batch per run). Targets origin/main — the real post-merge tip; the checkout SHA is the
# trigger-time tip and lags the merges this run made. Skips when main's tip isn't a Renovate commit
# (a human owns that release) or nothing new merged. ff-only merges keep the renovate commit's
# 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
# already-built image; pushed with renovate-bot's PAT so release.yml fires (the built-in token won't).
auto-release:
runs-on: docker-host
needs: renovate
steps:
- uses: actions/checkout@v4.2.2
with:
fetch-depth: 0
- name: Tag a release for what Renovate merged
env:
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
REPO: ${{ github.repository }}
run: |
set -euo pipefail
git fetch --force --quiet origin '+refs/heads/main:refs/remotes/origin/main' '+refs/tags/*:refs/tags/*'
if [ "$(git log -1 --format='%ae' origin/main)" != "renovate@larvit.se" ]; then
echo "main tip not authored by Renovate — a human owns this release; skipping"; exit 0
fi
LATEST=$(git tag -l 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -n1)
LATEST=${LATEST:-v0.0.0}
if [ -z "$(git log "${LATEST}..origin/main" --author='renovate@larvit.se' --format='%H')" ]; then
echo "No untagged renovate commits since ${LATEST} — nothing to release"; exit 0
fi
BUMPS=$(git log "${LATEST}..origin/main" --author='renovate@larvit.se' \
--format='%(trailers:key=Release-Bump,valueonly)' | { grep -vx '' || true; })
NEXT=$(docker run --rm -v "$PWD:/repo" -w /repo node:24.16.0-alpine3.24 \
node auto-release/next-version.ts "$LATEST" $BUMPS)
echo "Releasing $LATEST -> $NEXT"
git tag "$NEXT" origin/main
git push "https://renovate-bot:${RENOVATE_TOKEN}@gitea.larvit.se/${REPO}.git" "$NEXT"
renovate/renovate:43.251.0
+1 -1
View File
@@ -1,5 +1,5 @@
# Node 24 runs TypeScript directly (type stripping) — no build step. Pinned exact tag.
FROM node:24.18.0-alpine3.24
FROM node:24.16.0-alpine3.24
WORKDIR /app
+1 -17
View File
@@ -1177,7 +1177,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 |
| `mirror.yml` | push to `main` or any tag, or manual | force-push `main` + tags 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 |
| `renovate.yml` | nightly cron, or manual | open dependency-update PRs, automerge them once the gate is green, then cut one release tag for the run |
| `renovate.yml` | nightly cron, or manual | open dependency-update PRs and automerge them once the gate is green |
`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)`.
@@ -1255,22 +1255,6 @@ this repo and store its Gitea PAT as the Actions **secret** `RENOVATE_TOKEN`. Un
exists, the nightly job fails loud (and, like the other secrets, a `GITEA_` prefix is
rejected).
**Auto-release on dependency updates** — a second job in `renovate.yml` (`auto-release`, `needs:
renovate`) cuts **one** `vX.Y.Z` tag per run covering the renovate-bot commits merged to `main`
since the last tag (it targets `origin/main`, and **skips** when the tip isn't a Renovate commit —
a human owns that release — or when nothing new merged). Renovate stamps every commit with a
`Release-Bump: <updateType>` trailer (`commitBody` in `renovate.json`); the job takes the highest
trailer on those commits — any dependency's `major`/`minor`/`patch` maps straight through,
defaulting to `patch`.
**Pre-1.0 the level shifts down** — a dep major bumps the `0.x` minor, dep minor/patch bump the
`0.x` patch (see [`auto-release/next-version.ts`](auto-release/next-version.ts), unit-tested) — so
routine bumps never auto-cross into `1.0.0`; `1.0.0` stays a deliberate hand-cut tag. It's
**tag-only** (no source commits): the tag hands off to `release.yml`, which promotes the
already-built image, and is pushed with renovate-bot's PAT so `release.yml` actually fires (a tag
pushed by the built-in Actions token wouldn't trigger it). The plugin-contract version
(`HOST_API_VERSION`) is deliberately **not** touched here — it moves only when the plugin API
itself changes, by hand.
**One-time server setup** — register an
[act_runner](https://docs.gitea.com/usage/actions/act-runner) in host mode with the label
`docker-host` (config: `labels: ["docker-host:host"]`) on a machine with Docker Engine +
-43
View File
@@ -1,43 +0,0 @@
import assert from "node:assert/strict";
import { test } from "node:test";
import { bumpFromUpdateType, maxLevel, nextVersion } from "./next-version.ts";
test("bumpFromUpdateType: only major/minor keep their level; everything else is patch", () => {
assert.equal(bumpFromUpdateType("major"), "major");
assert.equal(bumpFromUpdateType("minor"), "minor");
assert.equal(bumpFromUpdateType("patch"), "patch");
assert.equal(bumpFromUpdateType("digest"), "patch");
assert.equal(bumpFromUpdateType("pin"), "patch");
assert.equal(bumpFromUpdateType("lockFileMaintenance"), "patch");
assert.equal(bumpFromUpdateType(""), "patch");
});
test("maxLevel: defaults to patch, escalates on the highest level present", () => {
assert.equal(maxLevel([]), "patch");
assert.equal(maxLevel(["patch"]), "patch");
assert.equal(maxLevel(["patch", "minor"]), "minor");
assert.equal(maxLevel(["minor", "major", "patch"]), "major");
assert.equal(maxLevel(["digest", "pin"]), "patch");
assert.equal(maxLevel(["", "bogus"]), "patch"); // unknown → patch, never throws
});
test("nextVersion pre-1.0 (major===0): shift down so we never auto-cross into 1.0.0", () => {
// dep major → 0.x minor (the 0.x "breaking" slot); dep minor/patch → 0.x patch
assert.equal(nextVersion("v0.0.2", "major"), "v0.1.0");
assert.equal(nextVersion("v0.0.2", "minor"), "v0.0.3");
assert.equal(nextVersion("v0.0.2", "patch"), "v0.0.3");
assert.equal(nextVersion("v0.3.4", "major"), "v0.4.0");
assert.equal(nextVersion("v0.3.4", "minor"), "v0.3.5");
assert.equal(nextVersion("v0.3.4", "patch"), "v0.3.5");
});
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");
});
test("nextVersion rejects a tag that is not vX.Y.Z", () => {
assert.throws(() => nextVersion("1.2.3", "patch"), /vX\.Y\.Z/);
assert.throws(() => nextVersion("vx.y.z", "patch"), /vX\.Y\.Z/);
});
-43
View File
@@ -1,43 +0,0 @@
// Pure release-version math for the Renovate auto-release (see renovate.yml → auto-release job,
// README → CI/CD). Renovate stamps each commit with a `Release-Bump: <updateType>` trailer; the
// workflow feeds those values here to pick the next `vX.Y.Z` tag. Kept side-effect-free and unit
// tested (next-version.test.ts) — the git/tag/push side lives in the workflow shell.
export type Bump = "major" | "minor" | "patch";
// A dependency change is always at least a patch; only a real major/minor escalates.
export function bumpFromUpdateType(updateType: string): Bump {
if (updateType === "major") return "major";
if (updateType === "minor") return "minor";
return "patch";
}
export function maxLevel(updateTypes: string[]): Bump {
let level: Bump = "patch";
for (const updateType of updateTypes) {
const bump = bumpFromUpdateType(updateType);
if (bump === "major") return "major";
if (bump === "minor") level = "minor";
}
return level;
}
// Pre-1.0 (major===0) shifts every level down one notch, so a dependency major only bumps the 0.x
// minor and we never auto-cross into 1.0.0 — that stays a deliberate human milestone.
export function nextVersion(latestTag: string, level: Bump): string {
const match = /^v(\d+)\.(\d+)\.(\d+)$/.exec(latestTag);
if (!match) throw new Error(`latest tag must be vX.Y.Z, got ${JSON.stringify(latestTag)}`);
const major = Number(match[1]);
const minor = Number(match[2]);
const patch = Number(match[3]);
const effective: Bump = major === 0 ? (level === "major" ? "minor" : "patch") : level;
if (effective === "major") return `v${major + 1}.0.0`;
if (effective === "minor") return `v${major}.${minor + 1}.0`;
return `v${major}.${minor}.${patch + 1}`;
}
// CLI: node auto-release/next-version.ts <latestTag> [updateType...] → prints the next tag.
if (process.argv[1]?.endsWith("/next-version.ts")) {
const [, , latestTag, ...updateTypes] = process.argv;
process.stdout.write(nextVersion(latestTag ?? "", maxLevel(updateTypes)));
}
+1 -1
View File
@@ -27,7 +27,7 @@ services:
# backs it (SCHEDULING_UPSTREAM above points here). Stand-in for the customer's real service —
# stdlib-only, in-memory, no auth. Prod points SCHEDULING_UPSTREAM at the real backend instead.
shifts-upstream:
image: node:24.18.0-alpine3.24
image: node:24.16.0-alpine3.24
command: node /srv/server.ts
restart: unless-stopped
volumes:
+1 -1
View File
@@ -1,6 +1,6 @@
# Playwright runner — browsers preinstalled, pinned to match @playwright/test in e2e-tests/.
# Built/run via e2e-tests/compose.visual.yml; targets the `web` service over the network.
FROM mcr.microsoft.com/playwright:v1.49.1-noble
FROM mcr.microsoft.com/playwright:v1.61.1-noble
WORKDIR /e2e-tests
+3 -3
View File
@@ -54,7 +54,7 @@ services:
# The reference plugin's upstream (examples/shifts-upstream) so /scheduling/shifts shows real rows.
shifts-upstream:
image: node:24.18.0-alpine3.24
image: node:24.16.0-alpine3.24
command: ["node", "/server.ts"]
volumes:
- ./examples/shifts-upstream/server.ts:/server.ts:ro
@@ -67,7 +67,7 @@ services:
# Mock OIDC provider for the SSO login test — stdlib Node, auto-approves, signs an id_token Kratos
# verifies via its jwks. Reachable as the same host (mock-oidc:9000) by both the browser and Kratos.
mock-oidc:
image: node:24.18.0-alpine3.24
image: node:24.16.0-alpine3.24
command: ["node", "/mock-oidc.ts"]
environment:
ISSUER: http://mock-oidc:9000
@@ -82,7 +82,7 @@ services:
# Same-origin gateway: Kratos-owned paths → kratos, everything else → web (e2e-tests/proxy.ts).
proxy:
image: node:24.18.0-alpine3.24
image: node:24.16.0-alpine3.24
command: ["node", "/proxy.ts"]
depends_on:
web:
+1 -1
View File
@@ -8,6 +8,6 @@
"test": "playwright test"
},
"devDependencies": {
"@playwright/test": "1.49.1"
"@playwright/test": "1.61.1"
}
}
+1 -1
View File
@@ -15,7 +15,7 @@
"dev": "node --watch src/server.ts",
"gen-jwks": "node src/auth/gen-jwks.ts",
"typecheck": "tsc --noEmit",
"test": "node --test \"src/**/*.test.ts\" \"plugins/**/*.test.ts\" \"examples/**/*.test.ts\" \"registry-cleanup/**/*.test.ts\" \"auto-release/**/*.test.ts\""
"test": "node --test \"src/**/*.test.ts\" \"plugins/**/*.test.ts\" \"examples/**/*.test.ts\" \"registry-cleanup/**/*.test.ts\""
},
"dependencies": {
"@larvit/log": "2.3.0",
+2 -3
View File
@@ -2,7 +2,6 @@
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"automerge": true,
"commitBody": "Release-Bump: {{{updateType}}}",
"packageRules": [
{
"description": "Ory services share one release train - update kratos, keto and hydra together",
@@ -27,8 +26,8 @@
},
{
"customType": "regex",
"description": "Pin the node image workflow run-steps invoke (registry-cleanup, auto-release)",
"managerFilePatterns": [".gitea/workflows/registry-cleanup.yml", ".gitea/workflows/renovate.yml"],
"description": "Pin the node image registry-cleanup runs cleanup.ts in",
"managerFilePatterns": [".gitea/workflows/registry-cleanup.yml"],
"matchStrings": ["\\snode:(?<currentValue>[0-9][^\\s\"']*)"],
"depNameTemplate": "node",
"datasourceTemplate": "docker"
+1 -2
View File
@@ -11,8 +11,7 @@
- [x] CI/CD - Sync docker images to docker hub after each re-tag to git tags. (`release.yml` pushes the same `X.Y.Z`/`X.Y`/`X`/`latest` tags to `docker.io/larvit/plainpages` after the Gitea re-tag — releases only, no hash tags; auth via the `DOCKERHUB_USER` variable + `DOCKERHUB_TOKEN` secret; documented in README → CI/CD.)
- [x] Write a short text on how to use this docker image to publish on docker hub and save it to README-dockerhub.md (tagline, tags, clone-free quick start — the image ships the Ory config, extracted via `docker run … tar` + a self-contained compose.yml — env table, first plugin; pasted into the Docker Hub overview by hand — noted in README → CI/CD.)
- [x] CI/CD - Setup renovate bot. Check how other repos on this Gitea is setup you can get access to, there should be a number of renovate bot activated ones. (`renovate.yml` runs the self-hosted `renovate/renovate` image nightly against `renovate.json` — this repo only, via the shared `renovate@larvit.se` bot + `RENOVATE_TOKEN` secret, mirroring the `pwrpln/core` pattern; standard managers cover npm/Dockerfiles/compose/gitea-action pins, two custom regex managers cover the image tags embedded in workflow `run:` steps, the Ory + Playwright lockstep sets are grouped, every bump stays an exact pin, and each PR automerges once the gate is green; documented in README → CI/CD.)
- [ ] CI/CD - Renovate: set a read-only `GITHUB_COM_TOKEN` env in `renovate.yml` so Renovate stops hitting github.com rate limits when resolving github-hosted deps (Playwright, lucide, `actions/checkout`) and can fetch changelogs. Non-blocking refinement; needs a read-only GitHub PAT stored as an Actions secret.
- [x] CI/CD - When renovate updates a dependency - also release a new version of plainpages based on what got updated with Renovate. Major typescript? New apiVersion + new major. A tiny patch to ejs? Only patch release etc. Before implementing, explain in detail how you will solve this. (`renovate.yml` gains an `auto-release` job (`needs: renovate`) that cuts one `vX.Y.Z` tag per run for what Renovate merged; level = highest `Release-Bump:` trailer Renovate stamps via `commitBody`, any dep's major/minor/patch mapped straight through (default patch). Decoupled from `apiVersion` (tag-only, `HOST_API_VERSION` untouched — a "major" is just a bigger image tag, never a plugin break); pre-1.0 shifts down so nothing auto-crosses into 1.0.0. Pure `auto-release/next-version.ts` + unit tests; tag pushed with renovate-bot's PAT so `release.yml` fires; documented in README → CI/CD.)
- [ ] CI/CD - When renovate updates a dependency - also release a new version of plainpages based on what got updated with Renovate. Major typescript? New apiVersion + new major. A tiny patch to ejs? Only patch release etc.
- [ ] Add an e2e test for the admin plugin's OAuth2-clients (Hydra) screen. The full-flow e2e suite runs without Hydra (compose.full.yml), so /admin/clients register/detail/delete is only unit-covered (src/http/app.test.ts); wire Hydra into an e2e stack and drive the screen in the browser.
- [ ] Build and publish docker image as CI/CD.
- [ ] Add i18n support.
+1 -1
View File
@@ -24,5 +24,5 @@
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true
},
"include": ["auto-release", "config", "examples/config", "examples/plugins", "plugins", "registry-cleanup", "src"]
"include": ["config", "examples/config", "examples/plugins", "plugins", "registry-cleanup", "src"]
}