Files
Peter Steinberger 60f238c5af fix(ci): scope the Node toolchain cache to runners that need it
GitHub-hosted images carry Node 24.19.0 in /opt/hostedtoolcache, so hosted jobs
resolved from there and never populated the cached root. They still ran a
restore that could only miss and then a save whose path did not exist, logging
"Path Validation Error: Path(s) specified in the action for caching do(es) not
exist" on every hosted job. No entry was ever written, so nothing was poisoned,
but the warning is noise and the steps are pure waste on ~30 jobs per run.

Gate both steps on runner.environment != 'github-hosted', the signal this
workflow already uses for Blacksmith-only behavior, and gate the save on the
setup step reporting that a download actually populated the root. That second
guard also covers a future self-hosted image whose toolcache clears the floor.

Verified on Blacksmith: a satisfying image toolcache leaves the root absent so
the save is skipped, and a download populates it so the save runs.
2026-08-17 00:04:53 -07:00

496 lines
25 KiB
YAML

name: Setup Node environment
description: >
Install Node 24 by default, pnpm, optionally Bun, and optionally run pnpm
install. Requires actions/checkout to run first.
inputs:
node-version:
description: Node.js version to install.
required: false
default: "24.x"
install-bun:
description: Whether to install Bun alongside Node.
required: false
default: "true"
install-trufflehog:
description: Whether to install the pinned TruffleHog binary for a reusable test environment.
required: false
default: "false"
install-deps:
description: Whether to run pnpm install after environment setup.
required: false
default: "true"
frozen-lockfile:
description: Whether to use --frozen-lockfile for install.
required: false
default: "true"
use-actions-cache:
description: Whether to restore the pnpm store with actions/cache.
required: false
default: "true"
save-actions-cache:
description: Whether to save the pnpm store with actions/cache after install when no exact cache restored.
required: false
default: "false"
dependency-cache:
description: Whether to restore workspace node_modules and its local pnpm store from the exact semantic dependency cache.
required: false
default: "false"
save-dependency-cache:
description: Whether to save workspace node_modules and its local pnpm store after a successful install on an exact cache miss.
required: false
default: "false"
vitest-fs-cache:
description: Whether to persist Vitest's experimental filesystem module cache.
required: false
default: "false"
restore-test-caches:
description: Whether to restore the Vitest transform and test-scope Node compile caches together.
required: false
default: "false"
node-compile-cache:
description: Whether to persist Node's on-disk V8 compile cache.
required: false
default: "false"
node-compile-cache-scope:
description: Cache namespace for isolating workloads with different writer ownership.
required: false
default: "test"
save-node-compile-cache:
description: Whether this job may save the Node compile cache.
required: false
default: "false"
save-vitest-fs-cache:
description: Whether this job may save the shared Vitest filesystem module cache.
required: false
default: "false"
build-all-cache-scope:
description: >
Namespace for restoring and saving build-all's content-addressed step cache.
Leave empty to disable; use only for declaration builds with public inputs.
required: false
default: ""
runs:
using: composite
steps:
- name: Normalize container toolcache
shell: bash
run: |
set -euo pipefail
if [[ -d /__t && ! -e /opt/hostedtoolcache ]]; then
mkdir -p /opt
ln -s /__t /opt/hostedtoolcache
fi
# Blacksmith's image tracks an older runner-images snapshot whose toolcache
# Node patches sit just under this repo's engines floor (measured 2026-08-16:
# 20.20.0/22.22.0/24.13.0 against >=22.22.3 and >=24.15.0), so every job
# falls through to a nodejs.org download. That is ~2.6s normally but tail-
# spikes past 110s when the mirror throttles our ~46 simultaneous fetches,
# and it lands on every job at once. Keep the payload in the Actions cache,
# which Blacksmith serves from its colocated backend. A stale or corrupt
# entry is self-healing: ensure-node probes each candidate's version and
# falls back to the download when none satisfies the floor.
# Restore by prefix, never by exact key: cache entries are immutable and an
# exact hit suppresses the post-job save, so a floating `24.x` key would pin
# the first Node it ever saw. Once the floor advances past it every job would
# restore the rejected payload and re-download forever. The save below is
# keyed on the version actually installed, so a newer resolve publishes a new
# entry and later prefix restores pick it up.
# GitHub-hosted images carry a Node that already clears the floor, so they
# resolve from /opt/hostedtoolcache and would only ever miss here, then warn
# on a save whose path was never created. Scope both steps to self-hosted.
- name: Restore Node toolchain cache
if: runner.os != 'Windows' && runner.environment != 'github-hosted'
id: node-toolchain-restore
continue-on-error: true
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ runner.temp }}/openclaw-node-toolchain/node
key: openclaw-node-toolchain-v1-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-
restore-keys: |
openclaw-node-toolchain-v1-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-
- name: Setup Node.js
id: setup-node
shell: bash
env:
REQUESTED_NODE_VERSION: ${{ inputs.node-version }}
OPENCLAW_NODE_TOOLCHAIN_ROOT: ${{ runner.os != 'Windows' && format('{0}/openclaw-node-toolchain/node', runner.temp) || '' }}
run: |
set -euo pipefail
source "$GITHUB_ACTION_PATH/../setup-pnpm-store-cache/ensure-node.sh"
openclaw_ensure_node "$REQUESTED_NODE_VERSION"
echo "resolved-version=$(node -p 'process.versions.node')" >> "$GITHUB_OUTPUT"
# Only a download populates the cached root. An image whose toolcache
# already clears the floor leaves it absent, and saving a missing path
# warns without producing an entry.
if [ -n "${OPENCLAW_NODE_TOOLCHAIN_ROOT:-}" ] && [ -d "${OPENCLAW_NODE_TOOLCHAIN_ROOT}" ]; then
echo "toolchain-populated=true" >> "$GITHUB_OUTPUT"
fi
# Skipped when the restore already matched this exact resolved version, so a
# warm run uploads nothing. On a version change the 46-way fanout races here;
# the losers log a benign "cache already exists" and continue.
- name: Save Node toolchain cache
if: ${{ runner.os != 'Windows' && runner.environment != 'github-hosted' && steps.setup-node.outputs.toolchain-populated == 'true' && steps.node-toolchain-restore.outputs.cache-matched-key != format('openclaw-node-toolchain-v1-{0}-{1}-{2}-{3}', runner.os, runner.arch, inputs.node-version, steps.setup-node.outputs.resolved-version) }}
continue-on-error: true
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ runner.temp }}/openclaw-node-toolchain/node
key: openclaw-node-toolchain-v1-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}-${{ steps.setup-node.outputs.resolved-version }}
- name: Configure dependency cache store
if: inputs.dependency-cache == 'true'
shell: bash
run: |
set -euo pipefail
# Keep both sides of pnpm's hard links below the workspace so one tar
# archive preserves them without relying on runner-home path depth.
echo "PNPM_CONFIG_STORE_DIR=$GITHUB_WORKSPACE/.cache/openclaw-pnpm-store" >> "$GITHUB_ENV"
- name: Resolve dependency cache key
id: dependency-cache-key
if: inputs.dependency-cache == 'true'
shell: bash
env:
FROZEN_LOCKFILE: ${{ inputs.frozen-lockfile }}
run: |
set -euo pipefail
deps_input_fingerprint="$(node "$GITHUB_ACTION_PATH/dependency-fingerprint.mjs" \
--workspace "$GITHUB_WORKSPACE" --frozen-lockfile "$FROZEN_LOCKFILE")"
cache_key="${GITHUB_REPOSITORY:?}-node-deps-v2-os-${RUNNER_OS:?}-arch-${RUNNER_ARCH:?}-node-$(node --version)-${deps_input_fingerprint:?}"
echo "key=$cache_key" >> "$GITHUB_OUTPUT"
- name: Prepare dependency cache restore
if: inputs.dependency-cache == 'true'
shell: bash
run: |
rm -rf "$GITHUB_WORKSPACE/node_modules" "$GITHUB_WORKSPACE/.cache/openclaw-pnpm-store"
find \
"$GITHUB_WORKSPACE/ui" \
"$GITHUB_WORKSPACE/packages" \
"$GITHUB_WORKSPACE/extensions" \
"$GITHUB_WORKSPACE/examples" \
-mindepth 1 -maxdepth 2 \( -type d -o -type l \) -name node_modules \
-exec rm -rf -- {} +
- name: Restore exact dependency cache
id: dependency-cache
if: inputs.dependency-cache == 'true'
continue-on-error: true
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: |
node_modules
ui/node_modules
packages/*/node_modules
examples/*/node_modules
.cache/openclaw-pnpm-store
key: ${{ steps.dependency-cache-key.outputs.key }}
- name: Prepare dependency cache miss fallback
if: inputs.dependency-cache == 'true' && steps.dependency-cache.outputs.cache-hit != 'true'
shell: bash
run: |
# actions/cache treats service, download, and extraction failures as
# misses. Clear any partial extraction before restoring the pnpm store.
rm -rf "$GITHUB_WORKSPACE/node_modules" "$GITHUB_WORKSPACE/.cache/openclaw-pnpm-store"
find \
"$GITHUB_WORKSPACE/ui" \
"$GITHUB_WORKSPACE/packages" \
"$GITHUB_WORKSPACE/extensions" \
"$GITHUB_WORKSPACE/examples" \
-mindepth 1 -maxdepth 2 \( -type d -o -type l \) -name node_modules \
-exec rm -rf -- {} +
- name: Setup pnpm
id: setup-pnpm
uses: ./.github/actions/setup-pnpm-store-cache
with:
node-version: ${{ inputs.node-version }}
# On an exact dependency-cache hit, the same archive already restored
# the complete store. Every miss can seed it from the coarser cache,
# including legacy Blacksmith callers that disabled that old fallback.
use-actions-cache: ${{ ((inputs.dependency-cache == 'true' && steps.dependency-cache.outputs.cache-hit != 'true') || (inputs.dependency-cache != 'true' && inputs.use-actions-cache == 'true')) && 'true' || 'false' }}
- name: Setup TruffleHog
if: inputs.install-trufflehog == 'true'
shell: bash
run: bash scripts/install-trufflehog.sh
- name: Restore and save Vitest transform cache
if: inputs.vitest-fs-cache == 'true' && inputs.save-vitest-fs-cache == 'true' && runner.os != 'Windows'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: /var/tmp/openclaw-vitest-fs-cache
# Blacksmith transparently accelerates the upstream Actions cache API.
# The scheduled/dispatch warmer writes one immutable protected archive;
# all CI shards restore it into isolated runner-local directories.
key: ${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', 'src/state/*.sql', '!**/node_modules/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', 'src/state/*.sql', '!**/node_modules/**') }}-
- name: Restore Vitest transform cache
if: (inputs.vitest-fs-cache == 'true' || inputs.restore-test-caches == 'true') && inputs.save-vitest-fs-cache != 'true' && runner.os != 'Windows'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: /var/tmp/openclaw-vitest-fs-cache
key: ${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', 'src/state/*.sql', '!**/node_modules/**') }}-${{ github.run_id }}-${{ github.run_attempt }}
restore-keys: |
${{ github.repository }}-vitest-fs-v3-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', 'src/state/*.sql', '!**/node_modules/**') }}-
- name: Configure Vitest transform cache
if: (inputs.vitest-fs-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows'
env:
CACHE_GENERATION: ${{ hashFiles('pnpm-lock.yaml', 'pnpm-workspace.yaml', '**/package.json', '**/tsconfig*.json', 'vitest.config.*', 'test/vitest/**', 'src/state/*.sql', '!**/node_modules/**') }}
CACHE_WRITER: ${{ inputs.save-vitest-fs-cache == 'true' && '1' || '0' }}
shell: bash
run: |
set -euo pipefail
cache_root=/var/tmp/openclaw-vitest-fs-cache
generation_file="$cache_root/.openclaw-transform-generation"
mkdir -p "$cache_root"
cache_generation=""
if [[ -f "$generation_file" ]]; then
cache_generation="$(<"$generation_file")"
fi
cache_entry="$(find "$cache_root" -mindepth 1 -maxdepth 1 -print -quit)"
# Every restore is runner-local. Never mix incompatible transform
# inputs even when an older archive was selected through a prefix.
if [[ -n "$cache_entry" ]] && [[ "$cache_generation" != "$CACHE_GENERATION" ]]; then
echo "Vitest transform inputs changed; clearing incompatible cache generation"
find "$cache_root" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
fi
printf '%s\n' "$CACHE_GENERATION" > "$generation_file"
# The shard runner treats this as a persistent root and assigns one
# isolated subdirectory per concurrent Vitest worker.
echo "OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=$cache_root" >> "$GITHUB_ENV"
echo "OPENCLAW_VITEST_FS_MODULE_CACHE_WRITER=$CACHE_WRITER" >> "$GITHUB_ENV"
- name: Select Node compile cache epoch
id: node-compile-cache-epoch
if: (inputs.node-compile-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows'
shell: bash
env:
CACHE_SCOPE: ${{ inputs.node-compile-cache-scope }}
run: |
set -euo pipefail
if [ "$CACHE_SCOPE" = "build" ]; then
echo "value=$(date -u +%Y%m%d)" >> "$GITHUB_OUTPUT"
else
echo "value=${GITHUB_RUN_ID:?}-${GITHUB_RUN_ATTEMPT:?}" >> "$GITHUB_OUTPUT"
fi
- name: Restore and save Node compile cache
if: inputs.node-compile-cache == 'true' && inputs.save-node-compile-cache == 'true' && runner.os != 'Windows'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: /var/tmp/openclaw-node-compile-cache
key: ${{ github.repository }}-node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ steps.node-compile-cache-epoch.outputs.value }}
restore-keys: |
${{ github.repository }}-node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-
- name: Restore Node compile cache
if: (inputs.node-compile-cache == 'true' || inputs.restore-test-caches == 'true') && inputs.save-node-compile-cache != 'true' && runner.os != 'Windows'
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: /var/tmp/openclaw-node-compile-cache
key: ${{ github.repository }}-node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ steps.node-compile-cache-epoch.outputs.value }}
restore-keys: |
${{ github.repository }}-node-compile-v3-${{ inputs.node-compile-cache-scope }}-protected-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-
- name: Configure Node compile cache
if: (inputs.node-compile-cache == 'true' || inputs.restore-test-caches == 'true') && runner.os != 'Windows'
env:
CACHE_WRITER: ${{ inputs.save-node-compile-cache == 'true' && '1' || '0' }}
shell: bash
run: |
set -euo pipefail
cache_root=/var/tmp/openclaw-node-compile-cache
mkdir -p "$cache_root"
echo "NODE_COMPILE_CACHE=$cache_root" >> "$GITHUB_ENV"
echo "NODE_COMPILE_CACHE_PORTABLE=1" >> "$GITHUB_ENV"
echo "OPENCLAW_NODE_COMPILE_CACHE_WRITER=$CACHE_WRITER" >> "$GITHUB_ENV"
- name: Setup Bun
if: inputs.install-bun == 'true'
shell: bash
run: |
set -euo pipefail
npm install -g bun@1.3.14
- name: Runtime versions
shell: bash
run: |
node -v
npm -v
pnpm -v
if command -v bun &>/dev/null; then bun -v; fi
- name: Capture node path
shell: bash
run: |
node_bin="$(dirname "$(node -p 'process.execPath')")"
if command -v cygpath >/dev/null 2>&1; then
node_bin="$(cygpath -u "$node_bin")"
fi
# zizmor: ignore[github-env] node_bin comes from trusted actions/setup-node output in this composite action.
echo "NODE_BIN=$node_bin" >> "$GITHUB_ENV"
echo "$node_bin" >> "$GITHUB_PATH"
- name: Install dependencies
if: inputs.install-deps == 'true'
shell: bash
env:
CI: "true"
DEPENDENCY_CACHE: ${{ inputs.dependency-cache }}
DEPENDENCY_CACHE_HIT: ${{ steps.dependency-cache.outputs.cache-hit }}
FROZEN_LOCKFILE: ${{ inputs.frozen-lockfile }}
run: |
set -euo pipefail
export PATH="$NODE_BIN:$PATH"
which node
node -v
pnpm -v
case "$FROZEN_LOCKFILE" in
true) LOCKFILE_FLAG="--frozen-lockfile" ;;
false) LOCKFILE_FLAG="" ;;
*)
echo "::error::Invalid frozen-lockfile input: '$FROZEN_LOCKFILE' (expected true or false)"
exit 2
;;
esac
install_args=(
install
--ignore-scripts=false
--config.engine-strict=false
--config.enable-pre-post-scripts=true
--config.side-effects-cache=true
)
if [ "$DEPENDENCY_CACHE" = "true" ]; then
# Both trees live below the workspace. Prefer real hard links so the
# single cache archive can preserve store/package identity; pnpm
# safely falls back to copies for files it cannot hard-link.
install_args+=(--package-import-method=hardlink)
fi
if [ -n "$LOCKFILE_FLAG" ]; then
install_args+=("$LOCKFILE_FLAG")
fi
append_pnpm_option_arg() {
local env_name="$1"
local option_name="$2"
local value="${!env_name-}"
if [ -n "$value" ]; then
install_args+=("--${option_name}=${value}")
fi
}
append_pnpm_option_arg PNPM_CONFIG_CHILD_CONCURRENCY child-concurrency
append_pnpm_option_arg PNPM_CONFIG_MODULES_DIR modules-dir
append_pnpm_option_arg PNPM_CONFIG_NETWORK_CONCURRENCY network-concurrency
append_pnpm_option_arg PNPM_CONFIG_STORE_DIR store-dir
append_pnpm_option_arg PNPM_CONFIG_VIRTUAL_STORE_DIR virtual-store-dir
run_pnpm_install() {
local fetch_mode="$1"
pnpm "${install_args[@]}" "$fetch_mode"
}
clear_dependency_modules() {
rm -rf "$GITHUB_WORKSPACE/node_modules"
find \
"$GITHUB_WORKSPACE/ui" \
"$GITHUB_WORKSPACE/packages" \
"$GITHUB_WORKSPACE/extensions" \
"$GITHUB_WORKSPACE/examples" \
-mindepth 1 -maxdepth 2 \( -type d -o -type l \) -name node_modules \
-exec rm -rf -- {} +
}
if [ -n "${PNPM_CONFIG_MODULES_DIR:-}" ]; then
mkdir -p "$PNPM_CONFIG_MODULES_DIR"
ln -sfn . "$PNPM_CONFIG_MODULES_DIR/node_modules"
export NODE_PATH="$PNPM_CONFIG_MODULES_DIR${NODE_PATH:+:$NODE_PATH}"
fi
install_status=0
if [ "$DEPENDENCY_CACHE_HIT" = "true" ]; then
run_pnpm_install --offline || install_status="$?"
else
run_pnpm_install --prefer-offline || install_status="$?"
fi
if [ "$install_status" -ne 0 ] && [ "$DEPENDENCY_CACHE_HIT" = "true" ]; then
echo "::warning::Cached dependency tree failed pnpm reconciliation; relinking it from the restored store"
clear_dependency_modules
install_status=0
run_pnpm_install --offline || install_status="$?"
fi
if [ "$install_status" -ne 0 ] && [ "$DEPENDENCY_CACHE_HIT" = "true" ]; then
echo "::warning::Restored dependency store failed pnpm reconciliation; retrying from an empty store"
clear_dependency_modules
rm -rf "${PNPM_CONFIG_STORE_DIR:?}"
install_status=0
run_pnpm_install --prefer-offline || install_status="$?"
fi
if [ "$install_status" -ne 0 ]; then
echo "::error::pnpm install failed"
exit "$install_status"
fi
if [ -n "${PNPM_CONFIG_MODULES_DIR:-}" ]; then
rm -rf node_modules
ln -sfn "$PNPM_CONFIG_MODULES_DIR" node_modules
ln -sfn . "$PNPM_CONFIG_MODULES_DIR/node_modules"
fi
if [ "$DEPENDENCY_CACHE" = "true" ]; then
# The exact archive includes importer links, and frozen offline
# reconciliation validates them without reaching the registry. Later
# build wrappers can use installed Node entrypoints directly.
echo "OPENCLAW_BUILD_ALL_NO_PNPM=1" >> "$GITHUB_ENV"
# Postinstall intentionally prunes plugin-local node_modules. Pnpm's
# redundant pre-run check treats that as stale and can launch unsafe
# concurrent implicit installs after CI fans out.
# zizmor: ignore[github-env] static pnpm policy owned by this action.
echo "pnpm_config_verify_deps_before_run=false" >> "$GITHUB_ENV"
fi
- name: Save exact dependency cache
if: inputs.install-deps == 'true' && inputs.dependency-cache == 'true' && inputs.save-dependency-cache == 'true' && steps.dependency-cache.outputs.cache-hit != 'true' && steps.dependency-cache.outcome != 'failure'
continue-on-error: true
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: |
node_modules
ui/node_modules
packages/*/node_modules
examples/*/node_modules
.cache/openclaw-pnpm-store
key: ${{ steps.dependency-cache-key.outputs.key }}
- name: Restore and save build-all cache
if: inputs.build-all-cache-scope != ''
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: .artifacts/build-all-cache
# Exact keys deduplicate concurrent jobs. Coarse restore supplies the
# newest declaration groups; build-all rehashes every group's inputs.
key: ${{ github.repository }}-build-all-v1-${{ inputs.build-all-cache-scope }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-${{ hashFiles('package.json', 'pnpm-lock.yaml', 'tsconfig*.json', 'tsdown*.config.ts', 'scripts/build-all.mts', 'scripts/tsdown-build.mts', 'scripts/lib/tsdown-*.mts', 'scripts/lib/plugin-sdk-*', 'scripts/lib/bundled-plugin-*', 'scripts/lib/optional-bundled-clusters.mjs', 'src/**', 'packages/**', 'extensions/**') }}
restore-keys: |
${{ github.repository }}-build-all-v1-${{ inputs.build-all-cache-scope }}-${{ runner.os }}-${{ runner.arch }}-node-${{ inputs.node-version }}-
# Prune before saving: prefix-key restores accrete every prior lockfile
# generation into the archive (measured 2.05 GiB, ~36s restore per job).
# Pruning collapses it to the current lockfile's closure; a dropped entry
# costs one registry refetch in a later job at worst.
- name: Prune pnpm store before save
if: ${{ inputs.install-deps == 'true' && inputs.use-actions-cache == 'true' && (inputs.dependency-cache != 'true' || steps.dependency-cache.outputs.cache-hit != 'true') && inputs.save-actions-cache == 'true' && runner.os != 'Windows' && steps.setup-pnpm.outputs.store-cache-hit != 'true' }}
shell: bash
working-directory: ${{ steps.package-manager.outputs.project-dir }}
run: |
du -sh "${{ steps.setup-pnpm.outputs.store-path }}" || true
pnpm store prune
du -sh "${{ steps.setup-pnpm.outputs.store-path }}" || true
- name: Save pnpm store cache
if: ${{ inputs.install-deps == 'true' && inputs.use-actions-cache == 'true' && (inputs.dependency-cache != 'true' || steps.dependency-cache.outputs.cache-hit != 'true') && inputs.save-actions-cache == 'true' && runner.os != 'Windows' && steps.setup-pnpm.outputs.store-cache-hit != 'true' }}
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: ${{ steps.setup-pnpm.outputs.store-path }}
key: ${{ steps.setup-pnpm.outputs.store-cache-primary-key }}