11name : bot-nix-hashes
22
3+ # Keeps nix/hashes.json in step with the lockfile through a pull request, using only GITHUB_TOKEN.
4+ # - hash and compute run repository code (nix build) with a read-only token and no secrets. compute uploads only the
5+ # new nix/hashes.json.
6+ # - publish holds the write token. It runs only .github/nix-hashes/ from this commit, treats the artifact as data,
7+ # validates its exact shape and commits only that file to bot/nix-hashes. It never runs repository code.
8+ # - check-bot-scope.yml, a required check, fails the bot's pull request unless it changes only a valid nix/hashes.json,
9+ # so a tampered branch cannot auto-merge.
10+ # This file, check-bot-scope.yml and .github/nix-hashes/ are the security boundary.
11+
312permissions :
413 contents : read
514
615on :
716 workflow_dispatch :
817 push :
918 branches : [dev]
19+ # Mirrored in .github/nix-hashes/publish.sh.
1020 paths :
1121 - " bun.lock"
1222 - " package.json"
@@ -25,7 +35,7 @@ concurrency:
2535jobs :
2636 # Native runners required: bun install cross-compilation flags (--os/--cpu)
2737 # do not produce byte-identical node_modules as native installs.
28- compute- hash :
38+ hash :
2939 strategy :
3040 fail-fast : false
3141 matrix :
4151 steps :
4252 - name : Checkout repository
4353 uses : actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
54+ with :
55+ persist-credentials : false
4456
4557 - name : Setup Nix
4658 uses : nixbuild/nix-quick-install-action@2c9db80fb984ceb1bcaa77cdda3fdf8cfba92035 # v34
@@ -87,37 +99,29 @@ jobs:
8799 path : hash.txt
88100 retention-days : 1
89101
90- update-hashes :
91- needs : compute-hash
92- if : github.event_name != 'pull_request'
93- runs-on : blacksmith-4vcpu-ubuntu-2404
94- environment : release
102+ # Assembles nix/hashes.json from the per-system hashes and uploads it as the only input publish accepts.
103+ compute :
104+ needs : hash
105+ runs-on : ubuntu-24.04
106+ outputs :
107+ changed : ${{ steps.update.outputs.changed }}
95108
96109 steps :
97110 - name : Checkout repository
98111 uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
99112 with :
100113 persist-credentials : false
101- fetch-depth : 0
102- ref : ${{ github.ref_name }}
103-
104- - name : Setup git committer
105- uses : ./.github/actions/setup-git-committer
106- with :
107- opencode-app-id : ${{ vars.OPENCODE_APP_ID }}
108- opencode-app-secret : ${{ secrets.OPENCODE_APP_SECRET }}
109-
110- - name : Pull latest changes
111- run : |
112- git pull --rebase --autostash origin "$GITHUB_REF_NAME"
113114
114115 - name : Download hash artifacts
115116 uses : actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
116117 with :
117- path : hashes
118+ path : ${{ runner.temp }}/ hashes
118119 pattern : hash-*
119120
120121 - name : Update hashes.json
122+ id : update
123+ env :
124+ HASHES : ${{ runner.temp }}/hashes
121125 run : |
122126 set -euo pipefail
123127
@@ -126,37 +130,74 @@ jobs:
126130 [ -f "$HASH_FILE" ] || echo '{"nodeModules":{}}' > "$HASH_FILE"
127131
128132 for SYSTEM in x86_64-linux aarch64-linux aarch64-darwin; do
129- FILE="hashes/hash-${SYSTEM}/hash.txt"
130- if [ -f "$FILE" ]; then
131- HASH="$(tr -d '[:space:]' < "$FILE")"
132- echo "${SYSTEM}: ${HASH}"
133- jq --arg sys "$SYSTEM" --arg h "$HASH" '.nodeModules[$sys] = $h' "$HASH_FILE" > tmp.json
134- mv tmp.json "$HASH_FILE"
135- else
136- echo "::warning::Missing hash for ${SYSTEM}"
137- fi
133+ HASH="$(tr -d '[:space:]' < "$HASHES/hash-${SYSTEM}/hash.txt")"
134+ echo "${SYSTEM}: ${HASH}"
135+ jq --arg sys "$SYSTEM" --arg h "$HASH" '.nodeModules[$sys] = $h' "$HASH_FILE" > tmp.json
136+ mv tmp.json "$HASH_FILE"
138137 done
139138
140139 cat "$HASH_FILE"
140+ bash .github/nix-hashes/validate.sh "$HASH_FILE"
141141
142- - name : Commit changes
143- run : |
144- set -euo pipefail
145-
146- HASH_FILE="nix/hashes.json"
147-
148- if [ -z "$(git status --short -- "$HASH_FILE")" ]; then
149- echo "No changes to commit"
142+ if git diff --quiet -- "$HASH_FILE"; then
143+ echo "changed=false" >> "$GITHUB_OUTPUT"
150144 echo "### Nix hashes" >> "$GITHUB_STEP_SUMMARY"
151145 echo "Status: no changes" >> "$GITHUB_STEP_SUMMARY"
152- exit 0
146+ else
147+ echo "changed=true" >> "$GITHUB_OUTPUT"
148+ echo "### Nix hashes" >> "$GITHUB_STEP_SUMMARY"
149+ echo "Status: changed" >> "$GITHUB_STEP_SUMMARY"
153150 fi
154151
155- git add "$HASH_FILE"
156- git commit -m "chore: update nix node_modules hashes"
152+ - name : Upload hashes.json
153+ uses : actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
154+ with :
155+ name : nix-hashes
156+ path : nix/hashes.json
157+ if-no-files-found : error
158+ retention-days : 1
159+
160+ # Runs even when compute found no change, so an open bot pull request with stale hashes is closed, not merged.
161+ publish :
162+ needs : compute
163+ if : github.repository == 'anomalyco/opencode' && github.ref == 'refs/heads/dev'
164+ runs-on : ubuntu-24.04
165+ concurrency :
166+ group : bot-nix-hashes-publish
167+ cancel-in-progress : false
168+ permissions :
169+ contents : write
170+ pull-requests : write
171+ actions : write
157172
158- git pull --rebase --autostash origin "$GITHUB_REF_NAME"
159- git push origin HEAD:"$GITHUB_REF_NAME"
173+ steps :
174+ - name : Check out publish scripts
175+ uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
176+ with :
177+ ref : ${{ github.sha }}
178+ path : trusted
179+ sparse-checkout : /.github/nix-hashes/
180+ sparse-checkout-cone-mode : false
181+ persist-credentials : false
160182
161- echo "### Nix hashes" >> "$GITHUB_STEP_SUMMARY"
162- echo "Status: committed $(git rev-parse --short HEAD)" >> "$GITHUB_STEP_SUMMARY"
183+ # Holds the push credential; only nix/hashes.json is checked out and nothing in it runs.
184+ - name : Check out dev
185+ uses : actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
186+ with :
187+ ref : dev
188+ path : repo
189+ sparse-checkout : /nix/hashes.json
190+ sparse-checkout-cone-mode : false
191+
192+ - name : Download hashes.json
193+ uses : actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
194+ with :
195+ name : nix-hashes
196+ path : ${{ runner.temp }}/nix-hashes
197+
198+ - name : Publish
199+ working-directory : repo
200+ run : bash ../trusted/.github/nix-hashes/publish.sh "$RUNNER_TEMP/nix-hashes/hashes.json"
201+ env :
202+ GH_TOKEN : ${{ github.token }}
203+ GH_REPO : ${{ github.repository }}
0 commit comments