Continuous Integration
CI workflow and local parity
Pull request, main, nightly, and release workflows, plus the closest local command set for each validation lane.
Diagram
CI and publish flow
Pull requests are change-routed; publish channels add provenance and reproducibility gates before anything is promoted.
Verify
PR-equivalent local gate
make bootstrap
make doctor
make ci-core
Default local baseline before handing work to CI.
Decision matrix
Map CI lanes to local commands
| CI concern | Run locally | Notes |
|---|---|---|
| Core PR validation | make ci-core | Covers lint, formatting, tidy, vendor, generated artifacts, tests, docs, Helm, security, fuzz smoke, and config compatibility. |
| Docs-only changes | make docs-build | For changes isolated to documentation, routing, or site behavior. |
| Dependency and license policy | make verify-vendor and make license-check | The repository enforces vendored dependency resolution and shipped-license allowlists through its local and CI artifact checks. |
| Static security and filesystem scans | make semgrep-ci, make security-ci, and make security-scan-built-images | Run these when dependencies, network-facing code, container-facing surfaces, or CI/config security surfaces changed. make semgrep-ci is the blocking local Semgrep scan across cmd, internal, api, hack, config, and .github; make security-ci includes that scan plus vulncheck, license policy, and the Trivy filesystem scan. |
| Focused E2E and platform validation | make test-e2e-ci ..., make helm-e2e-smoke, or make test-e2e-existing ... | Label filters and the existing-cluster path provide smaller or platform-specific reproductions. |
CI and release workflows enforce vendored Go dependencies. After dependency changes, rerun make verify-vendor. License verification uses that same vendored view of the dependency graph.
Inline nosemgrep suppressions are reserved for bounded intentional exceptions. Put the suppression on the exact reported line and add a plain-language explanation immediately above it.
Decision matrix
How routing expands
| Situation | What usually happens | Why it matters |
|---|---|---|
| Docs-only or workflow-only pull request | CI skips broad E2E by default and focuses on the relevant smaller lanes. | You avoid paying for cluster work that cannot fail for the files you touched. |
| PR touches backup, upgrades, security, provisioner, admission, or controller-critical code | Targeted E2E shards expand automatically. | Coverage follows the risk of the changed surface. |
Maintainer adds ci:full-e2e | The broader E2E suite runs. | This is appropriate when the change spans enough surface area that targeted routing is no longer sufficient. |
| Nightly, edge, or tagged release flow | Publish channels add provenance, reproducibility, and release-oriented hardening gates. | Passing PR CI alone is not enough to publish artifacts. |
Inspect
Typical focused E2E reproductions
make test-e2e-ci \
KIND_NODE_IMAGE=kindest/node:v1.35.1 \
E2E_LABEL_FILTER='(((lifecycle && !tls) || manager) && !openshift)' \
E2E_PARALLEL_NODES=2
make helm-e2e-smoke
make fuzz
FUZZ_TARGET_FILTER='FuzzDiscoverConfig|internal/service/upgrade' make fuzz
Choose the smallest reproduction that still matches the CI lane under investigation.
CI E2E shards write both JUnit and Ginkgo JSON reports under the uploaded E2E artifact. The workflow summary includes the selected label filter, spec counts, failures, and the slowest specs. Local reproductions can use the same report path variables:
Inspect
Local E2E report output
make test-e2e-ci \
E2E_LABEL_FILTER='lifecycle && !openshift' \
E2E_JUNIT_REPORT=artifacts/e2e-reports/local/junit.xml \
E2E_JSON_REPORT=artifacts/e2e-reports/local/ginkgo.json \
E2E_FAIL_ON_EMPTY=true
Use go run ./hack/tools/e2e_report --json-report artifacts/e2e-reports/local/ginkgo.json to render the same Markdown summary locally.
The E2E suite manifest is validated in ci-core through make verify-e2e-manifest. That check regenerates the catalog from ginkgo outline, validates test/e2e/suites.yaml, generates the GitHub Actions PR and nightly E2E matrices, and fails if suite ownership, labels, coverage tags, risk tier, isolation class, or routing metadata drift.
The same manifest owns E2E parallelism. Matrix rows include parallel_nodes, and CI passes that value to E2E_PARALLEL_NODES. Lanes may use more than one Ginkgo process only when every assigned suite is declared parallel-safe or serial; shared-cluster, global-mutator, external-cluster, and multi-cluster suites stay single-process until their isolation model changes.
Lanes may also declare prLabelFilter for pull-request routing. CI uses that optimized filter unless a full E2E run is requested or the run is for main; nightly and release-gate profiles continue to use the full lane labelFilter.
Nightly E2E routing is manifest-driven. The daily profile runs full coverage on the primary Kubernetes version and compatibility smoke rows on adjacent supported versions. The weekly profile runs full compatibility coverage across supported Kubernetes versions. Maintainers can manually dispatch the release-gate profile, optionally filtered to one lane or one Kubernetes version while preserving the same manifest validation.
Inspect
Inspect generated E2E matrices
make e2e-ci-matrix
make e2e-nightly-matrix E2E_NIGHTLY_PROFILE=daily
make e2e-nightly-matrix E2E_NIGHTLY_PROFILE=weekly-full
make e2e-nightly-matrix \
E2E_NIGHTLY_PROFILE=release-gate \
E2E_NIGHTLY_LANE=core \
E2E_NIGHTLY_KUBERNETES=1.35.1
Use these before changing test/e2e/suites.yaml or workflow routing.
test/e2e/suites.yaml owns the E2E version policy. Keep the OpenBao default image and the active Kind Kubernetes versions under versions instead of repeating concrete versions in each profile. The nightly profiles should reference @primary, @compatibility, and @releaseGate; update those central sets when Kind publishes a new runnable node image.
After CI parity
You are reading the unreleased main docs. Use the version menu for the newest published release, or check the release notes for what is already out.
Was this page helpful?
Use Needs work to open a structured GitHub issue for this page. The Yes button only acknowledges the signal locally.