Skip to content

Commit 88ab42c

Browse files
build(nix): reduce the manually pinned hashes to one and automate it (#5593)
* build(nix): reduce the manually pinned hashes to one and automate it Every mix or npm dependency bump invalidated one or more hashes in nix/flake-modules/package.nix, and each one had to be looked up from a failing build and pasted back by hand. Three changes shrink that to a single hash and take the pasting out of it. Fetch the npm dependencies with pkgs.importNpmLock instead of buildNpmPackage. The integrity hashes in assets/package-lock.json are already authoritative, so npmDepsHash is gone entirely and npm bumps no longer touch the Nix code at all. The phoenix, phoenix_html and phoenix_live_view entries are file: links into deps/, which only exists in a working tree after mix deps.get, so packageSourceOverrides points them at mixFodDeps. That also replaces the previous approach of letting npm install them and swapping them for symlinks afterwards, so the substitution is now declarative instead of a correction after the fact. The resulting assets are byte-identical to the previous ones. Derive the cldr revision from the ex_cldr version in mix.lock. It had to match, but that was only stated in a comment, and a stale revision is the one failure mode here that does not fail the build: Nix resolves the old output hash to the store path it already has and silently ships the locale data of the previous release. Reading it from the lockfile makes the two impossible to drift apart, and a lockfile without ex_cldr now aborts the evaluation instead of falling back to something plausible. This carries the locale data from 2.47.4 to 2.47.5, matching mix.lock. Add nix run .#update-nix-hashes for what is left. It builds, reads the hash mismatch Nix reports, writes it back and repeats until the build succeeds, so a green run also proves the package still builds. It keys on the reported hash string rather than on attribute names, so a further fixed-output derivation is covered without touching the script. Document the command in the development guide and replace the warning about stale cldr hashes with the reason the revision must not be pinned by hand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: let Dependabot pull requests refresh the Nix hashes A mix.lock bump invalidates the mixFodDeps hash, and an ex_cldrb ump moves the cldr revision and with it its hash, so every such pull request needed a manual nix run .#update-nix-hashes before it could be merged. Refreshing the hashes builds the dependencies the pull request updates and therefore runs their code, so the work is split across two jobs. The first has no credentials and only publishes a patch. The second starts from a clean checkout that never ran repository code, and is the only one that sees a write token, in its final push step. In a single job the pushing step would share a workspace with the build, where a git hook or a core.fsmonitor entry left behind during the build would run while the token is in the environment. That patch is untrusted input, so the second job validates what a commit would contain rather than the patch syntax: it applies the patch to the index without a token and then requires every staged entry to be a modification of an existing .nix file below nix/, with an unchanged file mode, whose content equals the one in HEAD once well formed sha256 literals are masked out. Checking the staged result instead of the diff text leaves no room for a binary hunk or for ---/+++ paths that disagree with the diff --git header. Restricted to Dependabot's own pull requests from branches in this repository. The actor check also stops the updater from running on the commit it pushes itself, while the regular CI workflows, which are not actor-gated, still verify it. closes: #4156 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: verify Nix hashes on all dependency pull requests The updater only ran for Dependabot, so a stale hash in a human pull request went unnoticed: a fixed-output derivation is addressed by its hash, and a stale one resolves to the path fetched for it earlier, which means the build stays green and no check reports anything. The compute job now runs for every pull request touching mix.exs or mix.lock, and the new verify job fails whenever the hashes it produces differ from the ones in the tree. The patch is published as an artifact for the author to apply. Pushing a repair into a pull request branch stays limited to Dependabot's own pull requests from branches in this repository. The commit job depends on compute alone, not on verify, so the failing check does not block the repair; its push produces a new run in which nothing is left to change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci(nix): keep the hash check off workflow-touching pull requests Pull requests that touch .github are not run automatically in this repository for SecOp reasons. This workflow did not honour that. Exclude the .github paths the way devops.yml does, and gate the compute job on the existing check_paths workflow, which is what covers a pull request that carries both a dependency change and a workflow change. Add a manual trigger so the hashes of such a pull request can still be verified deliberately, by someone who is allowed to start a run; that path only verifies, because the commit job's actor check does not hold for it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * ci: let maintainers run CI on pull requests touching .github Pull requests touching .github are deliberately kept out of the automatic runs, because with pull_request the workflow definition that runs is the one from the pull request. There was no way to run those checks anyway: devops.yml gated its jobs on github.event_name == 'schedule', but it has never had a schedule trigger, so that condition could never be true, and buildx.yml offered a workflow_dispatch trigger whose jobs the same gate skipped. Gate on workflow_dispatch instead, so starting a run by hand, which takes write access, reaches the jobs. devops.yml replaces the unreachable condition, buildx.yml keeps its schedule condition and gains this one, and osv-scanner.yml gains the trigger it was missing; a manual run there does the full repository scan rather than the pull request diff. Note that a manual buildx run publishes: from a branch it overwrites the edge tag. That was already the case for branches that do not touch .github, this only widens it. * ci: extend retention period for nix-hashes patch to 7 days * ci: update git push command to enhance security for token usage --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 926afa3 commit 88ab42c

11 files changed

Lines changed: 525 additions & 40 deletions

File tree

.github/workflows/buildx.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
name: Publish Docker images
22

33
on:
4-
workflow_dispatch:
54
workflow_call:
65
schedule:
76
- cron: "0 3 * * *"
@@ -13,6 +12,12 @@ on:
1312
- "!.github/**" # Important: Exclude PRs related to .github from auto-run
1413
- "!.github/workflows/**" # Important: Exclude PRs related to .github/workflows from auto-run
1514
- "!.github/actions/**" # Important: Exclude PRs related to .github/actions from auto-run
15+
# Pull requests touching .github are excluded from the automatic runs above,
16+
# since the workflow definition that would run is the one from the pull
17+
# request. This is the deliberate way to build them anyway, by someone with
18+
# write access. The job conditions below allow for it. Note that this
19+
# publishes: a run from a branch overwrites the edge tag.
20+
workflow_dispatch:
1621
env:
1722
REGISTRY_IMAGE: teslamate/teslamate
1823

@@ -29,7 +34,7 @@ jobs:
2934

3035
teslamate_build:
3136
needs: check_paths
32-
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'schedule'
37+
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
3338
strategy:
3439
fail-fast: false
3540
matrix:
@@ -62,7 +67,7 @@ jobs:
6267
needs:
6368
- check_paths
6469
- teslamate_build
65-
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'schedule'
70+
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
6671
steps:
6772
- name: Checkout
6873
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -83,7 +88,7 @@ jobs:
8388
type=edge
8489
grafana:
8590
needs: check_paths
86-
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'schedule'
91+
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
8792
runs-on: ubuntu-24.04
8893
timeout-minutes: 10
8994
steps:

.github/workflows/devops.yml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
name: DevOps
22

33
on:
4-
workflow_dispatch:
54
push:
65
branches: ["main"]
76
paths:
@@ -16,6 +15,11 @@ on:
1615
- "!.github/**" # Important: Exclude PRs related to .github from auto-run
1716
- "!.github/workflows/**" # Important: Exclude PRs related to .github/workflows from auto-run
1817
- "!.github/actions/**" # Important: Exclude PRs related to .github/actions from auto-run
18+
# Pull requests touching .github are excluded from the automatic runs above,
19+
# since the workflow definition that would run is the one from the pull
20+
# request. This is the deliberate way to run the chain for them anyway, by
21+
# someone with write access. The job conditions below allow for it.
22+
workflow_dispatch:
1923

2024
# Cancel any in-progress runs when a new commit is pushed
2125
concurrency:
@@ -32,26 +36,26 @@ jobs:
3236

3337
spell_check:
3438
needs: check_paths
35-
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'schedule'
39+
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'workflow_dispatch'
3640
uses: ./.github/workflows/spell_check.yml
3741

3842
ensure_linting:
3943
needs:
4044
- check_paths
4145
- spell_check
42-
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'schedule'
46+
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'workflow_dispatch'
4347
uses: ./.github/workflows/ensure_linting.yml
4448

4549
elixir_dep_verification_and_static_analysis:
4650
needs:
4751
- check_paths
4852
- ensure_linting
49-
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'schedule'
53+
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'workflow_dispatch'
5054
uses: ./.github/workflows/elixir_dep_verification_and_static_analysis.yml
5155

5256
elixir_test:
5357
needs:
5458
- check_paths
5559
- ensure_linting
56-
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'schedule'
60+
if: needs.check_paths.outputs.githubfolder == 'false' || github.event_name == 'workflow_dispatch'
5761
uses: ./.github/workflows/elixir_test.yml

.github/workflows/ensure_linting.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
uses: nix-community/cache-nix-action@7df957e333c1e5da7721f60227dbba6d06080569 # v7.0.2
2020
with:
2121
# restore and save a cache using this key
22-
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
22+
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock', 'mix.lock') }}
2323
# if there's no cache hit, restore a cache by this prefix
2424
restore-prefixes-first-match: nix-${{ runner.os }}-
2525
# collect garbage until Nix store size (in bytes) is at most this number

.github/workflows/osv-scanner.yml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ on:
1919
- "!.github/actions/**" # Important: Exclude PRs related to .github/actions from auto-run
2020
merge_group:
2121
branches: ["main"]
22+
# Pull requests touching .github are excluded from the automatic runs above,
23+
# since the workflow definition that would run is the one from the pull
24+
# request. This is the deliberate way to scan them anyway, by someone with
25+
# write access. The job conditions below allow for it.
26+
workflow_dispatch:
2227

2328
permissions:
2429
actions: read
@@ -31,12 +36,12 @@ jobs:
3136

3237
scan-scheduled:
3338
needs: check_paths
34-
if: ( github.event_name == 'push' && needs.check_paths.outputs.githubfolder == 'false' ) || github.event_name == 'schedule'
39+
if: ( github.event_name == 'push' && needs.check_paths.outputs.githubfolder == 'false' ) || github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
3540
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@9fd1bcce27f67e3bd819a0a7620e332803dc43bc" # v2.3.8
3641
with:
37-
# Don't fail on push to main / scheduled runs: vulnerabilities are still
38-
# reported via the SARIF upload, but the CI run stays green so it doesn't
39-
# look like a build failure on main.
42+
# Don't fail on push to main, scheduled or manual runs: vulnerabilities
43+
# are still reported via the SARIF upload, but the CI run stays green so
44+
# it doesn't look like a build failure on main.
4045
fail-on-vuln: false
4146
scan-args: |-
4247
-r

.github/workflows/update-flake-lock.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
uses: nix-community/cache-nix-action@7df957e333c1e5da7721f60227dbba6d06080569 # v7.0.2
2525
with:
2626
# restore and save a cache using this key
27-
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock') }}
27+
primary-key: nix-${{ runner.os }}-${{ hashFiles('**/*.nix', '**/flake.lock', 'mix.lock') }}
2828
# if there's no cache hit, restore a cache by this prefix
2929
restore-prefixes-first-match: nix-${{ runner.os }}-
3030
# collect garbage until Nix store size (in bytes) is at most this number

0 commit comments

Comments
 (0)