Thank you for your interest in contributing to git-hubby! This guide covers everything you need to get started as a contributor.
- Prerequisites
- Local Development Setup
- Development Workflow
- Project Layout
- Code Conventions
- Testing
- Submitting Changes
- CI/CD Workflows
- Go 1.25.5 or later (see mise.toml for the exact version)
- kubectl configured to access a Kubernetes cluster (v1.34+ recommended)
- Docker (or another OCI-compatible container tool) for image builds
- mise (optional but recommended — manages Go and Kubebuilder versions automatically)
-
Clone the repository:
git clone <repository-url> cd git-hubby
-
Initialize tooling with mise (optional):
mise install
-
Install Go dependencies:
go mod download
-
Create your local environment file:
make env
This copies
.env.tmplto.env(git-ignored). Edit.envto override defaults for your local environment — for example:LOG_LEVEL=DEBUG LOG_FORMAT=console WATCH_NAMESPACE="github-configuration" APP_CREDENTIALS_SECRET_NAMESPACE="github-controller"
See Configuration below for all available variables.
-
Install CRDs into your cluster:
make install
-
Run the operator locally:
make run
This runs the operator against your current kubectl context with webhooks disabled.
-
Run in a minimal setup:
make deploy
This deploys the operator without cert-manager or webhooks, and creates the GitHub App credentials secret directly from the provided arguments. After that you can apply your secret and organization resources and test your changes.
Run make help for the full list. The most important targets are:
| Command | Description |
|---|---|
make env |
Create .env from .env.tmpl (safe to re-run) |
make build |
Build the manager binary |
make run |
Run locally (webhooks disabled) |
make test |
Run unit tests with envtest |
make test-e2e |
Run end-to-end tests on a Kind cluster |
make lint |
Run golangci-lint |
make lint-fix |
Lint with auto-fix |
make generate |
Regenerate deepcopy and apply-configuration code |
make manifests |
Regenerate CRDs and RBAC from kubebuilder markers |
make codegen |
Run all code generation steps after an API change (manifests, generate, crd-docs, schemas) |
make install |
Install CRDs into the current cluster |
make deploy IMG=<image> |
Deploy the operator to the current cluster |
make undeploy |
Remove the operator from the current cluster |
make uninstall |
Remove CRDs from the current cluster |
Always regenerate all derived artifacts:
make codegenmake lint-fix # Auto-fix style issues
make test # Run unit testsAlways use the Kubebuilder CLI to create new resources — don't create files manually:
# New API / CRD
kubebuilder create api --group github --version v1alpha1 --kind <Kind>
# Validation webhook
kubebuilder create webhook --group github --version v1alpha1 --kind <Kind> --programmatic-validation
# Defaulting (mutating) webhook
kubebuilder create webhook --group github --version v1alpha1 --kind <Kind> --defaulting├── api/v1alpha1/ CRD type definitions and deepcopy (auto-generated)
├── cmd/main.go Application entry point and wiring
├── internal/
│ ├── controller/ Controller registration and setup
│ ├── reconciler/ Core reconciliation business logic
│ │ ├── orgrec/ Organization reconciler
│ │ ├── reporec/ Repository reconciler
│ │ ├── teamrec/ Team reconciler
│ │ ├── reconcilerfactory/ Factory for creating reconcilers
│ │ └── spreading/ Startup spreading mechanism
│ ├── ghclient/ GitHub client interface, implementation, and mock
│ ├── mapper/ K8s ↔ GitHub struct mapping and comparison
│ ├── ratelimit/ Rate limiter for the operator work queue
│ ├── webhook/v1alpha1/ Validation webhook logic
│ ├── conditions/ Status condition helpers
│ └── logging/ Log mapping utilities
├── config/ Kustomize manifests (mostly auto-generated)
└── test/ E2E and integration tests
| Path | Regenerated by |
|---|---|
api/v1alpha1/zz_generated.deepcopy.go |
make generate |
config/crd/bases/*.yaml |
make manifests |
config/rbac/role.yaml |
make manifests |
config/webhook/manifests.yaml |
make manifests |
docs/crds.md |
make crd-docs |
schemas/*.json |
make schemas |
PROJECT |
Kubebuilder CLI |
| Task | Location |
|---|---|
| CRD schemas & validation markers | api/v1alpha1/*_types.go |
| Application wiring & startup | cmd/main.go |
| Controller registration | internal/controller/*_controller.go |
| Reconciliation logic | internal/reconciler/{orgrec,reporec,teamrec}/ |
| GitHub API calls | internal/ghclient/ |
| K8s ↔ GitHub mapping | internal/mapper/ |
| Validation webhooks | internal/webhook/v1alpha1/ |
| Status conditions | internal/conditions/ |
- Follow standard Go conventions and the project's golangci-lint configuration.
- Run
make lint-fixbefore committing.
Follow Kubernetes logging message style guidelines:
- Start with a capital letter, no trailing period.
- Use past tense:
"Deleted Pod", not"Deleting Pod". - Specify the object type:
"Created Deployment", not"Created". - Use structured key-value pairs:
log.Info("Created Deployment", "name", deploy.Name)
log.Error(err, "Failed to create Pod", "name", name)- All packages under
cmd/,api/, andinternal/must use Ginkgo v2 + Gomega (BDD style). - Each package needs a
suite_test.gowith the Ginkgo bootstrap. - Behaviour tests go in separate
*_test.gofiles usingDescribe,Context,It,DescribeTable/Entry,Expect(), etc. - Do not use plain
testing.Tassertions in operator packages. Thetestingpackage is only acceptable for standalone tooling underhack/andtest/. - Use the mock GitHub client in
internal/ghclient/mock.gofor unit tests.
- CRD validation:
+kubebuilder:validation:*on struct fields in*_types.go. - RBAC:
+kubebuilder:rbac:groups=...,resources=...,verbs=...on controllerReconcile()methods. - Webhooks:
+kubebuilder:webhook:path=...,mutating=false,...on webhook structs. - Never remove
// +kubebuilder:scaffold:*comments — the CLI injects code at these markers.
- Each reconciler lives in a dedicated package (
orgrec,reporec,teamrec) with logic split by concern into separate files (e.g.,rec_org.go,rec_rulesets.go). - Reconcilers define sequential groups of parallel tasks via
RequiredReconciliations(). - Labels are applied in reconcilers via
addLabels()— not in webhooks. - Webhooks are validation-only; no mutating webhooks exist.
make testUses envtest (standalone API server + etcd). Coverage for internal/controller/ will show 0% by design — integration tests run reconciliation in separate goroutines outside Go's coverage instrumentation scope.
make test-e2eCreates an isolated Kind cluster, runs tests, then tears it down. Never run e2e tests against a production cluster.
Install the Ginkgo plugin for your IDE for enhanced test debugging and navigation.
The operator reads configuration from environment variables, CLI flags, and .env files.
| Variable | Description | Default |
|---|---|---|
LOG_LEVEL |
Log level (debug, info, warn, error; case-insensitive) |
info |
LOG_FORMAT |
Log format: json (default), ecs (Elastic Common Schema), console (human-readable) |
json |
WATCH_NAMESPACE |
Comma-separated namespace(s) to watch (required) | — |
APP_CREDENTIALS_SECRET_NAMESPACE |
Namespace containing the GitHub App credentials secret (required) | — |
ENABLE_WEBHOOKS |
Set to false to disable webhooks (for local development) |
true |
ENABLE_STARTUP_SPREADING |
Enable startup spreading to prevent API rate limit exhaustion | true |
STARTUP_SPREAD_PERIOD_MINUTES |
Grace period after startup during which reconciliations may be delayed | 5 |
SPREAD_INTERVAL_MINUTES |
Time window across which warm-start reconciliations are distributed | 180 |
The .env file is loaded automatically on startup and is git-ignored. Create it from the template:
make envEdit .env freely — it won't be committed. The template (.env.tmpl) contains sensible defaults for local development.
- Fork the repository and create a feature branch from
main. - Make your changes, following the conventions above.
- Run the full validation suite:
make codegen # if you changed types or markers make lint-fix make test
- Write or update tests for your changes.
- Commit using Conventional Commits format. This is enforced by CI on pull requests.
- Open a Pull Request against
mainwith a description of what changed and why.
The Codegen Check workflow verifies that generated code is up to date on every PR. It runs all generation steps, then fails if there are uncommitted changes. Always run this before pushing:
make codegenThe Update Helm Chart workflow manages CRD updates in Interhyp/git-hubby-helm:
- Automatic (after release): After a successful "Build & Release" workflow, CRDs are copied to the helm chart repo with an updated
appVersion, and a draft PR is created (main only) labeledautomatic-update. - Manual (any branch): You can trigger the workflow manually via
workflow_dispatchto test CRD updates from your feature branch. The result is pushed to asnapshot/<branch>branch ingit-hubby-helm(no PR is created).
To manually trigger from your branch:
gh workflow run "Update Helm Chart" --ref <your-branch-name>Note: Only CRDs are updated automatically. The Helm chart's other templates (deployment, RBAC, webhooks) are maintained manually. The CI workflow will comment on your PR if it detects changes that require a matching Helm chart update:
+kubebuilder:rbacmarkers → RBAC template+kubebuilder:webhookmarkers → webhook configuration templateconfig/manager/manager.yaml→ deployment template (env vars, args, ports, volumes)
This project uses Conventional Commits for automated semantic versioning and changelog generation.
<type>(<scope>): <subject>
[optional body]
[optional footer(s)]
Types (determines version bump):
| Type | Description | Version bump |
|---|---|---|
feat |
A new feature | Minor |
fix |
A bug fix | Patch |
docs |
Documentation only | None |
style |
Code style (formatting, no logic change) | None |
refactor |
Code change (no new feature, no fix) | None |
perf |
Performance improvement | Patch |
test |
Adding or updating tests | None |
build |
Build system or dependency changes | None |
ci |
CI configuration changes | None |
chore |
Maintenance tasks | None |
revert |
Reverts a previous commit | Patch |
Breaking changes (major version bump): Add BREAKING CHANGE: in the footer or ! after the type:
feat!: remove deprecated API endpoints
BREAKING CHANGE: The /v1/legacy endpoint has been removed.
Examples:
feat(org): add support for organization-level custom roles
fix(repo): handle 404 when repository is deleted externally
docs: update CONTRIBUTING.md with commit format guide
chore(deps): bump go-github to v87
By contributing to git-hubby, you agree that your contributions will be licensed under the Apache License 2.0.