Contributing¶
Development Setup¶
Prerequisites¶
- Go 1.25+
- Docker
- kubectl
- Kind (for local testing)
- golangci-lint
Clone and Build¶
Run Tests¶
# Unit tests (uses envtest for K8s API)
make test
# Lint
make lint
# E2E tests (requires Kind cluster)
make test-e2e
Run Locally¶
# Install CRDs into your current cluster
make install
# Run the operator locally (outside the cluster)
make run
Build the kubectl Plugin¶
Project Structure¶
cmd/main.go # Operator entrypoint
cmd/kubectl-hybernate/main.go # kubectl plugin
api/v1alpha1/ # CRD type definitions
internal/controller/ # Reconcilers
internal/forecast/ # Holt-Winters engine
internal/policy/ # Idle and scale policy
internal/signal/ # Signal interface and implementations
internal/lifecycle/ # Pause, resume, scale, destroy
internal/discovery/ # Workload scanning and classification
internal/cost/ # Cost accumulation
internal/metrics/ # Prometheus metrics
internal/export/ # YAML generation
config/ # CRD, RBAC, deployment manifests
Code Standards¶
Key coding standards:
- No comments that restate what the code does. Comments are for why, not what.
- Wrap errors with context:
fmt.Errorf("doing X: %w", err) - Return early on errors. Happy path flows straight down.
- Accept interfaces, return structs. Define interfaces at the point of consumption.
- Test behavior, not implementation. Table-driven tests with
testify.
Commit Conventions¶
Use Conventional Commits:
feat(idle): add idle detection with detector and webhook signal
fix(idle): reset idle timer on active signal
refactor(forecast): extract confidence scoring into separate type
chore(crd): regenerate CRD manifests
docs(guides): add Prometheus signals guide
test(policy): add table tests for scale constraints
One logical change per commit. Generated code gets its own commit.
Pull Requests¶
- Fork the repository
- Create a feature branch from
main - Make your changes with tests
- Run
make lint testand ensure everything passes - Open a PR with a clear description of what and why
PR Checklist¶
- [ ] Tests pass (
make test) - [ ] Lint passes (
make lint) - [ ] CRD manifests regenerated if types changed (
make manifests) - [ ] DeepCopy regenerated if types changed (
make generate) - [ ] Documentation updated if user-facing behavior changed
Regenerating Generated Code¶
After changing CRD types in api/v1alpha1/:
Commit generated code separately: chore(crd): regenerate CRD manifests