Skip to content
Open
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion .github/workflows/ci-build-artifact-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ permissions:

jobs:
build-artifact-gate:
name: Block committed build artifacts
name: Check gated paths
runs-on: ${{ fromJSON(inputs.runs-on) }}
steps:
- name: Checkout repository
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ permissions:

jobs:
build:
name: Build
name: Production build
runs-on: ${{ fromJSON(inputs.runs-on) }}
defaults:
run:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-detect-changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ permissions:

jobs:
detect:
name: Detect changes
name: Bucket changed files
runs-on: ${{ fromJSON(inputs.runs-on) }}
outputs:
# css/js/php come from the scope step; total/ignored/gha stay repo-wide on detect.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-lint-php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ permissions:

jobs:
lint-php:
name: Lint PHP
name: ${{ inputs.enable-phpstan && 'PHPCS + PHPStan' || 'PHPCS' }}
runs-on: ${{ fromJSON(inputs.runs-on) }}
defaults:
run:
Expand Down
42 changes: 41 additions & 1 deletion .github/workflows/wp-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,8 @@ jobs:
working-dir: ${{ needs.validate-inputs.outputs.working-dir }}

test-php:
name: Test PHP
# Named explicitly: GitHub's default suffix for this matrix renders as "(8.3, )".
name: Test PHP ${{ matrix.php }}${{ matrix.wp != '' && format(', WP {0}', matrix.wp) || '' }}
needs: [validate-inputs, detect-changes]
if: >-
(needs.detect-changes.outputs.php-count > 0 || needs.detect-changes.outputs.gha-count > 0)
Expand Down Expand Up @@ -421,3 +422,42 @@ jobs:
runs-on: ${{ inputs.runs-on }}
gated-paths: ${{ inputs.gated-paths }}
working-dir: ${{ needs.validate-inputs.outputs.working-dir }}

# The one check to require in branch protection: "<caller job> / CI result". The other check
# names and their count change with project-type, skip, what changed and the PHP matrix, and a
# skipped required check counts as passing. Runs on always(), so a failed, cancelled or
# never-started job fails it, while a job skipped by its own gate does not.
ci-result:
name: CI result
needs:
- validate-inputs
- detect-changes
- lint-css
- lint-js
- lint-php
- test-js
- test-php
- build
- build-artifact
- a11y
- artifact-gate
if: always()
runs-on: ${{ fromJSON(inputs.runs-on) }}
steps:
- name: Check every job's result
shell: bash
env:
NEEDS: ${{ toJSON(needs) }}
run: |
set -euo pipefail
{
echo "| Job | Result |"
echo "|---|---|"
jq -r 'to_entries[] | "| `\(.key)` | \(.value.result) |"' <<< "$NEEDS"
} >> "$GITHUB_STEP_SUMMARY"
bad="$(jq -r 'to_entries[] | select(.value.result != "success" and .value.result != "skipped") | "\(.key) (\(.value.result))"' <<< "$NEEDS")"
if [ -n "$bad" ]; then
echo "::error::CI did not pass: $(printf '%s' "$bad" | paste -sd ',' - | sed 's/,/, /g')."
exit 1
fi
echo "Every job passed or was skipped by its own gate."
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,6 @@ Use `bash -eo pipefail -c`, never bare `bash -c`: `-e` is not inherited, so a ba
## PR instructions

- GitHub flow: branch off `main`, PR back into `main`. A release is a tag on `main` that release-please cuts. Name branches `<type>/<kebab-slug>` (`fix/runs-on-input`). Never commit to `main` directly.
- [Conventional Commits](https://www.conventionalcommits.org/) for the PR title, typed by what changes for callers: `fix:` or `feat:` for any change to a reusable workflow, and `feat!:` for anything a caller has to change for (a removed, renamed or newly required input, a new `permissions:` entry, a renamed job `name:`). `docs:` and `chore:` (e.g. `chore(self-check): ...`) are only for this repo's own files and don't cut a release. `ci:` isn't accepted here: nearly everything in this repo is CI, so the title check makes the choice explicit. Example: `feat(lint-css): Add lint-css workflow`.
- [Conventional Commits](https://www.conventionalcommits.org/) for the PR title, typed by what changes for callers: `fix:` or `feat:` for any change to a reusable workflow, and `feat!:` for anything a caller has to change for (a removed, renamed or newly required input, a new `permissions:` entry). A renamed job `name:` changes a check name callers may require: it is `feat:` when a code search finds no caller requiring the old name, with that evidence in the PR body and the old and new names in the README's "Renamed checks" list, and `feat!:` otherwise. `docs:` and `chore:` (e.g. `chore(self-check): ...`) are only for this repo's own files and don't cut a release. `ci:` isn't accepted here: nearly everything in this repo is CI, so the title check makes the choice explicit. Example: `feat(lint-css): Add lint-css workflow`.
- Ensure the Code quality commands pass, the caller example is updated alongside the workflow, and the PR title says what changed for a consumer — see `CONTRIBUTING.md` for the full checklist.
- Ask first before removing or renaming an input, adding a deploy target, or granting a job `contents: write` it does not already need.
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,35 @@ Node comes from `.nvmrc` in `working-dir`, then the repository root, falling bac
`node-version` to override it. `wp-ci.yml` also forwards `php-version`, the Composer settings and a
`*-command` input per job, each falling back to that job's default when left empty.

### Branch protection

Require one check per `wp-ci.yml` call: `<caller job name> / CI result`, for example
`CI / CI result`, or `plugins/acme-blocks / CI result` for each unit of the matrix in
[`examples/wp-ci.yml`](examples/wp-ci.yml). It fails when any job failed, was cancelled or never
started, and passes when every job passed or was skipped by its own gate. Don't require the other
checks: which of them run, and their names, change with `project-type`, `skip`, the PHP matrix and
what a pull request touched, and a required check that never reports leaves the pull request
waiting forever.

`CI result` only exists when the calling job runs. A calling job with its own `if:` that skips
reports no `CI result`, so a repository that gates the call needs its own always-running aggregate
job, or should not require `CI result`.

#### Renamed checks

Check names come from job names, so a rename changes what a ruleset must require. Since v1.2.0:

| Workflow | Old check name | New check name |
|---|---|---|
| `ci-lint-php.yml` | `Lint PHP` | `PHPCS`, or `PHPCS + PHPStan` with `enable-phpstan` |
| `ci-build.yml` | `Build` | `Production build` |
| `ci-detect-changes.yml` | `Detect changes` | `Bucket changed files` |
| `ci-build-artifact-gate.yml` | `Block committed build artifacts` | `Check gated paths` |
| `wp-ci.yml` (`test-php` matrix) | `Test PHP (<php>, <wp>)`, GitHub's default, e.g. `Test PHP (8.3, )` | `Test PHP <php>`, or `Test PHP <php>, WP <wp>` |

Called through `wp-ci.yml`, each appears after the `wp-ci.yml` job, for example
`CI / Lint PHP / PHPCS`; called directly, after the caller's job, for example `lint / PHPCS`.

## Workflows

Each workflow declares its inputs, secrets and outputs in its own `workflow_call` block, and GitHub
Expand Down
Loading