c419a73bf5
CI / scan (pull_request) Failing after 1m22s
CI / check (pull_request) Failing after 1m27s
CI / commits (pull_request) Failing after 1m31s
CI / perf (pull_request) Failing after 14s
CI / a11y (pull_request) Failing after 1m36s
Docs site / build (pull_request) Failing after 1m41s
The CI runner (catthehacker/ubuntu:act-22.04) fails `pnpm install --frozen-lockfile` because grpc-tools' postinstall downloads a precompiled protoc archive from node-precompiled-binaries.grpc.io and Node 24's bundled CA set cannot verify the chain there. Node 24 itself surfaces the fix in the error message: pass `--use-system-ca` so Node consults the OS CA store in addition to its bundled set. The runner image carries the standard Ubuntu `ca-certificates` bundle, which validates the impacted chains. Applied at workflow scope (env: block) on both ci.yml and docs-site.yml so every Node child (pnpm itself plus every postinstall it spawns) inherits the option. Future native deps that hit the same TLS rejection will benefit automatically.
227 lines
9.7 KiB
YAML
227 lines
9.7 KiB
YAML
# Per ADR-0015 (CI/CD on Gitea Actions).
|
|
# Thin YAML — orchestration lives in package.json scripts (ci:check,
|
|
# ci:audit, ci:commits, ci:perf) and Nx targets. Any change to gate
|
|
# behaviour belongs in those scripts, not in this file.
|
|
|
|
name: CI
|
|
|
|
on:
|
|
pull_request:
|
|
branches: [main]
|
|
push:
|
|
branches: [main]
|
|
|
|
# Node 24's bundled CA set does not include every intermediate the
|
|
# self-hosted runner's network path serves (the precompiled-binary
|
|
# CDN behind grpc-tools is the first surface where this surfaced).
|
|
# `--use-system-ca` tells Node to consult the OS CA store in
|
|
# addition to its bundled set — supported since Node 22 and the
|
|
# documented Node-team fix for exactly this class of TLS-chain
|
|
# rejection. The runner image (`catthehacker/ubuntu:act-22.04` per
|
|
# ADR-0015) carries the standard Ubuntu `ca-certificates` bundle,
|
|
# which validates the impacted chains.
|
|
env:
|
|
NODE_OPTIONS: --use-system-ca
|
|
|
|
jobs:
|
|
check:
|
|
runs-on: [self-hosted, on-prem]
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
# Derive NX_BASE / NX_HEAD for `nx affected`. Replaces
|
|
# nrwl/nx-set-shas@v4, which is GitHub-only (it queries the GitHub
|
|
# API to find the last successful workflow run, returning 404 on
|
|
# Gitea). HEAD~1 is a reasonable approximation for push events on
|
|
# a squash-merge trunk; pull_request uses the merge-base with the
|
|
# target branch.
|
|
- name: Derive Nx affected base and head
|
|
shell: bash
|
|
run: |
|
|
# `actions/checkout@v4` with fetch-depth: 0 already pulls every
|
|
# branch and tag, so origin/<base_ref> is present locally — no
|
|
# extra `git fetch` is needed (and `--depth=0` is invalid: git
|
|
# requires a positive integer).
|
|
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
|
echo "NX_BASE=$(git merge-base HEAD origin/${{ github.base_ref }})" >> "$GITHUB_ENV"
|
|
else
|
|
echo "NX_BASE=HEAD~1" >> "$GITHUB_ENV"
|
|
fi
|
|
echo "NX_HEAD=HEAD" >> "$GITHUB_ENV"
|
|
- uses: pnpm/action-setup@v6
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: 'pnpm'
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm ci:check
|
|
|
|
scan:
|
|
runs-on: [self-hosted, on-prem]
|
|
# Step ordering matters here: Trivy and gitleaks BOTH run before
|
|
# `pnpm install`. Reason: gitleaks scans the working tree
|
|
# (`--no-git --source .`), and after install, `node_modules/`
|
|
# and `.pnpm-store/` are full of upstream packages whose READMEs
|
|
# and test fixtures contain demo RSA keys / fake API tokens —
|
|
# gitleaks then false-positives on them by the hundreds (caught
|
|
# the hard way: 381 hits on the first run). Trivy reads
|
|
# `pnpm-lock.yaml` for its vuln scan, not `node_modules`, so it
|
|
# also doesn't need install. `pnpm ci:audit` does the same — it
|
|
# queries the advisory DB against the lockfile.
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v6
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: 'pnpm'
|
|
# Dependency vulnerability scan. Trivy is a Go binary, not an npm
|
|
# package, so it cannot live in package.json scripts as cleanly
|
|
# as audit/lint do.
|
|
#
|
|
# We deliberately avoid `aquasecurity/trivy-action`. On cache
|
|
# miss the action falls back to `git clone github.com/aquasecurity/
|
|
# trivy` to fetch its install script, using `actions/checkout`
|
|
# which defaults `with.token` to `${{ github.token }}` (Gitea's
|
|
# auto-token, useless for github.com). The clone hits the
|
|
# anonymous github.com rate limit and fails with "could not
|
|
# read Username". Passing GITHUB_TOKEN as an env var doesn't
|
|
# help — actions/checkout reads it from `inputs.token`, not env.
|
|
#
|
|
# Direct curl + tar is simpler, predictable, and gives us an
|
|
# explicit version pin instead of `@master`. GITHUBCOM_TOKEN is
|
|
# passed to handle the github.com rate limit on the release
|
|
# download in the worst case (release artefacts are usually
|
|
# unmetered, but auth is free insurance).
|
|
- name: Install Trivy
|
|
env:
|
|
# renovate: datasource=github-releases depName=aquasecurity/trivy
|
|
TRIVY_VERSION: '0.70.0'
|
|
GITHUB_TOKEN: ${{ secrets.GITHUBCOM_TOKEN }}
|
|
run: |
|
|
curl -sfL \
|
|
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
|
-o /tmp/trivy.tar.gz \
|
|
"https://github.com/aquasecurity/trivy/releases/download/v${TRIVY_VERSION}/trivy_${TRIVY_VERSION}_Linux-64bit.tar.gz"
|
|
tar -xzf /tmp/trivy.tar.gz -C /usr/local/bin trivy
|
|
trivy --version
|
|
- name: Run Trivy
|
|
# `--scanners vuln`: limit Trivy to vulnerability scanning. Its
|
|
# secret scanner false-positives on demo RSA keys embedded in
|
|
# the README/fixtures of cryptographic npm packages (which
|
|
# land under .pnpm-store/), and we already have gitleaks below
|
|
# as the dedicated secret-scan gate. Trivy's intent in this
|
|
# job, per ADR-0015, was always "dependency vulnerability
|
|
# scan" — restoring that scope.
|
|
run: |
|
|
trivy fs \
|
|
--scanners vuln \
|
|
--ignore-unfixed \
|
|
--skip-dirs node_modules \
|
|
--exit-code 1 \
|
|
--severity CRITICAL,HIGH \
|
|
.
|
|
# Secret scan. Same install pattern as Trivy: gitleaks is a Go
|
|
# binary, and the official `gitleaks/gitleaks-action@v2` wrapper
|
|
# is now paywalled for organisations (a GITLEAKS_LICENSE secret
|
|
# from gitleaks.io is required, otherwise the action errors out
|
|
# with `🛑 missing gitleaks license`). The binary itself stays
|
|
# MIT-licensed and free — installing it directly bypasses the
|
|
# wrapper and gives us version pinning for free.
|
|
- name: Install gitleaks
|
|
env:
|
|
# renovate: datasource=github-releases depName=gitleaks/gitleaks
|
|
GITLEAKS_VERSION: '8.30.1'
|
|
GITHUB_TOKEN: ${{ secrets.GITHUBCOM_TOKEN }}
|
|
run: |
|
|
curl -sfL \
|
|
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
|
|
-o /tmp/gitleaks.tar.gz \
|
|
"https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz"
|
|
tar -xzf /tmp/gitleaks.tar.gz -C /usr/local/bin gitleaks
|
|
gitleaks version
|
|
- name: Run gitleaks
|
|
# `--no-git --source .` scans the working tree only. The scan
|
|
# job uses a shallow checkout, so a git-history scan would not
|
|
# see beyond HEAD anyway; the weekly security-scheduled
|
|
# workflow does the deep history scan with a full clone.
|
|
# `--redact` masks any matched secret in the log output so we
|
|
# do not leak it via the CI logs themselves.
|
|
run: |
|
|
gitleaks detect \
|
|
--no-git \
|
|
--source . \
|
|
--redact \
|
|
--exit-code 1
|
|
# npm-advisory check (against pnpm-lock.yaml). Run last so
|
|
# `pnpm install` does not pollute the working tree before the
|
|
# scanners above.
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm ci:audit
|
|
|
|
commits:
|
|
# PRs opened by Renovate (apf-portal-bot) carry commit messages
|
|
# generated from a vetted Conventional-Commits template — running
|
|
# commitlint on them is tautological. Per ADR-0017 amendment.
|
|
if: github.event_name == 'pull_request' && github.event.pull_request.user.login != 'apf-portal-bot'
|
|
runs-on: [self-hosted, on-prem]
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
with:
|
|
fetch-depth: 0
|
|
- uses: pnpm/action-setup@v6
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: 'pnpm'
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: COMMIT_LINT_FROM=origin/main pnpm ci:commits
|
|
|
|
perf:
|
|
# Skip the Lighthouse run on PRs opened by Renovate (apf-portal-bot):
|
|
# the per-PR perf signal on a dep bump is essentially zero (no
|
|
# routes yet, bundle is the static placeholder), and the Lighthouse
|
|
# round-trip burns several minutes per PR. Push events on `main`
|
|
# still run perf — we catch regressions immediately post-merge,
|
|
# not pre-merge. Per ADR-0017 amendment.
|
|
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'apf-portal-bot'
|
|
runs-on: [self-hosted, on-prem]
|
|
# Lighthouse CI drives a real Chrome instance; the default act runner
|
|
# image (catthehacker/ubuntu:act-22.04) ships without one. The :full
|
|
# variant adds Chrome, Firefox, and the GUI-test toolchain — pinned
|
|
# to the same Ubuntu 22.04 base as the default labels for parity.
|
|
container:
|
|
image: catthehacker/ubuntu:full-22.04
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v6
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: 'pnpm'
|
|
- run: pnpm install --frozen-lockfile
|
|
- run: pnpm ci:perf
|
|
- uses: actions/upload-artifact@v7
|
|
if: always()
|
|
with:
|
|
name: lighthouseci-report
|
|
path: .lighthouseci/
|
|
retention-days: 30
|
|
|
|
a11y:
|
|
runs-on: [self-hosted, on-prem]
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
- uses: pnpm/action-setup@v6
|
|
- uses: actions/setup-node@v6
|
|
with:
|
|
node-version-file: '.nvmrc'
|
|
cache: 'pnpm'
|
|
- run: pnpm install --frozen-lockfile
|
|
# Placeholder until the e2e a11y suite (axe-core via Playwright,
|
|
# per ADR-0016) is wired with the first real screens. The job
|
|
# exists so branch protection can require it from day one - it
|
|
# currently no-ops with a clear message.
|
|
- run: echo "a11y gate placeholder - axe-core via Playwright wires up with the first real screens (ADR-0016)."
|