Skip to content

Coding standards

Binding standards for Go code, tests, security, commits, architecture, and merge gates in Kollect. Operator-specific engineering principles (error taxonomy, robustness, definition of done) live in engineering guidelines. This page is the contributor-facing checklist enforced by lint, CI, and review.

Before you open a PR

task lint · task coverage · task coverage:race (recommended) · task verify · task scrub — see CONTRIBUTING.md and Testing strategy.

License headers

Source files should include SPDX and copyright headers:

// SPDX-License-Identifier: MIT
// Copyright (c) 2026 Konrad Heimel

TypeScript/CSS under ui/src/ use the same license line with // or /* */ comment style. CI enforces UI headers via task verify:headers (also run as part of task ui-ci).

Exception: Kubebuilder-generated api/*/zz_generated.deepcopy.go files are produced by controller-gen and may omit copyright headers. Do not hand-edit them; regenerate with make generate and task verify. If upstream tooling adds header support later, adopt it in the generator config rather than patching generated output.

Go conventions

Short, actionable rules for Go code in this repo. Operator reconcile semantics and error taxonomy: guidelines § 1.

Error handling

  • MUST wrap errors with context: fmt.Errorf("export to %s: %w", sink, err).
  • MUST use %w (not %v) so callers can errors.Is / errors.As — required for ErrTransient / ErrTerminal / ErrForbidden classification.
  • MUST NOT discard errors from fallible calls (errcheck enforces this).
  • MUST NOT use github.com/pkg/errors — blocked by gomodguard.

Formatting and style

  • MUST format Go with gofmt / goimportstask format:check fails CI on drift (gofmt + goimports via golangci-lint fmt --diff).
  • SHOULD follow the Google Go Style Guide for naming, simplicity, and readability; the Uber Go Style Guide is a useful secondary reference.
  • MUST run golangci-lint v2 locally before every PR (task lint); CI fails on lint errors.

Modules and dependencies

  • MUST keep module path github.com/konih/kollect (v0/v1); bump to …/v2 only on a tagged major release per Go module path rules.
  • MUST commit go.sum; run go mod tidy — preflight CI checks for drift.
  • SHOULD NOT vendor — no vendor/ directory; rely on the module proxy and checked-in go.sum.
  • MUST respect depguard / gomodguard policy (see Dependency policy below).

Tests

  • SHOULD run the race detector locally before PRs (task coverage:race or COVERAGE_RACE=1 CGO_ENABLED=1 bash hack/coverage.sh). CI task coverage runs without -race (CGO disabled in release/CI paths).
  • SHOULD prefer Ginkgo/Gomega matchers in _test.go files over testify/assert (depguard on tests).
  • Pyramid tiers, coverage floors, and sink integration gates: Testing strategy.

Security and supply chain

  • MUST run govulnchecktask vulncheck in CI on every PR (remediation thresholds: SCA remediation policy).
  • MUST pass gitleaks and task scrub before commit (see Security below).

Container builds

  • MUST build the operator manager with CGO_ENABLED=0 for the shipped image (Dockerfile); enable CGO locally only for task coverage:race. The runtime stage is Debian bookworm-slim (nonroot UID 65532) with git and openssh-client for spec.git.engine: cli and git ls-remote probes; go-git (default) does not use those binaries.

Go style and lint

Tool Config / command Purpose
golangci-lint v2 .golangci.yaml · task lint Static analysis, formatters, dependency policy
go-arch-lint .go-arch-lint.yml · task arch-lint Package import boundaries
gofmt / goimports task format:check Formatting drift gate (gofmt + goimports)

Run task lint locally before every PR. It includes golangci-lint and go-arch-lint check. CI workflow CI (.github/workflows/ci.yaml) runs lint and format checks; preflight runs codegen/changelog/module drift only.

Key linters enabled in .golangci.yaml include errcheck, govet, staticcheck, gosec, depguard, gomodguard, and logcheck (custom plugin via hack/tooling/.custom-gcl.yml). Maintainer setup and arch-lint baseline workflow: tooling-setup.md.

Logging

Use structured logging via log/slog or controller-runtime/log (logr). Do not introduce github.com/sirupsen/logrus — blocked by depguard and gomodguard.

The logcheck linter enforces Kubernetes logging conventions: stable message strings with variable data in key/value pairs; never log secrets, tokens, or full payloads. Operator logging rules: guidelines § 1.

Dependency policy

depguard denies deprecated stdlib shims (io/ioutil), non-standard error/logging packages, and legacy protobuf imports. gomodguard blocks logrus and pkg/errors in go.mod. When adding a legitimate dependency, extend gomodguard.blocked / depguard rules in .golangci.yaml and keep task lint green. Details: tooling-setup.md.

Tests and matchers

In _test.go files, prefer Ginkgo/Gomega matchers over testify/assert (depguard on tests).

Testing

Follow the six-tier pyramid (L0–L5) in Testing strategy and ADR-0706.

Requirement Detail
Coverage floor 65% statement coverage on ./internal/... today (task coverage, COVERAGE_MIN)
Pre-v0.10 target 80% — ratchet the floor only when coverage is sustained
Behavior tests Table-driven unit tests; envtest for controllers/webhooks; golden contracts for extractors
New sink backends Must reach L3 integration (task test-integration) before merge
Codegen drift task verify must pass (manifests, deepcopy, RBAC, mocks committed)

Integration-tagged tests (-tags=integration) and e2e packages are excluded from the default coverage profile.

Security

Control Where
CodeQL .github/workflows/codeql.yaml — Go analysis on main and PRs
Secret scan gitleaks in CI; task scrub + gitleaks protect --staged before commit
Vulnerability scan task vulncheck (govulncheck) in CI
SCA policy SCA remediation policy — CVE/license thresholds
Threat model SECURITY.md
Security ADR ADR-0104 — TLS, RBAC, redaction, secrets

Secrets and scrubbing

  • Credentials only via secretRef; never in CR spec/status, logs, or committed files.
  • Run task scrub on staged diffs before commit to catch private strings and old project identities.
  • Profile redaction uses scrubKeys before items enter the store (ADR-0303).

Git sink validation

Git and GitLab sink paths, refs, and config values are validated at admission and export time to block path traversal, shell injection, and unsafe refs:

  • ValidateGitRef — safe ref charset; rejects leading - and .. segments
  • validateObjectPath / workdir containment — object paths cannot escape the workdir
  • ValidateGitSinkWarnings — admission warnings for risky git sink settings (webhook)

Implementation: internal/sink/git/validate.go, internal/validation/git.go. Extend these validators when adding git-related fields; do not bypass with #nosec without an ADR note.

Transport and hub ingest

Sink and doc endpoints must use verified TLS with org CA support (caBundle / caSecretRef). Hub HTTP ingest listens in plain HTTP inside the pod — terminate TLS at the ingress or service mesh before traffic reaches the operator (ADR-0503).

Commits

CONTRIBUTING.md § Commit messages defines the format:

  • Conventional Commits types and scopes
  • Optional gitmoji shortcode prefix (:sparkles:, not Unicode emoji)
  • Breaking changes only when a tagged release exists and adopters must migrate

Changelog entries are generated with git-cliff (hack/release/cliff.toml). Only feat, fix, perf, refactor, and breaking commits appear in the user-facing changelog.

Architecture

Package layout and dependency flow are documented in ARCHITECTURE.md § Package boundaries.

Import rules are enforced by .go-arch-lint.yml (task arch-lint). When introducing a new internal/ package or cross-component import:

  1. Update .go-arch-lint.yml to describe the intended graph.
  2. Run task arch-lint and legalize existing violations incrementally (see tooling-setup.md).
  3. Record non-trivial decisions in docs/adr/.

Non-trivial API, tenancy, sink, or multi-cluster changes require an ADR before merge — see ADR index.

Pull request and CI gates

main is protected: linear history, required checks preflight, test, and kind-smoke (E2E Tier 0), no force-push. Add kind-smoke in GitHub branch protection after the workflow has run green on main at least once. Use Rebase and merge on PRs (CONTRIBUTING.md § Changelog and releases).

Gate Workflow / task Blocks merge?
Preflight .github/workflows/preflight.yaml Yes
E2E smoke .github/workflows/e2e-smoke.yaml → job kind-smoke Yes
go mod tidy drift preflight job Yes
go mod verify preflight job Yes
Codegen drift task verify Yes
Stale changelog task changelog:verify Yes
CI .github/workflows/ci.yaml Yes
Lint + arch fitness task lint (ci.yaml lint job) Yes
Format (gofmt + goimports) task format:check (ci.yaml lint job) Yes
Coverage floor task coverage (ci.yaml test job; no -race) Yes
Integration (L3) task test-integration Yes
Secret scan gitleaks Yes
Helm / image smoke task helm-test, task docker:build Yes

Optional jobs (e2e-extended Tier 1, e2e-nightly, perf-report, SonarCloud) do not block merge unless noted in ADR-0706.