From 1aa1e702d275ed9401f12cb55c02c667fd931f66 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Jul 2026 14:17:56 +0000 Subject: [PATCH 1/3] Support deploy-key pushes for doc pins and add copyable pin block The pins job's push to main can be blocked by the pull-request rule (GH013). Let the job authenticate with a write-enabled deploy key when the NIX_PINS_DEPLOY_KEY Actions secret is set ("Deploy keys" then goes on the ruleset bypass list); when unset, checkout falls back to GITHUB_TOKEN, which requires the workflow to be allowed through the ruleset instead. Add a standalone, copyable pin block to the plugin pins section of docs/deployment-nixos.md so the current versions and hashes can be copied with one click. The pins now appear twice (copy block + full module example), so the update script replaces every occurrence of each anchor instead of exactly one, keeping both blocks in sync. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01MkN8eGu18MoULRF4XLb5Y4 --- .github/workflows/ci.yaml | 8 +++++-- README.md | 5 ++++- docs/deployment-nixos.md | 31 +++++++++++++++++++++++++++- scripts/update-nixos-plugin-pins.mjs | 24 ++++++++++++--------- 4 files changed, 54 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 419c8cd..c480462 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -86,12 +86,16 @@ jobs: runs-on: ubuntu-latest needs: [check_if_version_upgraded, create_github_release] if: needs.check_if_version_upgraded.outputs.is_pre_release != 'true' - permissions: - contents: write steps: + # Pushing to main requires a bypass of the pull-request rule. + # If the NIX_PINS_DEPLOY_KEY secret is set (a write-enabled deploy + # key, with "Deploy keys" on the ruleset bypass list), the push + # goes over SSH; when unset, checkout falls back to GITHUB_TOKEN, + # which needs the workflow/bot allowed through the ruleset instead. - uses: actions/checkout@v4 with: ref: main + ssh-key: ${{ secrets.NIX_PINS_DEPLOY_KEY }} - uses: actions/setup-node@v4 - name: Re-pin docs/deployment-nixos.md to the new release run: node scripts/update-nixos-plugin-pins.mjs --tag v${{ needs.check_if_version_upgraded.outputs.to_version }} diff --git a/README.md b/README.md index 3c3c987..1e15cf8 100644 --- a/README.md +++ b/README.md @@ -242,7 +242,10 @@ Cutting and consuming a release is a single repeatable motion: `themeVersion` / `spiVersion` / `sha256` pins in [docs/deployment-nixos.md](docs/deployment-nixos.md) from the release assets' digests and commits the result to `main` — the checked-in NixOS snippet always matches the - latest release. + latest release. Because `main` only accepts pull requests, the pushing identity needs + a ruleset bypass: either allow the workflow's `GITHUB_TOKEN` through, or set the + Actions secret `NIX_PINS_DEPLOY_KEY` to a write-enabled deploy key and put + **Deploy keys** on the bypass list — the job pushes over SSH when the secret is set. 4. **Deploy**: copy the refreshed pin block into your NixOS config and `nixos-rebuild switch`. diff --git a/docs/deployment-nixos.md b/docs/deployment-nixos.md index cdb9c14..61dc6b7 100644 --- a/docs/deployment-nixos.md +++ b/docs/deployment-nixos.md @@ -33,7 +33,36 @@ All four jars go into Keycloak's `providers/` directory — The pins below are **kept up to date automatically**: after every release, CI recomputes the versions and sha256 hashes from the release assets and commits them back to this file (see [§6 Updating](#6-updating)). Whatever is checked in here always matches the latest -release — copy it as-is. +release — copy it as-is into your module's `let` bindings: + +```nix + themeVersion = "0.6.1"; + spiVersion = "0.3.0"; + + release = + ver: file: sha: + pkgs.fetchurl { + name = file; + url = "https://github.com/helpwave/id.helpwave.de/releases/download/v${ver}/${file}"; + sha256 = sha; + }; + + themePlugin = + release themeVersion "keycloak-theme-for-kc-26.2-and-above.jar" + "sha256-nhAyu69jEyf3F0W0qph94iGnVdNU69yGEAUzN3IaJOY="; + + captchaSPI = + release themeVersion "helpwave-captcha-${spiVersion}.jar" + "sha256-RSzFG775YXjNLxYlxvCMxxjoRRLYOCUNq2mutrUOaRM="; + + pictureSPI = + release themeVersion "helpwave-picture-${spiVersion}.jar" + "sha256-aReO2U4AVyKMQydMqvZ2vIhl3TfM6MWpS7/rZQWerXg="; + + policySPI = + release themeVersion "helpwave-policy-acceptance-${spiVersion}.jar" + "sha256-+FZ7skQGOEFD5AmZtIiRAO0xbUwn9bqcZU4Q6SejxBg="; +``` To re-pin manually (e.g. against an older release): diff --git a/scripts/update-nixos-plugin-pins.mjs b/scripts/update-nixos-plugin-pins.mjs index 2dc3e93..b50d9b3 100644 --- a/scripts/update-nixos-plugin-pins.mjs +++ b/scripts/update-nixos-plugin-pins.mjs @@ -102,16 +102,20 @@ function findAsset(release, predicate, description) { return asset } -/** Replaces exactly one occurrence; fails loudly if the doc anchor shape changed. */ -function replaceOnce(content, pattern, replacement, description) { - const matches = content.match(new RegExp(pattern, 'g')) ?? [] - if (matches.length !== 1) { +/** + * Replaces every occurrence (the pins appear both in the copyable block and in + * the full module example); fails loudly if the doc anchor shape changed. + */ +function replacePins(content, pattern, replacement, description) { + const global = new RegExp(pattern, 'g') + const matches = content.match(global) ?? [] + if (matches.length === 0) { fail( - `Expected exactly 1 match for ${description} in ${path.relative(process.cwd(), DOC_PATH)}, ` + - `found ${matches.length}. Did the pin block in the doc change shape?` + `Found no match for ${description} in ${path.relative(process.cwd(), DOC_PATH)}. ` + + 'Did the pin block in the doc change shape?' ) } - return content.replace(pattern, replacement) + return content.replace(global, replacement) } async function main() { @@ -149,13 +153,13 @@ async function main() { const original = fs.readFileSync(DOC_PATH, 'utf8') let content = original - content = replaceOnce( + content = replacePins( content, /themeVersion = "[^"]+";/, `themeVersion = "${themeVersion}";`, 'themeVersion pin' ) - content = replaceOnce( + content = replacePins( content, /spiVersion = "[^"]+";/, `spiVersion = "${spiVersion}";`, @@ -172,7 +176,7 @@ async function main() { } for (const [binding, fileAnchor] of Object.entries(bindingAnchors)) { const escapedAnchor = fileAnchor.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - content = replaceOnce( + content = replacePins( content, new RegExp(`(release themeVersion "${escapedAnchor}"\\s*\\n\\s*")sha256-[A-Za-z0-9+/=]+(")`), `$1${hashes[binding]}$2`, From b3f4ef7ea9378246705f1d95747f35d8d638aee8 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Jul 2026 14:17:56 +0000 Subject: [PATCH 2/3] Cap brand text at font-weight 700 for Space Grotesk The "helpwave id" brand text requested font-weight 900, but Space Grotesk's variable weight axis only spans 300-700 and the @font-face declares that range. Browsers diverge on out-of-range requests: Chromium on Windows (Edge) renders the 700 instance with synthetic bold smeared on top, and Safari can fall back to the default 400 instance, so the heavy brand text looked broken while all other weights rendered fine. Use font-bold (700) like hightide's own brand components do. Bump to 0.6.2 so the fix ships in a release. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01MkN8eGu18MoULRF4XLb5Y4 --- package.json | 2 +- src/login/components/Branding.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fddb0bd..1b334f3 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "id.helpwave.de", - "version": "0.6.1", + "version": "0.6.2", "repository": { "type": "git", "url": "git://github.com/helpwave/id.helpwave.de.git" diff --git a/src/login/components/Branding.tsx b/src/login/components/Branding.tsx index 00ab64b..3ca51bc 100644 --- a/src/login/components/Branding.tsx +++ b/src/login/components/Branding.tsx @@ -25,7 +25,7 @@ export function Branding({ animate = 'loading' }: BrandingProps) { return (
-
+
helpwave id
From afdd9c4c7f4c762041322846f24092cbd26a8944 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Jul 2026 14:25:26 +0000 Subject: [PATCH 3/3] Load self-hosted fonts in Storybook Storybook's preview imported the hightide globals and index.css but not fonts.css, so stories rendered in system-fallback fonts instead of the self-hosted Inter and Space Grotesk that production loads via main.tsx. Import fonts.css in the same order as the production entry. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01MkN8eGu18MoULRF4XLb5Y4 --- .storybook/preview.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/.storybook/preview.ts b/.storybook/preview.ts index 6bd6bc7..c3c0298 100644 --- a/.storybook/preview.ts +++ b/.storybook/preview.ts @@ -1,6 +1,7 @@ import type { Preview } from '@storybook/react-vite' import React from 'react' import '@helpwave/hightide/style/globals.css' +import '../src/fonts.css' import '../src/index.css' import { HightideProvider } from '@helpwave/hightide'