1010 - " bun.lock"
1111 - " package.json"
1212 - " packages/*/package.json"
13+ - " .github/workflows/update-nix-hashes.yml"
1314 pull_request :
1415 paths :
1516 - " bun.lock"
1617 - " package.json"
1718 - " packages/*/package.json"
19+ - " .github/workflows/update-nix-hashes.yml"
1820
1921jobs :
20- update-flake :
22+ compute-node-modules-hash :
2123 if : github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
22- runs-on : blacksmith-4vcpu-ubuntu-2404
24+ strategy :
25+ fail-fast : false
26+ matrix :
27+ include :
28+ - system : x86_64-linux
29+ host : blacksmith-4vcpu-ubuntu-2404
30+ - system : aarch64-linux
31+ host : blacksmith-4vcpu-ubuntu-2404-arm
32+ - system : x86_64-darwin
33+ host : macos-15-intel
34+ - system : aarch64-darwin
35+ host : macos-latest
36+ runs-on : ${{ matrix.host }}
2337 env :
24- TITLE : flake.lock
38+ SYSTEM : ${{ matrix.system }}
2539
2640 steps :
2741 - name : Checkout repository
28- uses : actions/checkout@v4
42+ uses : actions/checkout@v6
2943 with :
3044 token : ${{ secrets.GITHUB_TOKEN }}
3145 fetch-depth : 0
@@ -35,96 +49,104 @@ jobs:
3549 - name : Setup Nix
3650 uses : nixbuild/nix-quick-install-action@v34
3751
38- - name : Configure git
39- run : |
40- git config --global user.email "action@github.com"
41- git config --global user.name "Github Action"
42-
43- - name : Update ${{ env.TITLE }}
52+ - name : Compute node_modules hash
4453 run : |
4554 set -euo pipefail
46- echo "📦 Updating $TITLE..."
47- nix flake update
48- echo "✅ $TITLE updated successfully"
4955
50- - name : Commit ${{ env.TITLE }} changes
51- env :
52- TARGET_BRANCH : ${{ github.head_ref || github.ref_name }}
53- run : |
54- set -euo pipefail
56+ DUMMY="sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="
57+ HASH_FILE="nix/hashes.json"
58+ OUTPUT_FILE="hash-${SYSTEM}.txt"
5559
56- echo "🔍 Checking for changes in tracked files..."
60+ export NIX_KEEP_OUTPUTS=1
61+ export NIX_KEEP_DERIVATIONS=1
5762
58- summarize() {
59- local status="$1"
60- {
61- echo "### Nix $TITLE"
62- echo ""
63- echo "- ref: ${GITHUB_REF_NAME}"
64- echo "- status: ${status}"
65- } >> "$GITHUB_STEP_SUMMARY"
66- if [ -n "${GITHUB_SERVER_URL:-}" ] && [ -n "${GITHUB_REPOSITORY:-}" ] && [ -n "${GITHUB_RUN_ID:-}" ]; then
67- echo "- run: ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}" >> "$GITHUB_STEP_SUMMARY"
63+ BUILD_LOG=$(mktemp)
64+ TMP_JSON=$(mktemp)
65+ trap 'rm -f "$BUILD_LOG" "$TMP_JSON"' EXIT
66+
67+ if [ ! -f "$HASH_FILE" ]; then
68+ mkdir -p "$(dirname "$HASH_FILE")"
69+ echo '{"nodeModules":{}}' > "$HASH_FILE"
70+ fi
71+
72+ # Set dummy hash to force nix to rebuild and reveal correct hash
73+ jq --arg system "$SYSTEM" --arg value "$DUMMY" \
74+ '.nodeModules = (.nodeModules // {}) | .nodeModules[$system] = $value' "$HASH_FILE" > "$TMP_JSON"
75+ mv "$TMP_JSON" "$HASH_FILE"
76+
77+ MODULES_ATTR=".#packages.${SYSTEM}.default.node_modules"
78+ DRV_PATH="$(nix eval --raw "${MODULES_ATTR}.drvPath")"
79+
80+ echo "Building node_modules for ${SYSTEM} to discover correct hash..."
81+ echo "Attempting to realize derivation: ${DRV_PATH}"
82+ REALISE_OUT=$(nix-store --realise "$DRV_PATH" --keep-failed 2>&1 | tee "$BUILD_LOG" || true)
83+
84+ BUILD_PATH=$(echo "$REALISE_OUT" | grep "^/nix/store/" | head -n1 || true)
85+ CORRECT_HASH=""
86+
87+ if [ -n "$BUILD_PATH" ] && [ -d "$BUILD_PATH" ]; then
88+ echo "Realized node_modules output: $BUILD_PATH"
89+ CORRECT_HASH=$(nix hash path --sri "$BUILD_PATH" 2>/dev/null || true)
90+ fi
91+
92+ # Try to extract hash from build log
93+ if [ -z "$CORRECT_HASH" ]; then
94+ CORRECT_HASH="$(grep -E 'got:\s+sha256-[A-Za-z0-9+/=]+' "$BUILD_LOG" | awk '{print $2}' | head -n1 || true)"
95+ fi
96+
97+ if [ -z "$CORRECT_HASH" ]; then
98+ CORRECT_HASH="$(grep -A2 'hash mismatch' "$BUILD_LOG" | grep 'got:' | awk '{print $2}' | sed 's/sha256:/sha256-/' || true)"
99+ fi
100+
101+ # Try to hash from kept failed build directory
102+ if [ -z "$CORRECT_HASH" ]; then
103+ KEPT_DIR=$(grep -oE "build directory.*'[^']+'" "$BUILD_LOG" | grep -oE "'/[^']+'" | tr -d "'" | head -n1 || true)
104+ if [ -z "$KEPT_DIR" ]; then
105+ KEPT_DIR=$(grep -oE '/nix/var/nix/builds/[^ ]+' "$BUILD_LOG" | head -n1 || true)
106+ fi
107+
108+ if [ -n "$KEPT_DIR" ] && [ -d "$KEPT_DIR" ]; then
109+ HASH_PATH="$KEPT_DIR"
110+ [ -d "$KEPT_DIR/build" ] && HASH_PATH="$KEPT_DIR/build"
111+
112+ if [ -d "$HASH_PATH/node_modules" ]; then
113+ CORRECT_HASH=$(nix hash path --sri "$HASH_PATH" 2>/dev/null || true)
114+ fi
68115 fi
69- echo "" >> "$GITHUB_STEP_SUMMARY"
70- }
71- FILES=(flake.lock flake.nix)
72- STATUS="$(git status --short -- "${FILES[@]}" || true)"
73- if [ -z "$STATUS" ]; then
74- echo "✅ No changes detected."
75- summarize "no changes"
76- exit 0
77116 fi
78117
79- echo "📝 Changes detected:"
80- echo "$STATUS"
81- echo "🔗 Staging files..."
82- git add "${FILES[@]}"
83- echo "💾 Committing changes..."
84- git commit -m "Update $TITLE"
85- echo "✅ Changes committed"
118+ if [ -z "$CORRECT_HASH" ]; then
119+ echo "Failed to determine correct node_modules hash for ${SYSTEM}."
120+ cat "$BUILD_LOG"
121+ exit 1
122+ fi
86123
87- BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
88- echo "🌳 Pulling latest from branch: $BRANCH"
89- git pull --rebase origin "$BRANCH"
90- echo "🚀 Pushing changes to branch: $BRANCH"
91- git push origin HEAD:"$BRANCH"
92- echo "✅ Changes pushed successfully"
124+ echo "$CORRECT_HASH" > "$OUTPUT_FILE"
125+ echo "Hash for ${SYSTEM}: $CORRECT_HASH"
93126
94- summarize "committed $(git rev-parse --short HEAD)"
127+ - name : Upload hash artifact
128+ uses : actions/upload-artifact@v6
129+ with :
130+ name : hash-${{ matrix.system }}
131+ path : hash-${{ matrix.system }}.txt
132+ retention-days : 1
95133
96- update -node-modules-hash :
97- needs : update-flake
134+ commit -node-modules-hashes :
135+ needs : compute-node-modules-hash
98136 if : github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
99- strategy :
100- fail-fast : false
101- matrix :
102- include :
103- - system : x86_64-linux
104- host : blacksmith-4vcpu-ubuntu-2404
105- - system : aarch64-linux
106- host : blacksmith-4vcpu-ubuntu-2404-arm
107- - system : x86_64-darwin
108- host : macos-15-intel
109- - system : aarch64-darwin
110- host : macos-latest
111- runs-on : ${{ matrix.host }}
137+ runs-on : blacksmith-4vcpu-ubuntu-2404
112138 env :
113- SYSTEM : ${{ matrix.system }}
114- TITLE : node_modules hash (${{ matrix.system }})
139+ TITLE : node_modules hashes
115140
116141 steps :
117142 - name : Checkout repository
118- uses : actions/checkout@v4
143+ uses : actions/checkout@v6
119144 with :
120145 token : ${{ secrets.GITHUB_TOKEN }}
121146 fetch-depth : 0
122147 ref : ${{ github.head_ref || github.ref_name }}
123148 repository : ${{ github.event.pull_request.head.repo.full_name || github.repository }}
124149
125- - name : Setup Nix
126- uses : nixbuild/nix-quick-install-action@v34
127-
128150 - name : Configure git
129151 run : |
130152 git config --global user.email "action@github.com"
@@ -135,22 +157,66 @@ jobs:
135157 TARGET_BRANCH : ${{ github.head_ref || github.ref_name }}
136158 run : |
137159 BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
138- git pull origin "$BRANCH"
160+ git pull --rebase --autostash origin "$BRANCH"
161+
162+ - name : Download all hash artifacts
163+ uses : actions/download-artifact@v7
164+ with :
165+ pattern : hash-*
166+ merge-multiple : true
139167
140- - name : Update ${{ env.TITLE }}
168+ - name : Merge hashes into hashes.json
141169 run : |
142170 set -euo pipefail
143- echo "🔄 Updating $TITLE..."
144- nix/scripts/update-hashes.sh
145- echo "✅ $TITLE updated successfully"
171+
172+ HASH_FILE="nix/hashes.json"
173+
174+ if [ ! -f "$HASH_FILE" ]; then
175+ mkdir -p "$(dirname "$HASH_FILE")"
176+ echo '{"nodeModules":{}}' > "$HASH_FILE"
177+ fi
178+
179+ echo "Merging hashes into ${HASH_FILE}..."
180+
181+ shopt -s nullglob
182+ files=(hash-*.txt)
183+ if [ ${#files[@]} -eq 0 ]; then
184+ echo "No hash files found, nothing to update"
185+ exit 0
186+ fi
187+
188+ EXPECTED_SYSTEMS="x86_64-linux aarch64-linux x86_64-darwin aarch64-darwin"
189+ for sys in $EXPECTED_SYSTEMS; do
190+ if [ ! -f "hash-${sys}.txt" ]; then
191+ echo "WARNING: Missing hash file for $sys"
192+ fi
193+ done
194+
195+ for f in "${files[@]}"; do
196+ system="${f#hash-}"
197+ system="${system%.txt}"
198+ hash=$(cat "$f")
199+ if [ -z "$hash" ]; then
200+ echo "WARNING: Empty hash for $system, skipping"
201+ continue
202+ fi
203+ echo " $system: $hash"
204+ jq --arg sys "$system" --arg h "$hash" \
205+ '.nodeModules = (.nodeModules // {}) | .nodeModules[$sys] = $h' "$HASH_FILE" > "${HASH_FILE}.tmp"
206+ mv "${HASH_FILE}.tmp" "$HASH_FILE"
207+ done
208+
209+ echo "All hashes merged:"
210+ cat "$HASH_FILE"
146211
147212 - name : Commit ${{ env.TITLE }} changes
148213 env :
149214 TARGET_BRANCH : ${{ github.head_ref || github.ref_name }}
150215 run : |
151216 set -euo pipefail
152217
153- echo "🔍 Checking for changes in tracked files..."
218+ HASH_FILE="nix/hashes.json"
219+ echo "Checking for changes..."
154220
155221 summarize() {
156222 local status="$1"
@@ -166,27 +232,22 @@ jobs:
166232 echo "" >> "$GITHUB_STEP_SUMMARY"
167233 }
168234
169- FILES=(nix/hashes.json )
235+ FILES=("$HASH_FILE" )
170236 STATUS="$(git status --short -- "${FILES[@]}" || true)"
171237 if [ -z "$STATUS" ]; then
172- echo "✅ No changes detected."
238+ echo "No changes detected."
173239 summarize "no changes"
174240 exit 0
175241 fi
176242
177- echo "📝 Changes detected:"
243+ echo "Changes detected:"
178244 echo "$STATUS"
179- echo "🔗 Staging files..."
180245 git add "${FILES[@]}"
181- echo "💾 Committing changes..."
182246 git commit -m "Update $TITLE"
183- echo "✅ Changes committed"
184247
185248 BRANCH="${TARGET_BRANCH:-${GITHUB_REF_NAME}}"
186- echo "🌳 Pulling latest from branch: $BRANCH"
187- git pull --rebase origin "$BRANCH"
188- echo "🚀 Pushing changes to branch: $BRANCH"
249+ git pull --rebase --autostash origin "$BRANCH"
189250 git push origin HEAD:"$BRANCH"
190- echo "✅ Changes pushed successfully"
251+ echo "Changes pushed successfully"
191252
192253 summarize "committed $(git rev-parse --short HEAD)"
0 commit comments