Thank you for your interest in contributing! This document provides guidelines and instructions for contributing.
| Tool | Version | Install |
|---|---|---|
| Go | 1.26+ | golang.org/dl |
| Docker | 24+ | docs.docker.com |
| kubectl | 1.32+ | kubernetes.io |
| Helm | 3.16+ or 4.x | helm.sh |
| k3d or Kind | k3d 5.8+ / Kind 0.24+ | k3d.io or kind.sigs.k8s.io |
| Python 3 + pip | 3.8+ | For yamllint (YAML linting in make verify) |
The Makefile auto-installs these Go tools on first use (to $GOPATH/bin):
golangci-lint, gotestsum, controller-gen, setup-envtest, chainsaw, kustomize, helm-docs.
It also installs the Helm unittest plugin automatically when needed for
make helm-unittest or make verify.
yamllint (Python) is auto-installed via pip if missing when running make yaml-lint.
# Clone the repo
git clone https://github.com/attune-io/attune.git
cd attune
# Install Go dependencies
go mod download
# Build the operator and kubectl plugin
make build
make build-plugin
# Run all CI checks locally (lint, test, helm-docs, CRD freshness)
make verify# Unit tests (1000+ tests, 80% coverage threshold enforced, currently >90%)
make test
# Integration tests (uses envtest, no cluster needed)
make test-integration
# E2E tests (requires a local cluster with operator deployed)
# Recommended: k3d, because CI and nightly workflows run on k3d/K3S
make k3d-create # create k3d cluster
make k3d-deploy IMG=attune:e2e # build, load, deploy
make test-e2e # run Chainsaw E2E scenarios
make test-e2e-go # run full Go E2E suite
make k3d-delete # clean up
# Alternative: Kind (supported, but local-only and not the default CI path)
make kind-create # create Kind cluster
make kind-deploy IMG=attune:e2e # build, load, deploy
make test-e2e # run Chainsaw E2E scenarios
make test-e2e-go # run full Go E2E suite
make kind-delete # clean up
# All tests in sequence (unit + integration + Chainsaw + Go E2E)
# NOTE: E2E requires a cluster with the operator deployed (see above).
# Unit and integration tests run without any cluster.
make test-all
# Single command: auto-provisions k3d, deploys, runs unit + integration +
# Chainsaw E2E + full Go E2E suite, then cleans up
make test-local
# Fast end-to-end smoke check: auto-provisions k3d, deploys, runs one
# Chainsaw scenario + one Go E2E test, then cleans up
make test-local-smokemake test-e2e, make test-e2e-go, and make test-e2e-smoke work with
an already deployed k3d or Kind cluster.
make test-local includes the full Go E2E suite. Expect longer runtime than
make test-local-smoke, because the longer Prometheus warm-up scenarios now run
in the standard make test-e2e-go target and regular CI.
Important: make k3d-deploy and make kind-deploy mutate
config/manager/kustomization.yaml. Before committing, always restore it:
git checkout config/manager/kustomization.yamlmake docker-build IMG=attune:devRun make verify before every commit. It covers:
- golangci-lint (code quality + import alias enforcement)
- Unit and integration tests with coverage threshold (80%)
- Helm lint + template validation
- Helm chart docs freshness and unit tests
- CRD manifest freshness (
make manifestsoutput matches committed files) - Grafana dashboard sync (
deploy/grafana/dashboard.jsonsource and generated Helm dashboard stay aligned) - Documentation defaults consistency check
- govulncheck for known vulnerabilities
For faster feedback on docs-only or YAML-only changes, use make verify-quick
(skips integration tests and govulncheck).
If you changed CRD types (api/v1alpha1/), also run:
make manifests # regenerate CRDs and RBAC
make generate # regenerate deepcopy methodsCommit the generated output.
The docs/ directory is configured as an MkDocs
site with the Material theme.
pip install mkdocs-material
mkdocs serveThen open http://127.0.0.1:8000. Changes to markdown files reload
automatically.
- Every markdown file under
docs/must start with a# Titleheading. - Navigation order is controlled by
mkdocs.yml(thenav:key). - Admonitions (
!!! note,!!! tip,!!! warning) are supported.
If you are reproducing CI failures locally, prefer make verify over hand-built
Helm commands. The CI workflow includes a couple of details that are easy to
miss when replaying the Helm jobs manually:
- template validation simulates cert-manager CRDs with
--api-versions cert-manager.io/v1 - the Helm unittest plugin is installed from the exact release asset filename, which may not match the tag string one-to-one
If a local manual replay disagrees with CI, check the exact commands in
.github/workflows/ci.yaml before assuming the
chart or workflow is wrong.
CI runs on GitHub-hosted ubuntu-latest runners by default. To switch to
self-hosted runners, set the repository variable RUNNER to self-hosted
in Settings > Secrets and variables > Actions > Variables.
To check queued or in-progress CI runs:
make ci-runner-statusAll contributions must be signed off under the Developer Certificate of Origin (DCO v1.1). This certifies that you wrote the contribution or otherwise have the right to submit it under the project's Apache 2.0 license.
Add the sign-off by passing -s to git commit:
git commit -s -m "feat: add time-of-day-aware algorithm"This appends a Signed-off-by trailer with your name and email:
Signed-off-by: Your Name <your@email.com>
The name and email must match your git config user.name and
git config user.email. The DCO CI check (.github/workflows/dco.yaml)
verifies every commit on pull requests and will block merging if any commit
is missing the sign-off.
If you forgot to sign off, amend your commits:
# Amend the last commit
git commit --amend -s --no-edit
# Sign off all commits on a branch
git rebase --signoff main- Fork the repository and create a branch from
main - Make your changes with tests
- Sign off every commit (
git commit -s) - Run
make verifyto run all CI checks locally - Submit a pull request
Follow Conventional Commits:
feat: add time-of-day-aware algorithm
fix: handle nil Prometheus response gracefully
docs: update quickstart guide
test: add fuzz tests for estimator chain
chore: update controller-runtime to v0.24.1
- Use structured logging (
logr) exclusively; neverfmt.Printforlog.Printf - Follow controller-runtime patterns for reconciliation
- Use
resource.Quantityfor all CPU/memory values; never parse strings manually - Add table-driven tests for new logic
- Use
meta.SetStatusCondition()for condition management
When a fix merged to main should also go into a stable release branch,
add a backport/<branch> label to the PR (e.g., backport/release-0.1).
A GitHub Actions workflow will automatically cherry-pick the merge commit
and open a backport PR targeting that release branch.
If the cherry-pick has conflicts, the backport PR is created with conflict markers for manual resolution.
The project uses a single-module layout with one go.mod at the repo
root. All packages (api/, cmd/, internal/, pkg/, test/) are part
of the same module.
Dependabot is configured for four ecosystems:
| Ecosystem | Directory | Interval | Notes |
|---|---|---|---|
gomod |
/ |
weekly | K8s deps grouped separately |
github-actions |
/ |
weekly | All actions grouped |
pip |
/docs |
monthly | MkDocs site dependencies |
docker |
/ |
weekly | Base image updates |
If you add a new go.mod (e.g., tools/go.mod for build tooling):
- Add a corresponding entry in
.github/dependabot.yml - Create a
go.workfile at the repo root:go 1.26 use ( . ./tools ) - Add
go.workandgo.work.sumto version control - Update
make verifyto rungo mod tidyin the new module directory
Currently there is no go.work file because the single-module layout does
not require one.
This project follows the CNCF Code of Conduct.