From eab5da6041422b592080ba6439d7ecb80b918972 Mon Sep 17 00:00:00 2001 From: Tom Thorogood Date: Wed, 1 Jul 2026 13:31:27 +0930 Subject: [PATCH] Merge node-pr.yml's install job into build and test Previously node-pr.yml worked in three stages. It would start by running an install job that would download and archive all node dependencies. It would then, in parallel, run separate build and test jobs that would start by installing node again and unpacking those dependencies. This packing and unpacking step routinely took longer than the download did. It also consumes artifact storage space, which has caused persistent pipeline failures on one of our client hosted projects. It also resulted in a lot of logic duplicated in three separate places. Instead of all of this, we'll merge the install job into both the build and test jobs, and just download the node dependencies twice. This will actually result in faster pipeline runs approximately always. It also gives us the advantage of having safe-chain installed in both build and test jobs. This could be advantageous if future changes were to see them run additional installs, or if they commands they execute from package.json were to run any install commands. --- .github/workflows/node-pr.yml | 133 ++++++++++++---------------------- 1 file changed, 48 insertions(+), 85 deletions(-) diff --git a/.github/workflows/node-pr.yml b/.github/workflows/node-pr.yml index 54728a1..0515ca0 100644 --- a/.github/workflows/node-pr.yml +++ b/.github/workflows/node-pr.yml @@ -98,11 +98,13 @@ on: type: string jobs: - install: - name: ๐Ÿงถ Install + build: + name: ๐Ÿ—๏ธ Build + if: inputs.skip-build == false runs-on: ubuntu-latest env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + NODE_OPTIONS: ${{ inputs.node-options }} steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7.0.0 with: @@ -111,8 +113,8 @@ jobs: - name: Install Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e #v6.4.0 with: - package-manager-cache: false node-version-file: .nvmrc + package-manager-cache: false - name: Enable Corepack run: | # Enable corepack if packageManager is specified in package.json @@ -193,88 +195,12 @@ jobs: else npm ci $debug fi - env: INPUTS_PACKAGE_MANAGER: ${{ inputs.package-manager }} INPUTS_DEBUG: ${{ inputs.debug }} INPUTS_IS_YARN_CLASSIC: ${{ inputs.is-yarn-classic }} INPUTS_SKIP_CACHE: ${{ inputs.skip-cache }} - # Use tar to store cache so file permissions are maintained (https://github.com/actions/upload-artifact/issues/38) - - name: Archive node_modules with tar - run: find . -name "node_modules" -prune -print | tar -czf node_modules.tar.gz -T - - - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a #v7.0.1 - with: - name: node_modules - path: node_modules.tar.gz - retention-days: 1 - - build: - name: ๐Ÿ—๏ธ Build - needs: [install] - if: inputs.skip-build == false - runs-on: ubuntu-latest - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - NODE_OPTIONS: ${{ inputs.node-options }} - steps: - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 #v7.0.0 - with: - fetch-depth: ${{ inputs.fetch-depth }} - persist-credentials: false - - name: Install Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e #v6.4.0 - with: - node-version-file: .nvmrc - package-manager-cache: false - - name: Enable Corepack - run: | - # Enable corepack if packageManager is specified in package.json - if [ -f package.json ] && jq -e '.packageManager' package.json > /dev/null 2>&1; then - echo "packageManager field detected in package.json, enabling corepack" - corepack enable - fi - - name: Configure Dependency Cache - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e #v6.4.0 - with: - cache: ${{ inputs.package-manager }} - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1 - with: - name: node_modules - - name: Extract node_modules with tar - run: tar -xvzf node_modules.tar.gz -C . - - name: Setup additional environment variables - if: inputs.has-env-vars - run: | - # Parse and set additional environment variables securely - # Supports multiline values (e.g., SSH keys) - current_key="" - current_value="" - flush_var() { - if [ -n "$current_key" ]; then - while IFS= read -r mask_line; do - [ -n "$mask_line" ] && echo "::add-mask::${mask_line}" - done <<< "${current_value}" - echo "Setting environment variable: ${current_key}" - echo "${current_key}<> $GITHUB_ENV - echo "${current_value}" >> $GITHUB_ENV - echo "GHENV_EOF" >> $GITHUB_ENV - fi - } - while IFS= read -r line || [ -n "$line" ]; do - if [[ "$line" =~ ^[A-Za-z_][A-Za-z0-9_]*= ]]; then - flush_var - current_key="${line%%=*}" - current_value="${line#*=}" - elif [ -n "$current_key" ]; then - current_value="$(printf '%s\n%s' "${current_value}" "${line}")" - fi - done <<< "${SECRETS_ENV_VARS}" - flush_var - env: - # zizmor: ignore[secrets-outside-env] - SECRETS_ENV_VARS: ${{ secrets.ENV_VARS }} - - name: Register problem matchers uses: aligent/workflows/.github/actions/node-problem-matchers@main @@ -331,7 +257,6 @@ jobs: test: name: ๐Ÿงช Pull Request Checks - needs: [install] if: >- inputs.skip-test == false || inputs.skip-lint == false || @@ -363,11 +288,11 @@ jobs: uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e #v6.4.0 with: cache: ${{ inputs.package-manager }} - - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1 - with: - name: node_modules - - name: Extract node_modules with tar - run: tar -xvzf node_modules.tar.gz -C . + node-version-file: .nvmrc + - name: Install safe-chain + run: | + SAFE_CHAIN_URL="https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh" + curl -fsSL "$SAFE_CHAIN_URL" | sh -s -- --ci - name: Setup additional environment variables if: inputs.has-env-vars run: | @@ -399,6 +324,44 @@ jobs: env: # zizmor: ignore[secrets-outside-env] SECRETS_ENV_VARS: ${{ secrets.ENV_VARS }} + - name: Run pre-install commands + if: inputs.pre-install-commands != '' + run: | + # Execute pre-install commands line by line + echo "${INPUTS_PRE_INSTALL_COMMANDS}" | while IFS= read -r cmd; do + if [ -n "$cmd" ]; then + echo "Running: $cmd" + eval "$cmd" + fi + done + env: + INPUTS_PRE_INSTALL_COMMANDS: ${{ inputs.pre-install-commands }} + - name: Install dependencies + run: | + debug="" + if [ "${INPUTS_DEBUG}" = "true" ]; then + debug="--verbose" + fi + if [ "${INPUTS_PACKAGE_MANAGER}" = "yarn" ]; then + lock_dependencies="--immutable" + if [ "${INPUTS_IS_YARN_CLASSIC}" = "true" ]; then + lock_dependencies="--frozen-lockfile" + fi + skip_cache="" + if [ "${INPUTS_SKIP_CACHE}" = "true" ]; then + skip_cache="--force" + fi + + yarn config get nodeLinker + yarn install $lock_dependencies $skip_cache $debug + else + npm ci $debug + fi + env: + INPUTS_PACKAGE_MANAGER: ${{ inputs.package-manager }} + INPUTS_DEBUG: ${{ inputs.debug }} + INPUTS_IS_YARN_CLASSIC: ${{ inputs.is-yarn-classic }} + INPUTS_SKIP_CACHE: ${{ inputs.skip-cache }} - name: Run pre-test command if: inputs.pre-test-command != ''