Thanks for taking the time to contribute. This file covers conventions and the day-to-day workflow. Detailed reference material lives in docs/:
- docs/architecture.md — runtime model, package layout, request lifecycle, async-operation behavior.
- docs/design.md — UX principles, exit codes, output contract (read this before adding a new command).
- docs/auth.md — token lifecycle, config precedence, storage backends.
- docs/api-client.md — HTTP client conventions.
- docs/output.md — TTY vs CI rendering rules.
- docs/testing.md — unit, printer, and live-test patterns.
- docs/releasing.md — release pipeline runbook.
For non-trivial changes (new commands, new flags, new external dependencies), open a GitHub issue first so we can agree on the approach before you spend time on a PR. Small fixes (typos, doc clarifications, contained bug fixes) are fine to send directly.
For security-impacting issues, please follow SECURITY.md instead of opening a public issue.
make build # build bin/kupe with dev ldflags
make test # unit tests
make lint # golangci-lint
make gosec # security static analysis
make govulncheck # Go vulnerability check
make snapshot # local goreleaser run (no publish)
make manpages # generate completions and man(1) pagesHit a live development environment by exporting:
export KUPE_API_TOKEN=kupe_…
export KUPE_API_URL=https://api.dev.int.kupe.cloud
export KUPE_TENANT=kupe-testFor OIDC end-to-end testing, see docs/testing.md.
- Conventional commits. PR titles should match
feat|fix|refactor|docs|test|chore: ….feat:triggers a minor release;fix:triggers a patch. - Single-line subjects. Keep the title under ~70 chars and put detail in the body. The body should explain why, not what (the diff shows what).
- Tests. New code paths need test coverage. We prefer table-driven tests
using
cli.Test()IOStreams for command-level testing. Live tests (test/live/) are gated behind thelivebuild tag and run viamake test-liveagainst a real kupe-api. - No vendored deps without a reason. Run
go mod tidy && go mod vendorif you've changed imports; the diff invendor/modules.txtshould be clean. - Pre-commit hooks run
golangci-lint,gofmt,gosec,govulncheck, andmarkdownlint. Set uppre-committo catch issues locally before CI does.
- Noun-verb command order.
kupe cluster create, notkupe create cluster. Matchesgh,fly,hcloud. - Positional arg for the resource name.
kupe cluster get NAME, never--name. - Exit codes.
0success,1general,2misuse,3auth,4not- found,5conflict,6rate-limited,7platform refused (paused or unavailable). Defined in internal/cli/exit.go and surveyed in docs/design.md. - Structured stderr errors.
Error: message\n (request-id: abc123)\n— request ID surfaced for support tracing. - Data → stdout, status/progress/prompts → stderr.
-o jsonalways produces parseable stdout. - No telemetry. Period.
- Don't write to
os.Stdout/os.Stderrdirectly. Always go throughfactory.IOStreamsso commands stay unit-testable. - Don't print spinners or progress without checking
iostreams.SpinnersEnabled. They must auto-disable on non-TTY, in CI, with-q/-o json/NO_COLOR. - Don't hand-roll YAML surgery on kubeconfigs. Use
clientcmd.Mergeviainternal/kubeconfig.Merge. - Don't skip error wrapping.
fmt.Errorf("…: %w", err)always. - Don't leak tokens to logs, error messages, or
-v/--verboseoutput. The HTTP middleware stripsAuthorizationheaders before logging — any new tracing must do the same. - Don't import Viper. We use a hand-written YAML loader and the
precedence helper in
internal/config. - Don't add a command without updating docs/commands.md.
See docs/releasing.md. TL;DR: main is always
shippable; semantic-release tags releases on green CI, goreleaser attaches
artifacts.
By participating you agree to follow the principles in https://www.contributor-covenant.org/version/2/1/code_of_conduct/. Be kind, assume good faith, focus on the work.