@@ -145,7 +145,7 @@ jobs:
145145 clang --version
146146
147147 - name : Setup pnpm
148- uses : pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.0 .0
148+ uses : pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2 .0
149149 # Note: version is specified in package.json packageManager field, not here
150150
151151 - name : Setup Node.js
@@ -238,13 +238,9 @@ jobs:
238238 fi
239239
240240 # Helper function to hash files, returns empty string if no files found
241+ # Callers must pass -type f and optionally -name "*.mjs" to filter appropriately
241242 hash_files() {
242- local files=$(find "$@" 2>/dev/null | sort)
243- if [ -z "$files" ]; then
244- echo ""
245- else
246- echo "$files" | xargs $hash_cmd 2>/dev/null | $hash_cmd | cut -d' ' -f1
247- fi
243+ find "$@" 2>/dev/null | sort | xargs $hash_cmd 2>/dev/null | $hash_cmd | cut -d' ' -f1 || echo ""
248244 }
249245
250246 # Helper function to get hierarchical paths for a category/phase/platform/arch
@@ -269,6 +265,8 @@ jobs:
269265 # Now with hierarchical paths: shared/ → platform/shared/ → platform/arch/
270266
271267 # Common scripts (used by all phases) - hierarchical
268+ # Each checkpoint now has its own paths.mjs that re-exports from scripts/paths.mjs
269+ # This makes dependencies explicit and ensures changes to paths.mjs are tracked per-checkpoint
272270 COMMON_PATHS=$(get_hierarchical_paths scripts common "$PLATFORM" "$ARCH")
273271 COMMON_SCRIPTS=$(hash_files $COMMON_PATHS -type f -name "*.mjs")
274272
@@ -334,165 +332,54 @@ jobs:
334332 echo "final_hash=${FINAL_KEY}" >> $GITHUB_OUTPUT
335333 echo "build_mode=${BUILD_MODE}" >> $GITHUB_OUTPUT
336334
337- - name : Restore node-source cache
338- uses : actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
339- id : node-source-cache
340- if : ${{ !inputs.force }}
341- with :
342- path : packages/node-smol-builder/build/${{ steps.smol-cache-key.outputs.build_mode }}/source
343- key : node-source-${{ env.CACHE_VERSION }}-${{ steps.tool-versions.outputs.node-build-version }}-${{ steps.smol-cache-key.outputs.source_cloned_hash }}
344-
345- - name : Clean stale ninja files from cache
346- shell : bash
347- run : |
348- BUILD_MODE="${STEPS_SMOL_CACHE_KEY_OUTPUTS_BUILD_MODE}"
349- OUT_DIR="packages/node-smol-builder/build/${BUILD_MODE}/source/out"
350- if [ -d "$OUT_DIR" ]; then
351- echo "Cleaning stale ninja files from cached source to free disk space..."
352- rm -rf "$OUT_DIR"
353- echo "✅ Cleaned $OUT_DIR"
354- else
355- echo "No out/ directory to clean"
356- fi
357- env :
358- STEPS_SMOL_CACHE_KEY_OUTPUTS_BUILD_MODE : ${{ steps.smol-cache-key.outputs.build_mode }}
359-
360- - name : Restore node Release cache
361- uses : actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
362- id : node-release-cache
363- if : ${{ !inputs.force }}
364- with :
365- path : packages/node-smol-builder/build/${{ steps.smol-cache-key.outputs.build_mode }}/out/Release
366- key : node-release-${{ steps.tool-versions.outputs.node-build-version }}-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.smol-cache-key.outputs.build_mode }}-${{ steps.smol-cache-key.outputs.binary_released_hash }}
367-
368- - name : Restore node Stripped cache
369- uses : actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
370- id : node-stripped-cache
371- if : ${{ !inputs.force }}
372- with :
373- path : packages/node-smol-builder/build/${{ steps.smol-cache-key.outputs.build_mode }}/out/Stripped
374- key : node-stripped-${{ steps.tool-versions.outputs.node-build-version }}-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.smol-cache-key.outputs.build_mode }}-${{ steps.smol-cache-key.outputs.binary_stripped_hash }}
375-
376- - name : Restore node Compressed cache
377- uses : actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
378- id : node-compressed-cache
379- if : ${{ !inputs.force }}
380- with :
381- path : packages/node-smol-builder/build/${{ steps.smol-cache-key.outputs.build_mode }}/out/Compressed
382- key : node-compressed-${{ steps.tool-versions.outputs.node-build-version }}-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.smol-cache-key.outputs.build_mode }}-${{ steps.smol-cache-key.outputs.binary_compressed_hash }}
383-
384- - name : Restore node Final cache
385- uses : actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
386- id : node-final-cache
387- if : ${{ !inputs.force }}
388- with :
389- path : packages/node-smol-builder/build/${{ steps.smol-cache-key.outputs.build_mode }}/out/Final
390- key : node-final-${{ steps.tool-versions.outputs.node-build-version }}-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.smol-cache-key.outputs.build_mode }}-${{ steps.smol-cache-key.outputs.final_hash }}
391-
392335 - name : Restore checkpoint cache
393336 uses : actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
394337 id : checkpoint-cache
395338 if : ${{ !inputs.force }}
396339 with :
397- path : packages/node-smol-builder/build/${{ steps.smol-cache-key.outputs.build_mode }}/checkpoints
398- key : node-checkpoints-${{ steps.tool-versions.outputs.node-build-version }}-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.smol-cache-key.outputs.build_mode }}-${{ steps.smol-cache-key.outputs.final_hash }}
340+ path : |
341+ packages/node-smol-builder/build/shared/checkpoints
342+ packages/node-smol-builder/build/${{ steps.smol-cache-key.outputs.build_mode }}/checkpoints
343+ key : node-checkpoints-${{ env.CACHE_VERSION }}-${{ steps.tool-versions.outputs.node-build-version }}-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.smol-cache-key.outputs.build_mode }}-${{ steps.smol-cache-key.outputs.final_hash }}
399344
400- - name : Validate build cache integrity
345+ - name : Validate checkpoint cache integrity
401346 id : validate-cache
402- if : steps.node-final -cache.outputs.cache-hit == 'true'
347+ if : steps.checkpoint -cache.outputs.cache-hit == 'true'
403348 shell : bash
404349 run : |
405350 BUILD_MODE="${STEPS_SMOL_CACHE_KEY_OUTPUTS_BUILD_MODE}"
406- FINAL_DIR="packages/node-smol-builder/build/${BUILD_MODE}/out/Final"
407- CHECKPOINT_DIR="packages/node-smol-builder/build/${BUILD_MODE}/checkpoints"
408-
409- echo "Validating cached build for ${{ matrix.platform }}-${{ matrix.arch }}..."
410-
411- # Determine expected binary name
412- if [ "${{ matrix.os }}" = "windows" ]; then
413- BINARY_NAME="node.exe"
414- else
415- BINARY_NAME="node"
416- fi
351+ SHARED_CHECKPOINT_DIR="packages/node-smol-builder/build/shared/checkpoints"
352+ MODE_CHECKPOINT_DIR="packages/node-smol-builder/build/${BUILD_MODE}/checkpoints"
417353
418- # Check if binary exists
419- if [ ! -f "${FINAL_DIR}/${BINARY_NAME}" ]; then
420- echo "❌ Binary missing: ${FINAL_DIR}/${BINARY_NAME}"
421- rm -rf "$FINAL_DIR" "$CHECKPOINT_DIR"
422- echo "cache_valid=false" >> $GITHUB_OUTPUT
423- exit 0
424- fi
354+ echo "Validating cached checkpoints for ${{ matrix.platform }}-${{ matrix.arch }}..."
425355
426- # Check binary size (minimum 50MB for node binary)
427- if [ "${{ matrix.os }}" = "windows" ]; then
428- BINARY_SIZE=$(stat -c%s "${FINAL_DIR}/${BINARY_NAME}" 2>/dev/null || powershell -Command "(Get-Item '${FINAL_DIR}/${BINARY_NAME}').Length")
429- else
430- BINARY_SIZE=$(stat -f%z "${FINAL_DIR}/${BINARY_NAME}" 2>/dev/null || stat -c%s "${FINAL_DIR}/${BINARY_NAME}")
431- fi
432-
433- MIN_SIZE=52428800 # 50MB in bytes
434- if [ "$BINARY_SIZE" -lt "$MIN_SIZE" ]; then
435- echo "❌ Binary too small: $BINARY_SIZE bytes (minimum $MIN_SIZE)"
436- rm -rf "$FINAL_DIR" "$CHECKPOINT_DIR"
437- echo "cache_valid=false" >> $GITHUB_OUTPUT
438- exit 0
439- fi
440- echo "✓ Binary size OK: $BINARY_SIZE bytes"
441-
442- # Check if checkpoint files exist (phase-based structure, flat directory)
443- if [ ! -f "${CHECKPOINT_DIR}/binary-released.json" ] || \
444- [ ! -f "${CHECKPOINT_DIR}/binary-stripped.json" ] || \
445- [ ! -f "${CHECKPOINT_DIR}/binary-compressed.json" ]; then
356+ # Check if checkpoint tarballs and JSON files exist
357+ # source-cloned is in shared directory, others are in mode-specific directory
358+ if [ ! -f "${SHARED_CHECKPOINT_DIR}/source-cloned.tar.gz" ] || \
359+ [ ! -f "${SHARED_CHECKPOINT_DIR}/source-cloned.json" ] || \
360+ [ ! -f "${MODE_CHECKPOINT_DIR}/binary-compressed.tar.gz" ] || \
361+ [ ! -f "${MODE_CHECKPOINT_DIR}/binary-compressed.json" ]; then
446362 echo "❌ Checkpoint files incomplete"
447- rm -rf "$FINAL_DIR " "$CHECKPOINT_DIR "
363+ rm -rf "${SHARED_CHECKPOINT_DIR} " "${MODE_CHECKPOINT_DIR} "
448364 echo "cache_valid=false" >> $GITHUB_OUTPUT
449365 exit 0
450366 fi
451367
452- # Validate checksum if available in checkpoint
453- if command -v jq &> /dev/null; then
454- EXPECTED_CHECKSUM=$(jq -r '.checksum // empty' "${CHECKPOINT_DIR}/binary-compressed.json")
455- if [ -n "$EXPECTED_CHECKSUM" ]; then
456- if command -v shasum &> /dev/null; then
457- ACTUAL_CHECKSUM=$(shasum -a 256 "${FINAL_DIR}/${BINARY_NAME}" | cut -d' ' -f1)
458- else
459- ACTUAL_CHECKSUM=$(sha256sum "${FINAL_DIR}/${BINARY_NAME}" | cut -d' ' -f1)
460- fi
461-
462- if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then
463- echo "❌ Checksum mismatch (cache corruption detected)"
464- echo " Expected: $EXPECTED_CHECKSUM"
465- echo " Actual: $ACTUAL_CHECKSUM"
466- rm -rf "$FINAL_DIR" "$CHECKPOINT_DIR"
467- echo "cache_valid=false" >> $GITHUB_OUTPUT
468- exit 0
368+ # Check tarball integrity for both directories
369+ for checkpoint_dir in "${SHARED_CHECKPOINT_DIR}" "${MODE_CHECKPOINT_DIR}"; do
370+ for tarball in "${checkpoint_dir}"/*.tar.gz; do
371+ if [ -f "$tarball" ]; then
372+ if ! gzip -t "$tarball" 2>/dev/null; then
373+ echo "❌ Corrupted tarball: $(basename "$tarball")"
374+ rm -rf "${SHARED_CHECKPOINT_DIR}" "${MODE_CHECKPOINT_DIR}"
375+ echo "cache_valid=false" >> $GITHUB_OUTPUT
376+ exit 0
377+ fi
469378 fi
470- echo "✓ Checksum validation passed"
471- fi
472- fi
473-
474- # Smoke test: check version (only for native builds, skip cross-compiled)
475- # We build natively on each architecture except Windows ARM64 (cross-compiled on x64)
476- if [ "${{ matrix.cross_compile }}" != "true" ]; then
477- "${FINAL_DIR}/${BINARY_NAME}" --version > /dev/null 2>&1
478- if [ $? -ne 0 ]; then
479- echo "❌ Binary smoke test failed"
480- rm -rf "$FINAL_DIR" "$CHECKPOINT_DIR"
481- echo "cache_valid=false" >> $GITHUB_OUTPUT
482- exit 0
483- fi
484-
485- # Verify Node.js version matches
486- NODE_VER=$("${FINAL_DIR}/${BINARY_NAME}" --version | sed 's/v//')
487- if [ "$NODE_VER" != "${{ steps.tool-versions.outputs.node-build-version }}" ]; then
488- echo "❌ Version mismatch: expected ${{ steps.tool-versions.outputs.node-build-version }}, got $NODE_VER"
489- rm -rf "$FINAL_DIR" "$CHECKPOINT_DIR"
490- echo "cache_valid=false" >> $GITHUB_OUTPUT
491- exit 0
492- fi
493- fi
379+ done
380+ done
494381
495- echo "✅ Cache validation passed"
382+ echo "✅ Checkpoint cache validation passed"
496383 echo "cache_valid=true" >> $GITHUB_OUTPUT
497384 env :
498385 STEPS_SMOL_CACHE_KEY_OUTPUTS_BUILD_MODE : ${{ steps.smol-cache-key.outputs.build_mode }}
@@ -526,7 +413,7 @@ jobs:
526413 fi
527414
528415 - name : Build Node.js smol
529- if : steps.node-final -cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.cache_valid == 'false'
416+ if : steps.checkpoint -cache.outputs.cache-hit != 'true' || steps.validate-cache.outputs.cache_valid == 'false'
530417 shell : bash
531418 env :
532419 BUILD_MODE : ${{ inputs.build_mode || 'prod' }}
0 commit comments