From f305faa292d004c1d1d03ba74d688cf2d3de3c23 Mon Sep 17 00:00:00 2001 From: Etienne Lescot Date: Mon, 3 Aug 2026 08:19:22 +0200 Subject: [PATCH 1/4] ci(nix): let the bump workflow open its PR, and catch hash drift on the PR that causes it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two failures, one visible and one not. `gh pr create` ran under GITHUB_TOKEN while the repo has "Allow GitHub Actions to create and approve pull requests" turned off, so the v1.7.0 bump pushed its branch and then died on the API call. #136 was opened by hand to work around it. Every other release workflow already authenticates with OPENSCREEN_RELEASE_TOKEN, which is not subject to that restriction and whose PRs trigger CI — so the note telling reviewers to push an empty commit goes away too. The quiet one: `src` is this repo's tree rather than a fetched tarball, so npmDepsHash has to agree with whatever package-lock.json sits beside it, and the only thing that refreshed it fired on stable releases. main's hash last matched on 2026-07-05 while the lockfile moved ten more times, leaving `nix run github:getopenscreen/openscreen` broken for four weeks with nothing reporting it. A release-time bump structurally cannot cover that gap, so the check now runs on lockfile and nix/ changes and prints the hash to paste. Version goes to 1.7.0 here, which is what #136 was for. --- .github/workflows/bump-nix-package.yml | 10 +++-- .github/workflows/nix-check.yml | 60 ++++++++++++++++++++++++++ nix/package.nix | 2 +- 3 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/nix-check.yml diff --git a/.github/workflows/bump-nix-package.yml b/.github/workflows/bump-nix-package.yml index 57e48e51a4..daa45bff1d 100644 --- a/.github/workflows/bump-nix-package.yml +++ b/.github/workflows/bump-nix-package.yml @@ -78,7 +78,13 @@ jobs: - name: Create PR env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # NOT GITHUB_TOKEN: the repo has "Allow GitHub Actions to create and + # approve pull requests" turned off, so `gh pr create` dies with + # "GitHub Actions is not permitted to create or approve pull requests" + # — it did exactly that on v1.7.0, after pushing the branch, and #136 + # had to be opened by hand. The PAT every other release workflow + # already uses has no such restriction, and its PRs trigger CI. + GH_TOKEN: ${{ secrets.OPENSCREEN_RELEASE_TOKEN }} VERSION: ${{ steps.meta.outputs.version }} HASH: ${{ steps.hash.outputs.hash }} BRANCH: ${{ steps.meta.outputs.branch }} @@ -112,7 +118,5 @@ jobs: - \`npmDepsHash\` → \`${HASH}\` (computed via \`prefetch-npm-deps package-lock.json\`) Merge this so Nix users (NixOS, Home Manager, \`nix run github:${{ github.repository }}\`) pick up the new release. - - > Note: PRs opened by \`GITHUB_TOKEN\` don't auto-trigger CI. The diff is two lines — review the change here, then merge. If you want CI to run, push an empty commit to this branch or close-and-reopen the PR. EOF )" diff --git a/.github/workflows/nix-check.yml b/.github/workflows/nix-check.yml new file mode 100644 index 0000000000..2831ca0615 --- /dev/null +++ b/.github/workflows/nix-check.yml @@ -0,0 +1,60 @@ +name: Nix + +# nix/package.nix records `npmDepsHash`, a hash of the npm dependency set that +# package-lock.json resolves to. The two have to agree or `nix build` fails +# outright, and nothing here ever checked that they did. +# +# The only thing that refreshed the hash was bump-nix-package.yml, which fires +# on stable releases. `src` is this repo's own tree, not a fetched tarball, so +# every lockfile change landing between two releases left main's hash pointing +# at dependencies that no longer existed. It last matched on 2026-07-05 (v1.6.0) +# while package-lock.json moved ten more times, so `nix run github:…` was broken +# for four weeks with nothing reporting it. +# +# Release-time bumps can't fix that — the drift starts the moment a lockfile PR +# merges. This runs on the PR that causes it and prints the hash to paste. +on: + pull_request: + paths: + - package-lock.json + - nix/** + - .github/workflows/nix-check.yml + push: + branches: [main] + paths: + - package-lock.json + - nix/** + +jobs: + npm-deps-hash: + name: npmDepsHash matches package-lock.json + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: cachix/install-nix-action@v27 + with: + nix_path: nixpkgs=channel:nixos-unstable + extra_nix_config: | + experimental-features = nix-command flakes + + - name: Compare recorded hash against the lockfile + run: | + set -euo pipefail + EXPECTED=$(nix run nixpkgs#prefetch-npm-deps -- package-lock.json) + RECORDED=$(sed -nE 's|^[[:space:]]*npmDepsHash[[:space:]]*=[[:space:]]*"([^"]*)";|\1|p' nix/package.nix) + + echo "recorded in nix/package.nix: ${RECORDED:-}" + echo "expected from package-lock.json: $EXPECTED" + + if [[ -z "$EXPECTED" ]]; then + echo "::error::prefetch-npm-deps returned an empty hash" + exit 1 + fi + + if [[ "$EXPECTED" != "$RECORDED" ]]; then + echo "::error file=nix/package.nix::npmDepsHash is stale — set it to $EXPECTED" + exit 1 + fi + + echo "In sync." diff --git a/nix/package.nix b/nix/package.nix index f30c7c7e41..2a48181a19 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -11,7 +11,7 @@ buildNpmPackage { nodejs = nodejs_22; pname = "openscreen"; - version = "1.6.0"; + version = "1.7.0"; src = let From 716e329a0eeb6eb4e65a116c73e557c370d548cd Mon Sep 17 00:00:00 2001 From: Etienne Lescot Date: Mon, 3 Aug 2026 08:21:47 +0200 Subject: [PATCH 2/4] fix(nix): refresh npmDepsHash to the lockfile main actually has MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value dated to v1.6.0 on 2026-07-05; package-lock.json moved ten times after that, so `nix build` had been refusing the mismatch ever since. The replacement is what the new check computed from the current lockfile: recorded sha256-IZypOLWlDShIjCKWxlJcrdtIkMu0P/DuXaq4c0HW3FY= expected sha256-SggSPoDnKzmvgXpIGP11y6h390SkoZszeMjFTaokRjQ= #136 carried sha256-cb8loUzQs4fz3um0xbYgtjQdcRZh/Ptk2YPgv3FHP/s=, computed from main as it stood on 2026-07-19 — stale for the same reason, which is why merging it would not have fixed anything. --- nix/package.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nix/package.nix b/nix/package.nix index 2a48181a19..620e610b0b 100644 --- a/nix/package.nix +++ b/nix/package.nix @@ -33,7 +33,7 @@ buildNpmPackage { ); }; - npmDepsHash = "sha256-IZypOLWlDShIjCKWxlJcrdtIkMu0P/DuXaq4c0HW3FY="; + npmDepsHash = "sha256-SggSPoDnKzmvgXpIGP11y6h390SkoZszeMjFTaokRjQ="; env.ELECTRON_SKIP_BINARY_DOWNLOAD = "1"; From 4a4ec2fa4bcb0a2f8e9417020473109f96050946 Mon Sep 17 00:00:00 2001 From: Etienne Lescot Date: Mon, 3 Aug 2026 08:33:04 +0200 Subject: [PATCH 3/4] ci(nix): watch the long-lived branches too, not just main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The push trigger listed main alone, which is the same gap ci.yml carries a comment about after PRs #167-#169 merged into a release branch with every job skipped. Pull requests were already covered everywhere — that trigger has no branch filter — but a rebase force-push onto release/** is not a pull request, and a release branch is the last place to let a stale hash through unnoticed. --- .github/workflows/nix-check.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nix-check.yml b/.github/workflows/nix-check.yml index 2831ca0615..3e50208941 100644 --- a/.github/workflows/nix-check.yml +++ b/.github/workflows/nix-check.yml @@ -19,8 +19,13 @@ on: - package-lock.json - nix/** - .github/workflows/nix-check.yml + # Same branch list as ci.yml, and for the reason its own comment gives: a + # release branch is the last place to skip a check. PRs need no filter here — + # the trigger above has none, so they are covered wherever they land — but a + # direct push or a rebase force-push onto a long-lived branch is not a PR and + # would otherwise go unchecked. push: - branches: [main] + branches: [main, feat/ai-edition, "release/**"] paths: - package-lock.json - nix/** From e67d665c869a522d7524c2fa393c93dacca879ed Mon Sep 17 00:00:00 2001 From: Etienne Lescot Date: Mon, 3 Aug 2026 09:09:42 +0200 Subject: [PATCH 4/4] @ ci(nix): least-privilege the two nix workflows, symmetric path filters Review follow-ups, all three mechanical: - nix-check.yml declares `permissions: contents: read` instead of inheriting it. It was the only workflow in .github/workflows/ without a permissions block, and inheriting a repo-level setting is precisely what broke the bump workflow it ships alongside. - The push trigger now watches .github/workflows/nix-check.yml like the pull_request trigger already did, so an edit to the check itself is checked on both legs. - bump-nix-package.yml drops `pull-requests: write`. The PR is opened with the PAT now, so that grant on GITHUB_TOKEN buys nothing. @ --- .github/workflows/bump-nix-package.yml | 3 ++- .github/workflows/nix-check.yml | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/bump-nix-package.yml b/.github/workflows/bump-nix-package.yml index daa45bff1d..661b881d6a 100644 --- a/.github/workflows/bump-nix-package.yml +++ b/.github/workflows/bump-nix-package.yml @@ -10,9 +10,10 @@ on: required: true type: string +# GITHUB_TOKEN only pushes the branch here — the PR itself is opened with the +# PAT below, so no `pull-requests: write` is needed (and it never worked). permissions: contents: write - pull-requests: write jobs: bump: diff --git a/.github/workflows/nix-check.yml b/.github/workflows/nix-check.yml index 3e50208941..afce7665d4 100644 --- a/.github/workflows/nix-check.yml +++ b/.github/workflows/nix-check.yml @@ -29,6 +29,13 @@ on: paths: - package-lock.json - nix/** + - .github/workflows/nix-check.yml + +# Read-only, and stated rather than inherited: the repo default happens to be +# `read` today, which is exactly the kind of repo-level setting that silently +# changed this workflow's sibling out from under it. +permissions: + contents: read jobs: npm-deps-hash: