Skip to content

Contributing

Local cluster

make kind-create            # idempotent; override KIND_CLUSTER_NAME / KIND_K8S_VERSION
make install                # CRDs into the current context
make kind-destroy

make kind-create gives you a single node. For anything that depends on scheduling across nodes, such as the node selectors and tolerations in the node placement guide, use the checked-in config instead, which brings up one control plane and two workers:

kind create cluster --config hack/kind-config.yaml

To run the controller against that cluster without building an image:

make run

Building and deploying an image

Build from Dockerfile.e2e, which compiles from source. The root Dockerfile is the release image: goreleaser assembles it from a prebuilt dist/ tarball, so a plain docker build against it fails on the tar step. This is also exactly what the e2e workflow does.

docker build -f Dockerfile.e2e -t controller:dev .
kind load docker-image controller:dev --name kind
make deploy IMG=controller:dev

Tag it something other than latest: kind re-pulls a latest tag even after kind load, and you get the published image instead of yours.

Tests

make test                   # unit + envtest suites
make lint                   # golangci-lint
make govulncheck            # CVE scan of the built binary

make test runs manifests and generate first, so a change to the API types that you forgot to regenerate shows up as a dirty tree rather than as a mysterious test failure.

make lint reports zero issues on a clean tree, so anything it prints is yours. CI gates only the lines a PR touches, which is deliberately weaker; the local target is the full-tree check.

Live e2e

make test-e2e runs against the current kube context and a real GitLab project. It needs a deployed operator and three environment variables, and skips when they are unset:

Variable Notes
GITLAB_E2E_URL GitLab instance to register against.
GITLAB_E2E_TOKEN Access token with both api and create_runner, Maintainer on the project. Use a throwaway project.
GITLAB_E2E_PROJECT_ID Numeric project id.

Copy .envrc.example to .envrc (gitignored) and fill them in, or let the OpenTofu stack in test/e2e/terraform provision the project, CI file and token and emit the file for you:

cd test/e2e/terraform
tofu output -raw e2e_env > "$(git rev-parse --show-toplevel)/.envrc"

Changing the API

The generated pieces are committed, so any change to api/ needs three regenerations before the tree is clean again:

make manifests              # CRDs in config/crd/bases
make generate               # deepcopy functions
make docs                   # docs/reference/api.md

CI runs make docs-verify, which regenerates the reference into a temp file and fails on any diff against the committed one. There is no way to land an API change with a stale reference.

Working on the docs

make docs-deps              # pip install mkdocs-material + mike
make docs-serve             # http://localhost:8000

mkdocs.yml is in strict mode, so a broken internal link or a nav entry pointing at a missing file fails the build locally exactly as it does in CI.

Guide examples are currently written inline, and nothing validates them against the CRDs. pymdownx.snippets is configured so a page can include a manifest straight from config/samples instead, which removes the retyping and so the drift, but note that it only inserts the file and checks the path exists. Validating those manifests against the CRDs would be a separate CI step, and there is not one yet.

How docs get published

docs.yml deploys the dev alias on every push to main that touches docs/** or mkdocs.yml. The release workflow deploys a numbered version once the image is published, and moves the latest alias unless the tag is a backport onto an older minor, in which case that version is published without disturbing latest. Both push to the gh-pages branch, which is what GitHub Pages serves.

To publish a docs-only fix for the current release without cutting a new version, run the docs workflow manually and give it the version to overwrite.

The site is served from the gh-pages branch at https://gitlab-runner-operator.alekc.dev/, with the custom domain set in the repository's Pages settings. GitHub keeps that domain as a CNAME file at the root of gh-pages; mike only writes version directories, versions.json and the root redirect, so it leaves that file alone. Deleting it drops the site back to alekc.github.io.