Skip to content
Merged
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
70 changes: 70 additions & 0 deletions .github/actions/ci-gate/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# SPDX-License-Identifier: Apache-2.0
# Copyright Open Network Fabric Authors

# Enable a job on a deep run (push, merge queue, or dispatch) or when a pull
# request has a requested label.

name: "CI gate"
description: "Decide whether a phased job runs on this event"

inputs:
labels:
description: >-
Space-separated label suffixes; `ci:+` is implied.
required: true
always-labels:
description: >-
Label suffixes that enable every gate.
required: false
default: "merge-ready"
on-value:
description: "What to emit when the gate is on."
required: false
default: "true"
off-value:
description: "What to emit when the gate is off."
required: false
default: "false"

outputs:
value:
description: "`on-value` if the gate is on, `off-value` if it is not."
value: "${{ steps.gate.outputs.value }}"

runs:
using: "composite"
steps:
- id: "gate"
shell: "bash"
# Avoid expression interpolation in the script body.
env:
EVENT: "${{ github.event_name }}"
LABELS: "${{ toJSON(github.event.pull_request.labels.*.name) }}"
WANTED: "${{ inputs.labels }} ${{ inputs.always-labels }}"
ON_VALUE: "${{ inputs.on-value }}"
OFF_VALUE: "${{ inputs.off-value }}"
run: |
set -euo pipefail
# Split WANTED into suffixes without allowing shell glob expansion.
set -f

on=false
case "${EVENT}" in
push | merge_group | workflow_dispatch)
on=true
;;
*)
for suffix in ${WANTED}; do
if jq -e --arg l "ci:+${suffix}" \
'if type == "array" then index($l) != null else false end' \
<<<"${LABELS:-[]}" >/dev/null; then
on=true
break
fi
done
;;
esac

if "${on}"; then value="${ON_VALUE}"; else value="${OFF_VALUE}"; fi
printf '%s -> %s\n' "${WANTED}" "${value}"
printf 'value=%s\n' "${value}" >>"${GITHUB_OUTPUT}"
77 changes: 59 additions & 18 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,12 @@ Production artifacts are produced via nix builds in a separate CI workflow.

### Main steps

1. Check code changes to determine which tests are required
2. Build and test across a matrix of nix targets and profiles:
- Nix targets: `tests.all`, `frr.dataplane`, `dataplane`
- Profiles: `debug`, `release`
3. Run `cargo deny` checks for license and security issues
4. Execute tests:
- Regular tests using `cargo nextest` (via `just test`)
- Shuttle tests (concurrent execution testing with `features=shuttle`)
5. Run `cargo clippy` for linting (via `just clippy`)
6. Build documentation with `rustdoc` (via `just docs`)
7. Run doctests (via `just doctest`)
8. Push container images to GHCR (for non-test targets)
9. Run VLAB/HLAB integration tests (virtual/hybrid lab environments)
10. Publish release artifacts and bump fabricator on tag pushes
1. Plan jobs from the event and `ci:+` labels
2. Run lint, debug checks, and debug coverage on pull requests
3. Run expensive profiles and specialized jobs on labeled or deep runs
4. Build and push containers required by VLAB/HLAB
5. Aggregate required results in the `Summary` job
6. Publish release artifacts and bump fabricator on tag pushes

### Manual dispatch options

Expand All @@ -60,23 +52,72 @@ Production artifacts are produced via nix builds in a separate CI workflow.

### Pull Request label options

- `ci:+merge-ready` - Run everything the merge queue will run, so a failure
is found before queueing; HLAB remains excluded, because the merge queue does
not run it either. It tests this branch's head while the queue tests the
result of merging it, so a green run here can still fail in the queue if
`main` moved underneath it
- `ci:+test/all-profiles` - Add release and fuzz checks plus fuzz coverage
- `ci:+sanitize` - Run address and thread sanitizer tests
- `ci:+test-each` - Test each workspace package independently
- `ci:+miri` - Run Miri checks
- `ci:+wasm` - Run the WASM build check
- `ci:+concurrency` - Run Shuttle and Loom tests
- `ci:+cross` - Build all cross-platform containers
- `ci:+cross/full` - Also run the workspace test suite under qemu-user, on the
two aarch64 musl legs. Gated like every other job, so the merge queue and
`ci:+merge-ready` include it
- `ci:+vlab` - Run VLAB tests on this PR
- `ci:+hlab` - Run HLAB tests on this PR
- `ci:+release` - Enable release tests for VLAB/HLAB on this PR
- `ci:-upgrade` - Disable upgrade tests on this PR
- `ci:-upgrade` - Disable upgrade tests on this PR. `ci:+merge-ready`
overrides it, because the merge queue has no labels to read and would
run the upgrade legs anyway; a `merge-ready` run that skipped them would
not be the preview it claims to be

Labels are additive, and optional: a pull request needs none of them.
`ci:-upgrade` is the sole exception, subtracting a job that would otherwise run.

Adding a label starts a **new** workflow run, and that run repeats the default
jobs as well as the ones the label enabled. This applies to _every_ label, not
only the `ci:` ones: the trigger cannot filter by name and the run in flight is
cancelled, so adding `bug` or `documentation` mid-run discards whatever it had
finished. Label first, or wait for the run to end.
GitHub cannot add a job to a run that already exists, so this is unavoidable
without teaching jobs to skip work an earlier run finished for the same commit.
Set the labels you expect to need when opening the pull request and the repeat
does not arise.

Not labelling is also a reasonable choice.
The gated jobs are the ones judged unlikely to fail, and the merge queue runs
the full suite regardless, so a bad assumption costs a re-queue rather than a
bad merge.
When the merge queue does catch one of these, add the matching label and push
the fix, which keeps the check on the pull request from then on.
If those queue failures stop being rare, the phasing is worth revisiting.

### Job matrix

- Nix targets: `tests.all` (runs tests, lints, docs), `frr.dataplane`
and `dataplane` (build and push containers)
- Profiles: `debug`, `release`
- Checks: `debug` by default; `release` and `fuzz` on deep runs
- Coverage: `debug` by default; `fuzz` on deep runs
- Miri: required on deep runs; opt-in on pull requests with `ci:+miri`
- Containers: debug/release for dataplane and FRR; release for validator
- VLAB configurations: spine-leaf fabric mode, L2VNI/L3VNI VPC modes,
with gateway enabled

### Artifacts

- Container images pushed to GitHub Container Registry (GHCR)
- Release containers published on tag pushes via `just push`
- Coverage reports from each `coverage/<profile>` job, kept for 7 days:
- `coverage-html-<profile>.tar.gz` - `llvm-cov` HTML report, including the
per-branch counts that Codecov does not render. Unpack and open
`html/index.html`
- `lcov-<profile>.info` - LCOV report with repository-relative paths, for
feeding to other coverage tooling

Both upload unarchived, so they download as the named file rather than
wrapped in a zip.

---

Expand Down
Loading
Loading