# Contributing

This page is for anyone changing CAPTF itself: the repository layout, the prerequisites, the build/lint/test/verify loop, the conventions the checks enforce, and running the manager against a real cluster.

## Repository layout

| Path | Holds |
| --- | --- |
| `api/` | The `v1alpha1` Go types: its own module in `go.work`, so the CRD types carry no dependency on controller-runtime or the manager. |
| `cmd/manager`, `cmd/runner`, `cmd/tfcapi-lint` | The three binaries. Each has an `app` package with its wiring and an `app/options` or equivalent for flags, so `main.go` stays a thin entry point. |
| `internal/` | Everything the binaries share, one package per concern: `controllers` (one subpackage per reconciled kind, plus `shared` for the common reconcile flow and `sweep` for the orphan RBAC sweep), `jobs`, `runner`, `state`, `identity`, `rbac`, `runlease`, `inputs`, `render`, `outputs`, `locks`, `conditions`, `contract`, `hash`, `ownership`, `plankey`, `webhooks`, `lint`, `feature`, `imageinspect`, `manager`, `metrics`, `strutil`. |
| `config/` | Kustomize bases: `crd`, `rbac`, `webhook`, `manager`, `certmanager`, assembled by `default`; `network-policy` and `prometheus` are separate optional overlays, and `samples` holds example custom resources. |
| `templates/` | The `clusterctl generate` templates and flavors, and the Go tests that decode them. |
| `hack/` | Build and verify tooling: pinned tool installers under `hack/tools`, the `verify-*.sh`/`check-*.sh` scripts `make verify` runs, `hack/godoccheck`, `hack/covercheck` with its per-package floors in `hack/coverage-floors.txt`, and the dev scripts behind `make run`. |
| `test/` | A third module in `go.work` for the e2e-tagged test environment and suites: `framework/` (kind, images, providers and diagnostics, unit-tested with fakes), `env/lifecycle/` (the `testenv-*` operations) and `e2e/` (the foundation and no-op suites). See [Testing](<https://captf.io/docs/developer-guide/testing/index.md>). |
| `internal/*/testdata` | Golden files and frozen fixtures that unit tests read, such as real captured Terraform and OpenTofu state under `internal/state/testdata/fixtures`. |

The no-op demo modules are not in this repository: they live in [captf-io/noop-modules](<https://github.com/captf-io/noop-modules>).

The documentation lives in a separate repository, [captf-io/captf-io.github.io](<https://github.com/captf-io/captf-io.github.io>), with the rest of captf.io, published at [https://captf.io/docs/](<https://captf.io/docs/>); see [Writing Documentation](<https://captf.io/docs/developer-guide/documentation/index.md>).

Each Go module (`.`, `api` and `test`) is listed in `go.work`. `hack/verify-modules.sh` (`make verify`) checks that none carries a `replace` directive and that they agree on the Kubernetes and controller-runtime versions they share. Because `api` only exists inside the workspace, `go mod tidy` run from the repository root does not update its `go.mod`; add or bump one of its dependencies by editing `api/go.mod`’s `require` block directly, then run `go mod tidy` inside `api/` with `GOWORK=off`.

> [!NOTE]
>
> **Before you begin**
>
> - Go, matching the version `go.work` declares, `make`, `git`, `jq` and `curl`.
> - `podman` (the default `CONTAINER_TOOL`) or `docker`, to build images.
> - `python3`, with the `jsonschema` and `PyYAML` packages, for `make verify-schemas`, `make verify-metadata`, `make promtool-check`, `make promtool-test` and `make release-assets`.
> - `podman` or `docker`, running, for the [test environment](<https://captf.io/docs/developer-guide/testing/#end-to-end-tests>) (`make testenv-up` and the `e2e-*` targets).

Everything else — `controller-gen`, `kustomize`, `golangci-lint` (plus its kube-api-linter build), `clusterctl`, `promtool`, `goreleaser`, `goimports` and `gotestsum` — is a pinned binary `make` downloads for you.

## The build, lint, test and verify loop

1. Install the pinned tools once: `make tools`. Each lands in `hack/tools/bin` as `<name>-<version>`, plus an unversioned symlink; bumping a version in the `Makefile` re-downloads it.
2. After changing `api/v1alpha1` or a controller’s markers, regenerate the deepcopy code and the CRD/RBAC/webhook manifests: `make generate    manifests`. `make verify-gen` (part of `make verify`) fails if either is stale.
3. Format and lint: `make fmt lint`. `fmt` runs `gofmt -s` and `goimports    -local github.com/captf-io/cluster-api-provider-terraform`; `lint` runs `golangci-lint` (`.golangci.yml`) in every module, then kube-api-linter (`.golangci-kal.yml`) on `api/`. `lint` and `vet` also check the `test` module’s e2e-tagged code. `make lint-fix` reruns both linters with their auto-fixers.
4. Run the unit tests: `make test`. See [Testing](<https://captf.io/docs/developer-guide/testing/index.md>) for what it covers and how to run less than everything. CI runs `make test-cover` and `make cover-check` instead, which also enforce a coverage floor per package.
5. Before sending a change, run `make verify`: every check listed in [Make Targets](<https://captf.io/docs/reference/make-targets/#verify>), including that generated code is current and that e2e code stays out of the default test run. A change to anything the documentation’s reference pages describe (a flag, a condition, an event, a metric, an annotation, a field of a kind) also needs that page updated in `captf-io/captf-io.github.io`: see [Reference pages](<https://captf.io/docs/developer-guide/documentation/#reference-pages>).
6. `make build` compiles every `cmd/*` binary to `bin/`; never to the repository root. To see a change work, run the manager against a cluster: see [Running the manager](<#running-the-manager>).

The full target list, grouped the same way, is in [Make Targets](<https://captf.io/docs/reference/make-targets/index.md>).

## Conventions

- **Commits**: an imperative subject in `<subsystem>: <summary>` form, such as `runner: name the exit codes` — check `git log` for the subsystem names already in use.
- **License header**: every hand-written Go file starts with the Apache 2.0 header the other files in its package carry; `hack/boilerplate.go.txt` is the header `controller-gen` writes on `api/v1alpha1/zz_generated.deepcopy.go`.
- **Documentation comments**: `hack/godoccheck` (`make verify-godoc`, part of `make verify`) enforces one rule per declaration and one per package, everywhere except generated files and `hack/tools`:

  - Every function, method and type has a doc comment starting with its name; every interface method and top-level `const` or `var` group needs only a doc comment, not one starting with its name.
  - Every named parameter is mentioned by name in that comment. Receivers, parameters named `_`, and a `Test`/`Benchmark`/`Fuzz` function’s conventional `*testing.T`/`*testing.B`/`*testing.F` parameter are exempt.
  - A function or method that returns anything says what it returns, using “return”, “returns”, “returned” or “reports”.
  - Every non-generated, non-external-test package has a `doc.go` whose package comment is a real overview of at least 400 characters.
- **Generated code**: `zz_generated.deepcopy.go` and the CRD/RBAC/webhook manifests come from `make generate manifests`. Never hand-edit a generated file: `make verify-gen` fails when the deepcopy code or the manifests no longer match their source. The documentation’s reference pages are written by hand (see [Reference pages](<https://captf.io/docs/developer-guide/documentation/#reference-pages>)).
- **Test tiers**: code that needs a real cluster carries the `e2e` build tag and lives only in `test/e2e/` or `test/env/lifecycle/`; `make verify-test-tiers` fails otherwise. See [Testing](<https://captf.io/docs/developer-guide/testing/index.md>).
- **Coverage**: a package’s coverage must not fall below its floor in `hack/coverage-floors.txt`. When a package’s coverage rises, raise its floor in the same change; never lower one without a reason on its line.

## Common changes

Each change below also needs its reference page in `captf-io/captf-io.github.io` updated by hand, in the same change; run `tools/check_reference.py` there, with `--provider` pointed at this checkout, to list any name a page is missing (see [Reference pages](<https://captf.io/docs/developer-guide/documentation/#reference-pages>)).

- **Adding a manager flag**: register it in `cmd/manager/app/options/options.go`, then document it in [Manager Flags](<https://captf.io/docs/reference/manager-flags/index.md>).
- **Adding a condition reason**: condition types, their reasons and each reason’s doc comment live in `api/v1alpha1/conditions_consts.go`; `ConditionReasons` returns the full type-to-reason table, and every reason constant must be in it. A reason is set from `internal/conditions` when it applies across controllers, or from `internal/controllers/shared` when it belongs to one reconcile flow. Afterward, run `make generate manifests` to regenerate the CRD schema and the deepcopy code, and document the reason in [Conditions](<https://captf.io/docs/reference/conditions/index.md>). `make verify-gen` and `make test` fail until the generated files and the reason table are complete.

  An event reason follows the same shape: it lives in `internal/controllers/shared/events.go`, or `internal/runner/events.go` for a runner event, with a doc comment. Document it in [Events](<https://captf.io/docs/reference/events/index.md>). A `tfcapi-lint` check ID in `internal/lint` needs a doc comment too, and an entry in [tfcapi-lint CLI](<https://captf.io/docs/reference/tfcapi-lint-cli/#checks>).

## Running the manager

There are two ways to run the manager you are changing. Both need a cluster that has the CAPTF CRDs, cert-manager and Cluster API.

- **The test environment** is the closest to a real install. `make   testenv-up` builds the manager image from your tree, creates a kind cluster and installs cert-manager, Cluster API and CAPTF. After a change, `make testenv-reload` rebuilds the image and rolls the manager Deployment over to it. `make testenv-logs` collects diagnostics and `make testenv-down` deletes the cluster. The same image is the runner image, so a change under `internal/runner` or `cmd/runner` reaches Jobs on the next reload.
- **`make run`** builds `bin/manager` and runs it on your machine against the cluster in your current `kubeconfig`, with leader election off and a self-signed webhook certificate in `bin/dev-webhook-certs`. It is quick, but the Jobs it creates run the image in `RUNNER_IMAGE` (default `IMG`), not your working tree: run `make docker-build` and make that image available to the cluster first when you change the runner. Pass extra manager flags with `ARGS`.

> [!WARNING]
>
> **Point `make run` at a cluster you can break**
>
> `make run` uses whatever cluster your current `kubeconfig` context names. The test environment never reads `~/.kube/config`; it writes its own `bin/testenv/<name>/kubeconfig` and an `env.sh` that exports it.

> [!NOTE]
>
> **See also**
>
> - [Testing](<https://captf.io/docs/developer-guide/testing/index.md>)
> - [Releasing](<https://captf.io/docs/developer-guide/releasing/index.md>)
> - [Writing Documentation](<https://captf.io/docs/developer-guide/documentation/index.md>), [Contributing to the Docs](<https://captf.io/docs/developer-guide/docs-workflow/index.md>) and [Updating the Website](<https://captf.io/docs/developer-guide/website/index.md>) for changes to captf.io itself.
