Contributing to Attune
September 11, 2026 ยท View on GitHub
Thank you for your interest in contributing! This document provides guidelines and instructions for contributing.
Development Setup
Prerequisites
| 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.31+ | 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.
Local Development
# 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
# After CRD/API or RBAC changes, refresh release manifests (dist/install.yaml
# and dist/crds.yaml). PR CI's "CRD Freshness Check" job runs
# `make verify-release-artifacts` and fails if these lag:
make build-installer IMG=ghcr.io/attune-io/attune:latest
make build-crds
# dist/ is gitignored for local noise; force-add when committing:
# git add -f dist/install.yaml dist/crds.yaml
Running Tests
# Unit tests (1300+ test functions, 80% coverage threshold enforced, currently ~92%)
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-smoke
make 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.yaml
Building the Container Image
make docker-build IMG=attune:dev
Pre-commit Checklist
Run make verify before every commit. The Makefile verify and
verify-quick targets are the source of truth (not this list). Today
make verify-quick runs:
- golangci-lint, yaml-lint, Chainsaw lint, unit tests, Python tests
- Helm lint, helm-docs freshness, Helm unit tests
- boilerplate,
go mod tidy, documentation defaults - Helm RBAC vs generated ClusterRole, Grafana dashboard sync
- documentation tool versions, Go version sync (go.mod vs Dockerfile)
- PrometheusRule metric names, Helm schema fields, Helm default image
tag vs
appVersion, release artifacts (dist/install.yamlanddist/crds.yaml)
make verify then adds integration tests, govulncheck, and a generated
file freshness check (make manifests generate must not change CRDs,
Helm CRDs, deepcopy, or RBAC).
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 methods
make build-installer IMG=ghcr.io/attune-io/attune:latest
make build-crds
git add -f dist/install.yaml dist/crds.yaml
make verify-quick fails if dist/ lags the CRD. Commit the generated
output.
Documentation Site
The docs/ directory is configured as an MkDocs
site with the Material theme.
Local preview
pip install mkdocs-material
mkdocs serve
Then open http://127.0.0.1:8000. Changes to markdown files reload
automatically.
Editing docs
- 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.
Helm CI parity notes
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 Runners
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-status
Developer Certificate of Origin (DCO)
All 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
Issue Triage Labels
New issues are automatically labeled based on the author's relationship to the repository:
| Label | Who gets it | Meaning |
|---|---|---|
ready | OWNER, MEMBER, COLLABORATOR | Accepted backlog, ready for implementation |
needs-triage | External contributors | Awaits maintainer review before implementation |
needs-info | (applied manually) | Blocked on the reporter for more information |
Maintainers accept an external issue by adding ready and removing
needs-triage. Issues labeled needs-triage or needs-info will not be
implemented until a maintainer promotes them.
Unlabeled legacy issues (opened before this workflow existed) are treated as accepted.
Pull Request Process
- 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
Commit Messages
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
Code Style
- 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
Backporting Fixes to Release Branches
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.
Go Module Layout and Dependabot
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 | One docs-minor-patch PR; majors stay solo |
docker | / | weekly | Base image updates (golang:X.Y.Z must match go.mod) |
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.
The Dockerfile golang:X.Y.Z tag and the go.mod go X.Y.Z directive
must stay on the same patch. make verify-go-version-sync (and Lint CI)
enforces this. Dependabot docker PRs only bump the image; the auto-merge
workflow copies the new tag into go.mod so those PRs can pass
govulncheck without a manual follow-up.
Code of Conduct
This project follows the CNCF Code of Conduct.