mirror of
https://github.com/n8n-io/n8n.git
synced 2026-05-30 16:26:59 +02:00
Co-authored-by: n8n-cat-bot[bot] <n8n-cat-bot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> Co-authored-by: Declan Carroll <declan@n8n.io>
157 lines
7.1 KiB
YAML
157 lines
7.1 KiB
YAML
# This action works transparently on both Blacksmith and GitHub-hosted runners.
|
|
# Blacksmith runners benefit from transparent caching and optional Docker layer caching.
|
|
# GitHub-hosted runners use standard GitHub Actions caching.
|
|
|
|
name: 'Node.js Build Setup'
|
|
description: 'Configures Node.js with pnpm, installs Aikido SafeChain for supply chain protection, installs dependencies, enables Turborepo caching, (optional) sets up Docker layer caching, and builds the project or an optional command.'
|
|
|
|
inputs:
|
|
node-version:
|
|
description: 'Node.js version to use. Pinned to 24.15.0 by default for reproducible builds.'
|
|
required: false
|
|
default: '24.15.0'
|
|
enable-docker-cache:
|
|
description: 'Whether to set up Blacksmith Buildx for Docker layer caching (Blacksmith runners only).'
|
|
required: false
|
|
default: 'false'
|
|
build-command:
|
|
description: 'Command to execute for building the project or an optional command. Leave empty to skip build step.'
|
|
required: false
|
|
default: 'pnpm build'
|
|
install-command:
|
|
description: 'Command to execute for installing project dependencies. Leave empty to skip install step.'
|
|
required: false
|
|
default: 'pnpm install --frozen-lockfile'
|
|
cache-dependency-path:
|
|
description: 'Path(s) to the lockfile(s) used to compute the pnpm-store cache key. Scope this down (e.g. to `.github/scripts/pnpm-lock.yaml`) when only installing a subset, to avoid restoring the full workspace store.'
|
|
required: false
|
|
default: 'pnpm-lock.yaml'
|
|
|
|
runs:
|
|
using: 'composite'
|
|
steps:
|
|
- name: Setup pnpm
|
|
uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
|
|
|
|
# Cache the Node toolcache so setup-node skips the ~33s nodejs.org download
|
|
# on every subsequent job. First fresh runner pays the download; later jobs
|
|
# (including parallel E2E shards) hit the cache. Blacksmith transparently
|
|
# routes actions/cache through its S3 backend.
|
|
#
|
|
# The `x64.complete` sibling marker file must be cached alongside the
|
|
# toolchain — without it, setup-node's `tc.find` returns empty and the
|
|
# action re-downloads from nodejs.org. Re-downloads also keep the silent
|
|
# fall-through window open: errors during download/extract are swallowed,
|
|
# PATH is left untouched, and the runner image's baked-in Node 20.20.0
|
|
# quietly takes over. Caching the parent version dir captures the marker
|
|
# alongside the toolchain. Key bumped to v2 to invalidate stale entries.
|
|
- name: Restore Node.js Toolcache
|
|
if: runner.os == 'Linux' && runner.arch == 'X64'
|
|
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
|
with:
|
|
path: /opt/hostedtoolcache/node/${{ inputs.node-version }}
|
|
key: node-toolcache-v2-${{ runner.os }}-${{ runner.arch }}-${{ inputs.node-version }}
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
|
|
with:
|
|
node-version: ${{ inputs.node-version }}
|
|
cache: 'pnpm'
|
|
cache-dependency-path: ${{ inputs.cache-dependency-path }}
|
|
|
|
# Fail fast if setup-node silently fell through to the runner's baked-in
|
|
# Node instead of activating the requested version.
|
|
# see: https://github.com/actions/setup-node/issues/1137
|
|
- name: Verify Node.js Version
|
|
shell: bash
|
|
env:
|
|
EXPECTED_NODE_VERSION: ${{ inputs.node-version }}
|
|
run: |
|
|
ACTUAL_NODE_VERSION="$(node --version)"
|
|
if [ "${ACTUAL_NODE_VERSION#v}" != "${EXPECTED_NODE_VERSION#v}" ]; then
|
|
echo "::error::setup-node did not activate Node ${EXPECTED_NODE_VERSION} (got ${ACTUAL_NODE_VERSION})"
|
|
exit 1
|
|
fi
|
|
|
|
# To avoid setup-node cache failure.
|
|
# see: https://github.com/actions/setup-node/issues/1137
|
|
- name: Verify PNPM Cache Directory
|
|
shell: bash
|
|
run: |
|
|
PNPM_STORE_PATH="$( pnpm store path --silent )"
|
|
if [ ! -d "$PNPM_STORE_PATH" ]; then
|
|
mkdir -p "$PNPM_STORE_PATH"
|
|
fi
|
|
|
|
- name: Configure SafeChain
|
|
shell: bash
|
|
run: |
|
|
# SafeChain only reads configs from this directory https://github.com/AikidoSec/safe-chain#configuration-options-1
|
|
mkdir -p "$HOME/.safe-chain"
|
|
cp "${{ github.action_path }}/safe-chain.config.json" "$HOME/.safe-chain/config.json"
|
|
|
|
- name: Install Aikido SafeChain
|
|
run: |
|
|
VERSION="1.5.3"
|
|
EXPECTED_SHA256="0107cbbbf90159379756157e902acae512d62ffbd174307e42c5fe9f266792d3"
|
|
node .github/scripts/retry.mjs --attempts 3 --delay 10 -- \
|
|
curl -fsSL -o install-safe-chain.sh "https://github.com/AikidoSec/safe-chain/releases/download/${VERSION}/install-safe-chain.sh"
|
|
echo "${EXPECTED_SHA256} install-safe-chain.sh" | sha256sum -c -
|
|
sh install-safe-chain.sh --ci
|
|
rm install-safe-chain.sh
|
|
shell: bash
|
|
|
|
# `--reporter=append-only` collapses pnpm output to a single terse line and
|
|
# drops the ERR_PNPM_* code plus the offending package. Combined with the
|
|
# `timeout` wrapper, a SIGKILL or a non-pnpm post-install crash surfaces as
|
|
# bare `ELIFECYCLE` with no actionable diagnostics. We let pnpm pick its own
|
|
# CI reporter, tee stderr to a file, and re-emit the captured log on
|
|
# failure so the failure survives even if the live log buffer is cut off
|
|
# when `timeout` terminates the process group.
|
|
- name: Install Dependencies
|
|
if: ${{ inputs.install-command != '' }}
|
|
env:
|
|
INSTALL_COMMAND: ${{ inputs.install-command }}
|
|
INSTALL_LOG: ${{ runner.temp }}/pnpm-install.err
|
|
run: |
|
|
set +e
|
|
timeout --kill-after=30s 300s $INSTALL_COMMAND 2> >(tee "$INSTALL_LOG" >&2)
|
|
rc=$?
|
|
set -e
|
|
# Let the backgrounded `tee` flush before we read the file back.
|
|
wait 2>/dev/null || true
|
|
if [ $rc -ne 0 ]; then
|
|
echo "::group::pnpm install stderr (captured)"
|
|
cat "$INSTALL_LOG" 2>/dev/null || echo "(no captured log)"
|
|
echo "::endgroup::"
|
|
case $rc in
|
|
124) echo "::error::pnpm install timed out after 300s (exit 124)" ;;
|
|
137) echo "::error::pnpm install received SIGKILL (exit 137 — likely OOM or kill-after timeout)" ;;
|
|
esac
|
|
fi
|
|
exit $rc
|
|
shell: bash
|
|
|
|
- name: Configure Turborepo Cache
|
|
uses: rharkor/caching-for-turbo@5d14fba18e450c09393333cfd4242e8b3cb455a6 # v2.4.2
|
|
with:
|
|
server-port: 0
|
|
|
|
- name: Setup Docker Builder for Docker Cache (Blacksmith)
|
|
if: ${{ inputs.enable-docker-cache == 'true' && contains(runner.name, 'blacksmith') }}
|
|
uses: useblacksmith/setup-docker-builder@ef12d5b165b596e3aa44ea8198d8fde563eab402 # v1.4.0
|
|
|
|
- name: Setup Docker Builder (GitHub fallback)
|
|
if: ${{ inputs.enable-docker-cache == 'true' && !contains(runner.name, 'blacksmith') }}
|
|
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0
|
|
|
|
- name: Build Project
|
|
if: ${{ inputs.build-command != '' }}
|
|
env:
|
|
BUILD_COMMAND: ${{ inputs.build-command }}
|
|
run: |
|
|
$BUILD_COMMAND --summarize
|
|
node .github/scripts/send-build-stats.mjs || true
|
|
node .github/scripts/send-docker-stats.mjs || true
|
|
shell: bash
|