OpenFrame Documentation
Deployment guides, the CLI reference, architecture and admin guides for OpenFrame
OpenFrame is on your phone now! · iOS and Android apps are live. Get alerts and handle tickets on the go!
Deployment guides, the CLI reference, architecture and admin guides for OpenFrame
Thank you for your interest in contributing to OpenFrame CLI — the Go command-line tool that provisions Kubernetes clusters (local k3d, or cloud GKE/EKS via Terraform) and deploys the OpenFrame platform onto them through ArgoCD's app-of-apps pattern.
This repository, flamingo-stack/openframe-cli, is a single Go module (github.com/flamingo-stack/openframe-cli). Per the organization's dependency graph it has no recorded upstream dependencies on other internal repositories and no recorded downstream consumers — it is a terminal artifact whose dependencies are third-party Go modules (Cobra, client-go, pterm, huh/bubbletea, sigstore-go) and external CLI tools it shells out to (Docker, k3d, Helm, Terraform, gcloud, aws).
Not the platform repository. The actual OpenFrame platform that this CLI installs onto a cluster lives in the separate
flamingo-stack/openframe-oss-tenantrepository. Do not look for platform services, charts, or application code inside this repository's subdirectories.
flamingo-stack/openframe-cli remain the mechanism for proposing code changes.cmd/, internal/cluster, internal/chart, internal/app, and the shared packages fit together.go1.26.5 for a Windows-amd64 GC bug), external tools (Docker, k3d, Helm, Terraform, gcloud, aws), linting setup, build commands, and the local run/debug loop. This document does not duplicate those steps.main and develop. Most day-to-day work merges into develop.main, via a manual dispatch of the release workflow — there is no automatic release on push. The release workflow enforces a hard guard that fails if dispatched from anywhere other than refs/heads/main, because the self-updater's cosign signing identity is pinned to that workflow running from main. Do not rename the release workflow file or remove that guard.main, develop, and any branch with an open pull request.flamingo-stack/openframe-cli once your branch is ready.@flamingo-stack/devops-engineers as the default reviewer for the whole repository.golangci-lint + go mod tidy -diff), a compile-only cross-platform build matrix across all six supported GOOS/GOARCH combinations, unit tests on Linux/Windows/macOS (plus the race detector on Linux), and — for PRs targeting main from this repository itself — a full end-to-end cluster lifecycle test.🦩 Flamingo Code Review is available on pull requests. It does not run on every event automatically: a review is triggered by checking a box on the invitation comment, adding the flamingo-review label, or commenting @flamingo-review (optionally @flamingo-review full to re-review the whole cumulative diff).semantic-release analyzes on main, the title itself must be a valid Conventional Commit — this is linted automatically on every title edit. A free-form title contributes nothing to any future release version bump and will fail the check.This repository follows Conventional Commits, enforced at the PR-title level (since the repository squash-merges, individual commits within a branch are not separately linted):
type(scope)?: subject
Examples:
fix(k3d): tolerate KUBECONFIG lists
feat!: drop v1 config
type is one of the conventional types (fix, feat, chore, docs, refactor, test, ci, etc.).scope is optional and parenthesized, naming the affected area (e.g. k3d, cluster, app).! after the type/scope (e.g. feat!:) marks a breaking change.fix maps to a patch release, feat to a minor release, and a BREAKING CHANGE footer or ! marker to a major release — this is how semantic-release computes the next version with no manual version input.Write your PR title in this format from the start; it is linted on every edit.
Before requesting review, and while reviewing someone else's PR, verify:
golangci-lint run ./... passes locally with no new findings — this gate is blocking in CI.go mod tidy -diff reports no drift.make test-unit); on Linux, also run the race-detector variant (make test-race).integration build tag and lives under tests/integration/ — it must not run as part of go test ./... or make test-unit.Get<Group>Cmd()/get<Sub>Cmd() factory, not an inline cobra.Command{} literal, and any custom PersistentPreRunE calls ui.ApplyGlobalOutputFlags(cmd).New<Name>Service constructor, with dependencies injected rather than derived internally.fmt.Errorf("...: %w", err)) at each call boundary; exit codes from external tools are preserved via *executor.CommandError; errors already shown to the user are returned as *sharederrors.AlreadyHandledError to avoid double-printing.fmt.Println/fmt.Printf was added for user-facing output — it goes through pterm or internal/shared/ui.--context/cluster-targeting flag reject stray positional arguments with a named-flag hint, especially for destructive commands.semantic-release reads from main.refs/heads/main-only guard and the workflow's filename are both left intact — both are load-bearing for the self-update cosign trust chain.Full conventions and the active, judged review rule catalog are documented in Contributing Guidelines and enforced live via the organization's code review rule set. The key points to know up front:
Linting is done with golangci-lint v2, configured in .golangci.yml, building on the standard default set (errcheck, govet, ineffassign, staticcheck, unused) plus gosec, bodyclose, errorlint, and misspell. govet runs with enable-all: true, excluding only fieldalignment and shadow. Test files (_test.go) are exempt from errcheck and gosec. Every finding is reported — max-issues-per-linter and max-same-issues are both 0.
Run before opening a PR:
golangci-lint run ./...
go mod tidy -diff
These are judged, active rules specific to this repository — see Contributing Guidelines for the full list with examples:
Get<Group>Cmd() (exported) or get<Sub>Cmd() (unexported) factory function. The root command never constructs cobra.Command literals directly. A command group with its own PersistentPreRunE must call ui.ApplyGlobalOutputFlags(cmd) first, since Cobra runs only the closest parent's hook.New<Name>/New<Name>Service constructor returning a pointer, with dependencies injected rather than derived internally.config, ui, or errors) must be imported with a camelCase alias encoding the owning subsystem (sharedErrors, chartServices, uiCluster, etc.).fmt.Errorf("<context>: %w", err). Bare return err at a call boundary is not acceptable.sharedErrors.HandleGlobalError must be returned as (or wrap) *sharederrors.AlreadyHandledError, so main does not double-print it.*executor.CommandError, which main.exitCode() unwraps to propagate the exact exit code.pterm printers or helpers in internal/shared/ui. Raw fmt.Println/fmt.Printf to stdout is forbidden for user-facing output, since it bypasses --silent, --plain, and pterm's writer redirection used in tests.cmd/root.go's root cobra.Command must set SilenceErrors: true and SilenceUsage: true.version, commit, date) in cmd/root.go must be package-level string vars, never struct fields, since the linker's -ldflags -X flag can only target package-level string vars.pterm.Info.Printf instead.--context) must reject stray positional arguments with a validator naming the correct flag.CGO_ENABLED=0 go build -trimpath. The only sanctioned exception is the race-detector target.integration build tag and live under tests/integration/.Two deterministic, organization-wide checks also apply to every PR:
DUP-001, warn): a new or changed exported definition whose normalized structure matches an existing one elsewhere in the org is flagged.TEST-001, info): a new or changed exported definition that no test file references is flagged as shipped unverified.Full testing strategy, test-harness internals, and conventions are documented in Testing. In summary:
# Unit tests (fast, no external services)
go test ./...
# Unit tests with the race detector
go test -race ./...
# Integration tests (real k3d clusters; needs docker + k3d)
go test -tags integration ./tests/integration/...
internal/shared/executor.NewMockCommandExecutor(), or testutil.NewTestMockExecutor() / testutil.CreateStandardTestFlags()) over real binaries for anything that shells out.tests/testutil/command_assertions.go (AssertNoArgContains, AssertArgIsDiscrete, AssertNoBashCConcatenation, AssertNoDeleteOf, AssertNoCurlPipeShell).testutil.TestCommandStructure, testutil.TestCLICommand, testutil.TestClusterCommand) rather than hand-rolled plumbing.tests/testutil/flag_contract.go's AssertFlags/AssertSubcommands.--help coverage — cmd/help_matrix_test.go discovers the full command tree automatically — but you must still provide a non-empty Short description and flag usage text.Coverage is enforced through CI gates rather than a single numeric threshold: golangci-lint run ./... must report zero issues, go test ./... must pass on Linux/Windows/macOS, go test -race ./... must pass on Linux, every command must answer --help correctly, and the CLI's public surface (persistent flags, subcommands, flag specs) is frozen by contract tests.
If your change touches credentials, downloads, or self-update code, review Security first. In particular:
internal/chart/providers/git/auth.go (secrets carried only in memory, never in argv or on disk).internal/shared/redact's Redact helper rather than printing it directly.internal/shared/download's checksum-verification path (Downloader.FetchVerified / VerifyChecksum), never fetched ad hoc.exec.CommandContext(ctx, name, args...)), never a shell string; a #nosec suppression, if required, must be scoped and justified inline.internal/shared/selfupdate/cosign.go) as security-critical — any change that weakens or bypasses it is a regression, not a convenience fix.Do not open a public GitHub issue or pull request for a security vulnerability. Report it by following the process in SECURITY.md, which covers scope, what to include in a report, and what to expect in response.
The full documentation set for this repository starts at docs/README.md, which indexes getting-started guides, development docs (architecture, setup, testing, security, releasing), and reference material.
Documents referenced above, for convenience:
All contribution discussion happens on the OpenMSP Slack community (invite link) — there are no GitHub Issues or Discussions for this project.