Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 45 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
# Lint, tests and production build. Never deploys.
# Runs on the self-hosted Contabo runner. This repo is PUBLIC, so pull requests from forks are skipped: they must never run on that machine.
# Runs on the self-hosted Contabo runners (up to two jobs at once across every repo, queued by repo, then push to main before pull
# requests before nightly). This repo is PUBLIC, so pull requests from forks are skipped: they must never run on that machine.
#
# One job runs at a time across every repo on that runner, so a pull request runs only what its change affects:
# - Every pull request: lint, then the tests scripts/affected-tests.mjs picks from the changed files, then the Next.js build only if
# the change can reach it (app/, components/, public/, proxy.js, instrumentation.js, or any file those import, the Next, PostCSS or
# package config, the lockfile).
# - Everything (all tests, the build) runs on: push to main, nightly at 05:00 UTC, and a manual run (workflow_dispatch).
# - Those full runs also write the test map (which files each test loaded) and cache it for the pull requests.
# Two jobs:
# check The gate. A ready pull request (not a draft, not Dependabot's) runs lint, then the tests scripts/affected-tests.mjs picks
# from the changed files, then the Next.js build only if the change can reach it (app/, components/, public/, proxy.js,
# instrumentation.js, or any file those import, the Next, PostCSS or package config, the lockfile). A push to main, the
# nightly run and a manual run (workflow_dispatch) run lint and the build.
# full-suite Every test. Push to main, nightly at 05:00 UTC and manual runs only. Nothing waits for it, so a slow or red suite
# never holds back the build; a failure turns the run red and GitHub mails the owner. It also writes the test map (which
# files each test loaded) and caches it for the pull requests.
#
# A pull request runs the FULL suite and the build when it changes any of these (scripts/affected-tests.mjs, FOUNDATIONAL):
# package.json and the lockfile; the Next, OpenNext, ESLint, PostCSS, Wrangler, Vitest and components configs and jsconfig.json;
# .github/ (this file); tests/setup, tests/stubs, tests/helpers/app.mjs and shallow.mjs; scripts/test-all.mjs and
# scripts/affected-tests.mjs; migrations/; models/; perf-budgets.json; worker-entry.mjs.
# It also runs everything when a changed file maps to no test, or when a third or more of the suite is affected.
# If a failure ever shows up only on the main run, say so in that pull request so the list above can be tightened.
# If a failure ever shows up only on the full-suite job, say so in that pull request so the list above can be tightened.
name: CI

on:
pull_request:
# A draft runs nothing; marking it ready starts the gate.
types: [opened, synchronize, reopened, ready_for_review]
push:
branches: [main]
schedule:
Expand All @@ -33,7 +38,12 @@ concurrency:

jobs:
check:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
if: >-
github.event_name != 'pull_request' || (
github.event.pull_request.head.repo.full_name == github.repository
&& !github.event.pull_request.draft
&& github.actor != 'dependabot[bot]'
)
runs-on: [self-hosted, linux, contabo]
# Jobs also count the wait for the runner's shared lock.
timeout-minutes: 360
Expand Down Expand Up @@ -66,28 +76,43 @@ jobs:
run: node scripts/affected-tests.mjs --base "origin/${{ github.base_ref }}" --map .ci/test-map.json --list .ci/affected-tests.txt --github-output "$GITHUB_OUTPUT"

- name: Tests (everything)
if: github.event_name != 'pull_request' || steps.plan.outputs.mode == 'all'
if: github.event_name == 'pull_request' && steps.plan.outputs.mode == 'all'
run: npm test

- name: Tests (affected only)
if: github.event_name == 'pull_request' && steps.plan.outputs.mode == 'subset'
run: node --import ./tests/setup/register.mjs --test $(cat .ci/affected-tests.txt)

- name: Build
if: github.event_name != 'pull_request' || steps.plan.outputs.build == 'true'
run: npm run build

full-suite:
if: github.event_name != 'pull_request'
runs-on: [self-hosted, linux, contabo]
timeout-minutes: 360
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: 24
cache: npm

- run: npm ci

- name: Tests (everything)
env:
TEST_MAP_DIR: ${{ runner.temp }}/test-map
run: |
mkdir -p "$TEST_MAP_DIR"
npm test

- name: Tests (affected only)
if: github.event_name == 'pull_request' && steps.plan.outputs.mode == 'subset'
run: node --import ./tests/setup/register.mjs --test $(cat .ci/affected-tests.txt)

- name: Write the test map
if: github.event_name != 'pull_request'
run: node scripts/test-map.mjs "${{ runner.temp }}/test-map" .ci/test-map.json

- name: Cache the test map
if: github.event_name != 'pull_request'
uses: actions/cache/save@v4
with:
path: .ci/test-map.json
key: test-map-${{ github.sha }}

- name: Build
if: github.event_name != 'pull_request' || steps.plan.outputs.build == 'true'
run: npm run build
Loading