Files
shared-actions/.github/workflows/node-pr.yaml
T
tozuo-home-cluster-eso[bot]GitHubtozuo-home-cluster-eso[bot] <287217661+tozuo-home-cluster-eso[bot]@users.noreply.github.com>
852afeb5aa chore(deps): update actions/setup-node action to v7 (#6)
Co-authored-by: tozuo-home-cluster-eso[bot] <287217661+tozuo-home-cluster-eso[bot]@users.noreply.github.com>
2026-10-03 23:00:46 +01:00

176 lines
6.5 KiB
YAML

name: Node Pull Request
# Checks for a Node, Vue or Astro project: install without touching the lockfile,
# then run lint, typecheck, test and build. A script that does not exist is skipped.
on:
workflow_call:
inputs:
node-version:
description: "Node.js version to use"
required: false
type: string
default: "22"
working-directory:
description: "Directory that holds package.json"
required: false
type: string
default: "."
scripts:
description: "Comma-separated package.json scripts to run, in order. A script that does not exist is skipped."
required: false
type: string
default: "lint,typecheck,test,build"
pnpm-version:
description: "pnpm version to use, for a repo whose package.json has no packageManager field. Empty uses the packageManager field, or the newest pnpm."
required: false
type: string
default: ""
require-tests:
description: "Fail when package.json has no test script"
required: false
type: boolean
default: false
lfs:
description: "Enable Git LFS checkout (for repos with large files)"
required: false
type: boolean
default: false
runner:
description: "Runner label, or JSON object such as {\"group\":\"ci\",\"labels\":[\"linux\"]} to select a runner group. Falls back to vars.SHARED_ACTIONS_RUNNER, then ubuntu-latest."
required: false
type: string
default: ""
concurrency:
group: node-pr-${{ github.repository }}-${{ github.event_name }}-${{ github.head_ref || github.ref_name }}-${{ inputs.working-directory }}
cancel-in-progress: true
jobs:
pre:
name: Detect duplicate run
runs-on: ${{ fromJSON(startsWith(inputs.runner || vars.SHARED_ACTIONS_RUNNER || 'ubuntu-latest', '{') && (inputs.runner || vars.SHARED_ACTIONS_RUNNER || 'ubuntu-latest') || format('"{0}"', inputs.runner || vars.SHARED_ACTIONS_RUNNER || 'ubuntu-latest')) }}
timeout-minutes: 5
permissions:
pull-requests: read
outputs:
skip: ${{ steps.dup.outputs.skip }}
steps:
# A push to a branch with an open PR also fires pull_request; run only the PR one.
- name: Skip push run when a PR is open
id: dup
uses: actions/github-script@v9
with:
script: |
let skip = false;
if (context.eventName === 'push') {
try {
const { data } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
head: `${context.repo.owner}:${context.ref.replace('refs/heads/', '')}`,
state: 'open',
});
skip = data.length > 0;
} catch (e) {
core.warning(`PR lookup failed, running checks: ${e.message}`);
}
}
core.setOutput('skip', String(skip));
if (skip) core.notice('Open PR found for this branch; checks run on the pull_request event.');
checks:
name: Checks (${{ inputs.working-directory }})
needs: pre
if: needs.pre.outputs.skip != 'true'
runs-on: ${{ fromJSON(startsWith(inputs.runner || vars.SHARED_ACTIONS_RUNNER || 'ubuntu-latest', '{') && (inputs.runner || vars.SHARED_ACTIONS_RUNNER || 'ubuntu-latest') || format('"{0}"', inputs.runner || vars.SHARED_ACTIONS_RUNNER || 'ubuntu-latest')) }}
timeout-minutes: 30
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@v6
with:
lfs: ${{ inputs.lfs }}
- name: Run workflow prepare script
shell: bash
run: |
if [ -f "./workflow-prepare.sh" ]; then
chmod +x ./workflow-prepare.sh
./workflow-prepare.sh
fi
- name: Detect the package manager
id: detect
working-directory: ${{ inputs.working-directory }}
run: |
if [ ! -f package.json ]; then
echo "::error::No package.json in ${{ inputs.working-directory }}."
exit 1
fi
pm=npm
if [ -f pnpm-lock.yaml ]; then pm=pnpm
elif [ -f yarn.lock ]; then pm=yarn
fi
echo "pm=$pm" >> "$GITHUB_OUTPUT"
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: ${{ inputs.node-version }}
- name: Install dependencies
working-directory: ${{ inputs.working-directory }}
env:
PM: ${{ steps.detect.outputs.pm }}
PNPM_VERSION: ${{ inputs.pnpm-version }}
run: |
# Install without updating the lockfile, so a lockfile that does not match package.json fails.
case "$PM" in
pnpm | yarn)
command -v corepack >/dev/null || npm install --global corepack
corepack enable "$PM"
;;
esac
if [ "$PM" = pnpm ] && [ -n "$PNPM_VERSION" ]; then
corepack prepare "pnpm@$PNPM_VERSION" --activate
fi
case "$PM" in
pnpm) pnpm install --frozen-lockfile ;;
yarn) yarn install --immutable ;;
npm)
if [ -f package-lock.json ]; then npm ci; else npm install; fi
;;
esac
- name: Run scripts
working-directory: ${{ inputs.working-directory }}
env:
PM: ${{ steps.detect.outputs.pm }}
SCRIPTS: ${{ inputs.scripts }}
REQUIRE_TESTS: ${{ inputs.require-tests }}
run: |
if [ "$REQUIRE_TESTS" = true ] && ! jq -e '.scripts.test' package.json >/dev/null; then
echo "::error::package.json has no test script."
exit 1
fi
ran=()
IFS=',' read -r -a scripts <<<"$SCRIPTS"
for script in "${scripts[@]}"; do
script=$(echo "$script" | xargs)
[ -n "$script" ] || continue
if jq -e --arg s "$script" '.scripts[$s]' package.json >/dev/null; then
echo "::group::$PM run $script"
"$PM" run "$script"
echo "::endgroup::"
ran+=("$script")
else
echo "::notice::No '$script' script, skipping."
fi
done
if [ "${#ran[@]}" -eq 0 ]; then
echo "::warning::None of the scripts ($SCRIPTS) exist in package.json, so nothing was checked."
fi
echo "Ran: ${ran[*]:-none} (${PM})" >> "$GITHUB_STEP_SUMMARY"