Thanks for your interest in contributing to the ClickHouse Terraform provider.
-
Install Go >= 1.26.
-
Fork and clone the repository.
-
Enable the git hooks:
make enable_git_hooks
This symlinks the committed
.githooks/hooks (pre-commitandcommit-msg) into.git/hooks/. Most tooling is fetched on demand by the Make targets:golangci-lintand the patchedtfplugindocsare downloaded automatically the first time you runmake fmt/make docs, andadr-tool/go-test-coveragearego tooldependencies pinned ingo.mod. Onlygoreleasermust be installed separately if you want to runmake goreleaser-checklocally.
All commands are exposed through the Makefile so that local runs match CI:
| Command | Purpose |
|---|---|
make build |
Build the provider binary. |
make fmt |
Format Go source (gofumpt + goimports via golangci-lint). |
make lint |
Run go vet and golangci-lint. |
make sec |
Run security analysis (gosec) on its own. |
make test |
Run unit tests. |
make testacc |
Run acceptance tests (creates real resources). |
make cover |
Run tests and enforce coverage thresholds (.testcoverage.yml). |
make docs |
Regenerate registry documentation with tfplugindocs. |
make docs-check |
Fail if generated docs are out of date. |
make goreleaser-check |
Validate the release config and run a snapshot build. |
make adr |
Create a new ADR (title="..." statement="..."). |
make mock |
Regenerate the Cloud API client mock. |
make enable_git_hooks symlinks the committed .githooks/ hooks
into .git/hooks/. Bypass either in an emergency with git commit --no-verify.
The pre-commit hook runs make fmt docs build and
aborts the commit if any step fails. The remaining checks (lint, sec,
coverage, docs staleness, goreleaser config) run as separate CI jobs on every
pull request.
The commit-msg hook enforces
Conventional Commits via
scripts/check-conventional-commit.sh:
it rejects the commit if the subject line does not match
<type>[optional scope][!]: <description>. Merge, revert, and fixup!/squash!
subjects are allowed through unchanged.
This project uses Conventional Commits. Each commit subject must take the form:
<type>[optional scope][!]: <description>
- type — one of
feat,fix,docs,style,refactor,perf,test,build,ci,chore,revert. - scope (optional) — the area of the codebase; prefer the service group
where applicable, e.g.
feat(clickstack): ...,fix(postgres): .... !(optional) — marks a breaking change, e.g.fix!: ....
Examples:
feat(clickstack): add connection resource
fix!: drop support for Terraform < 1.0
docs: explain the ADR workflow
The repository squash-merges pull requests, so the PR title becomes the
commit subject on main and is validated by the
Validate PR title CI check. The
commit-msg hook enforces the same format locally for every commit. Conventional
Commits keep history machine-readable, which makes changelog generation and
semantic versioning straightforward.
Significant architectural choices are recorded as Architecture Decision Records
under decisions/ — separate from docs/, which is
reserved for the generated public Terraform Registry documentation. Raise an ADR
whenever you make a decision that is hard to reverse or that future contributors
would otherwise have to reverse-engineer (package layout, dependencies, release
mechanics, public behavior, and so on).
Records are managed with adr-tool, a Go
CLI pinned as a go tool dependency in go.mod (no separate install,
no Node.js). It is purely directory-based: numbers are zero-padded and assigned
automatically from the highest existing record, and templates are built in. There
is no generated index — we rely on the decisions/ listing and each
record's header.
-
Create a record:
make adr title="Short decision title" statement="The decision and its context"
This writes
decisions/000N-short-decision-title.mdfrom the built-in template. (Equivalent:go tool adr-tool short-adr -p ./decisions -t "..." -s "...".) -
Fill it in. Complete the considered options and decision outcome, and set the
Status(Proposed,Accepted,Rejected,Deprecated, orSuperseded). The title is the file's single top-level heading. -
Commit the record. Prefer raising the ADR in the same pull request that implements the decision, so the rationale is reviewed alongside the change.
For richer records or lifecycle changes, use the tool directly:
go tool adr-tool long-adr -p ./decisions -t "Title" -d "Deciders" -s "Statement"
go tool adr-tool change-status -p ./decisions -a 0001-some-adr.md -s accepted
go tool adr-tool supersede --helpTip
adr-tool's --options/-o flag splits values on commas, so avoid commas
inside a single option (use a dash or semicolon instead).
- Implement it in a service group under
internal/service/<group>/(clickhouse,postgres,clickstack, …). - Register it in the group's
ServicePackage(Resources()/DataSources()ininternal/service/<group>/<group>.go) — never inprovider.go, which composes its resources from theregistry. - Add an example under
examples/and runmake docs. - Add acceptance tests and run
make testacc. - If it is not GA yet, mark it beta: call
utils.BetaWarning("<name>", &resp.Diagnostics)so users see it at plan time, and open its description markdown with a~> **Note:** This resource is in beta.callout. When a whole service group is beta (as ClickStack is), one callout ininternal/provider/README.mdcovers the group instead of repeating it per resource. Beta resources ship in the normal build — there is no separate binary.
See GO_CONVENTIONS.md for package layout and naming, and
decisions/0002-adopt-service-group-layout.md
for why the provider is organised into service groups.
Go code in this repository follows the conventions documented in
GO_CONVENTIONS.md — package layout (internal/ over
pkg/, service groups), Must* vs error-returning naming, error handling, and
the rules that golangci-lint enforces automatically.
Note: the release process can only be run by ClickHouse employees.
Releases are produced by GoReleaser and published by the
Release workflow. The provider version embedded
in the binary is injected at build time via -X internal/project.version, and the
published checksums are signed with GPG.
There is one GoReleaser config, .goreleaser.yml. Every
release is built the same way — beta resources ship in the normal build, so
there is no separate binary to install.
The tag decides how GitHub serves the release: vX.Y.Z is a normal release,
and vX.Y.Z-<suffix> (e.g. v1.2.3-beta1) is flagged a pre-release by
prerelease: auto, so it is never served as "latest" and must be requested by
exact version. Pre-releases cut before this was renamed use -alphaN tags.
Validate the config locally before releasing with make goreleaser-check.
- Keep changes focused and include tests where practical.
- Ensure
make lint test docs-checkpasses before opening a PR. - Use a Conventional Commit PR title (it becomes the squash commit subject).
- Describe user-facing changes clearly in the PR description.
Be respectful and constructive. Report unacceptable behavior to the maintainers.