Skip to content

feat(pr-risk): use named risk tiers #272

feat(pr-risk): use named risk tiers

feat(pr-risk): use named risk tiers #272

name: Test workflow pins
# Regression lint (BE-5546): no reusable workflow here may give its
# `workflows_ref` input a `default:`. A default lets a caller SHA-pin `uses:`
# and still load this repo's scripts from a MUTABLE branch, into jobs holding
# write permissions — the pin then proves nothing. Removing that default is a
# one-line edit to undo, so it is guarded by CI rather than by convention.
#
# Runs on every workflow-file change (not just the three fixed ones) so a
# workflow added later is covered the day it lands.
on:
pull_request:
paths:
- '.github/workflows/**'
- '.github/workflow-pins/**'
push:
branches: [main]
paths:
- '.github/workflows/**'
- '.github/workflow-pins/**'
permissions:
contents: read
# One lint run per ref: a push that supersedes an in-flight run should cancel
# it, not race it for a runner.
concurrency:
group: test-workflow-pins-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
name: unittest + lint this repo
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.12'
- name: Run workflow-pins unit tests
run: python3 -m unittest discover -s .github/workflow-pins/tests -p 'test_*.py' -v
- name: Lint this repo's workflows
run: python3 .github/workflow-pins/check_workflow_pins.py
- name: Smoke test — a reintroduced default fails the lint
run: |
# Proves the lint is wired to FAIL, not just to run: the unit tests
# exercise the parser, this exercises the CLI's exit status.
dir="$(mktemp -d)"
cat > "$dir/regressed.yml" <<'YAML'
name: Regressed
on:
workflow_call:
inputs:
workflows_ref:
type: string
required: false
default: main
YAML
if python3 .github/workflow-pins/check_workflow_pins.py --workflows-dir "$dir"; then
echo "::error::lint passed a workflow with a workflows_ref default — expected a non-zero exit"
exit 1
fi
echo "lint correctly rejected the reintroduced default"
- name: Smoke test — an unguarded ref checkout fails the lint
run: |
# The other half of the hole: no default to find, but a job that
# checks out at the ref without the fail-fast guard still lets an
# omitted input reach `ref: ''` and take the default branch.
dir="$(mktemp -d)"
# The ref expression goes in via a placeholder, for two reasons: a
# literal Actions expression written here would be evaluated when this
# run block is rendered (and `inputs` is empty in this workflow), so
# the fixture would silently lose the very line under test; and the
# lint's own "this file uses the input, so it must declare it" check
# would then read this workflow as a consumer it failed to parse.
D='$'
sed "s|__REF__|${D}{{ inputs.workflows_ref }}|" > "$dir/unguarded.yml" <<'YAML'
name: Unguarded
on:
workflow_call:
inputs:
workflows_ref:
type: string
required: true
jobs:
run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@abc
with:
ref: __REF__
YAML
grep -q 'inputs.workflows_ref' "$dir/unguarded.yml" # fixture intact
if python3 .github/workflow-pins/check_workflow_pins.py --workflows-dir "$dir"; then
echo "::error::lint passed an unguarded workflows_ref checkout — expected a non-zero exit"
exit 1
fi
echo "lint correctly rejected the unguarded checkout"
- name: Smoke test — an unreadable `steps:` warns but does not fail
run: |
# The third state (BE-9045): a job whose `steps:` the pre-scan cannot
# read keeps its fail-open drop — a fail-CLOSED escape would
# manufacture false CI failures — but it must SAY so. Both halves are
# pinned here because either one alone is a silent regression: a
# warning that started failing the run, or a drop that went quiet
# again.
dir="$(mktemp -d)"
# Same placeholder trick as the step above, for the same two reasons.
D='$'
sed "s|__REF__|${D}{{ steps.typo.outputs.ref }}|" > "$dir/escaped.yml" <<'YAML'
name: Escaped
on:
workflow_call:
inputs:
workflows_ref:
type: string
required: true
jobs:
run:
runs-on: ubuntu-latest
steps: [{id: resolve, run: echo hi}, {uses: actions/checkout@abc, with: {ref: "__REF__"}}]
YAML
grep -q 'steps.typo.outputs.ref' "$dir/escaped.yml" # fixture intact
status=0
python3 .github/workflow-pins/check_workflow_pins.py --workflows-dir "$dir" > "$dir/out.txt" 2>&1 || status=$?
cat "$dir/out.txt"
if [ "$status" -ne 0 ]; then
echo "::error::lint FAILED on a job whose steps: it could not read — the escape must stay fail-open (exit $status)"
exit 1
fi
if ! grep -q '::warning' "$dir/out.txt"; then
echo "::error::lint dropped a checkout in an unreadable steps: job and said nothing — expected a ::warning annotation"
exit 1
fi
echo "lint warned about the unjudged job without failing the run"