|
1 | | -@AGENTS.md |
| 1 | +# OPM CLI repository guide |
| 2 | + |
| 3 | +## Purpose |
| 4 | + |
| 5 | +OPM CLI — command-line interface for Open Platform Model workflows. Build, validate, render, deploy, inspect portable app releases defined with CUE. Focus: type safety, clear command behavior, Kubernetes-oriented workflows. |
| 6 | + |
| 7 | +Stack: `cobra`, CUE Go SDK, Kubernetes `client-go`, `charmbracelet/log`, `testify`. |
| 8 | + |
| 9 | +For coding agents working in `cli/`. |
| 10 | + |
| 11 | +## Repository Rules |
| 12 | + |
| 13 | +- Small, independently verifiable changes; `CONSTITUTION.md` prefers tiny batches. |
| 14 | +- Update existing packages over new abstractions unless duplication/coupling justifies it. |
| 15 | + |
| 16 | +## Entrypoint |
| 17 | + |
| 18 | +Read when entering `cli/`: |
| 19 | + |
| 20 | +- `CONSTITUTION.md` - repo principles + change-shaping constraints. |
| 21 | +- `CLAUDE.md` - implementation guidance, commands, package map. |
| 22 | +- `README.md` - product purpose, command groups, user workflows. |
| 23 | +- `docs/STYLE.md` - doc prose style rules. |
| 24 | + |
| 25 | +## Repository Layout |
| 26 | + |
| 27 | +- `adr/` - Architecture Decision Records |
| 28 | +- `cmd/opm/` - CLI entrypoint + root command wiring. |
| 29 | +- `internal/cmd/` - Cobra command implementations. |
| 30 | +- `internal/cmdutil/` - shared flags, annotations, command-facing helpers. |
| 31 | +- `internal/config/` - config resolution, schema validation, defaults. |
| 32 | +- `internal/kubernetes/` - cluster ops, status, apply, delete, events. |
| 33 | +- `internal/output/` - terminal formatting, log output, tables, manifests. |
| 34 | +- `internal/releasefile/` - release file detection + loading. |
| 35 | +- `internal/workflow/` - shared render/apply/query orchestration. |
| 36 | +- `pkg/loader/` - CUE loading for modules, providers, releases. |
| 37 | +- `pkg/render/` - render pipeline logic. |
| 38 | +- `pkg/errors/` - shared structured errors; alias as `oerrors`. |
| 39 | +- `tests/integration/` - integration programs via `go run`. |
| 40 | +- `tests/e2e/` - end-to-end Go tests. |
| 41 | + |
| 42 | +## Architecture Decision Records |
| 43 | + |
| 44 | +ADRs capture significant technical decisions with context + consequences. |
| 45 | + |
| 46 | +- Location: `adr/` |
| 47 | +- Template: `adr/TEMPLATE.md` |
| 48 | +- Naming: `NNN-kebab-case-title.md` (three-digit, zero-padded) |
| 49 | + |
| 50 | +### Creating a new ADR |
| 51 | + |
| 52 | +1. Copy `adr/TEMPLATE.md` to `adr/NNN-title.md` with next available number. |
| 53 | +2. Set status to `Proposed`. |
| 54 | +3. Fill Context, Decision, Consequences. |
| 55 | +4. Update status to `Accepted` once agreed. |
| 56 | + |
| 57 | +### Updating an ADR |
| 58 | + |
| 59 | +- Never delete — update status instead. |
| 60 | +- Retire: set status `Deprecated`. |
| 61 | +- Replace: set status `Superseded by ADR-NNN`, create new ADR. |
| 62 | +- One decision per ADR. |
| 63 | + |
| 64 | +## Environment Notes |
| 65 | + |
| 66 | +- Go version in `go.mod`: `1.25.0`. |
| 67 | +- Integration + CUE workflows need registry config. |
| 68 | +- Local dev defaults: |
| 69 | + |
| 70 | +```bash |
| 71 | +export OPM_REGISTRY='opmodel.dev=localhost:5000+insecure,registry.cue.works' |
| 72 | +export CUE_REGISTRY='opmodel.dev=localhost:5000+insecure,registry.cue.works' |
| 73 | +``` |
| 74 | + |
| 75 | +## Build And Dev Commands |
| 76 | + |
| 77 | +### Core commands |
| 78 | + |
| 79 | +- `task build` - build `./bin/opm` from `./cmd/opm` with version ldflags. |
| 80 | +- `task build:all` - cross-compile for Linux, macOS, Windows. |
| 81 | +- `task install` - install CLI with version ldflags into `$GOPATH/bin`. |
| 82 | +- `task clean` - remove `bin/`, `coverage.out`, `coverage.html`. |
| 83 | +- `task generate` - run `go generate ./...`. |
| 84 | + |
| 85 | +### Formatting and static analysis |
| 86 | + |
| 87 | +- `task fmt` - run `go fmt ./...` and `goimports -w .`. |
| 88 | +- `task vet` - run `go vet ./...`. |
| 89 | +- `task lint` - run `golangci-lint run ./...`. |
| 90 | +- `task lint:fix` - run `golangci-lint run --fix ./...`. |
| 91 | +- `task tidy` - run `go mod tidy`. |
| 92 | +- `task check` - run `fmt`, `vet`, `lint`, all tests. |
| 93 | + |
| 94 | +### Tests |
| 95 | + |
| 96 | +- `task test` - run unit, integration, e2e suites. |
| 97 | +- `task test:unit` - run `go test ./internal/...` and `go test ./pkg/...`. |
| 98 | +- `task test:integration` - run integration programs; needs live kind cluster. |
| 99 | +- `task test:e2e` - run `go test ./tests/e2e/... -v`. |
| 100 | +- `task test:verbose` - run `go test -v ./...`. |
| 101 | +- `task test:coverage` - run `go test -coverprofile=coverage.out ./...` then generate `coverage.html`. |
| 102 | + |
| 103 | +### Running one test |
| 104 | + |
| 105 | +- Preferred: `task test:run TEST=TestName`. |
| 106 | +- Direct: `go test -v ./... -run "TestName"`. |
| 107 | +- Narrow to one package for speed: |
| 108 | + - `go test ./internal/config -run TestLoad -v` |
| 109 | + - `go test ./pkg/render -run TestFinalize -v` |
| 110 | +- Single subtest: use full regexp name via `go test -run`. |
| 111 | + |
| 112 | +### Integration cluster helpers |
| 113 | + |
| 114 | +- `task cluster:create` - create local `kind` cluster `opm-dev`. |
| 115 | +- `task cluster:status` - check cluster running. |
| 116 | +- `task cluster:delete` - remove local cluster. |
| 117 | +- `task cluster:recreate` - recreate cluster from scratch. |
| 118 | +- `task test:integration` checks for context `kind-opm-dev` before running. |
| 119 | + |
| 120 | +## Coding Standards |
| 121 | + |
| 122 | +### General |
| 123 | + |
| 124 | +- Follow `gofmt` and `goimports`; no hand-formatting imports. |
| 125 | +- Keep command packages thin; orchestration in `internal/workflow` or focused internal/pkg packages. |
| 126 | +- Explicit behavior over magic inference; `CONSTITUTION.md` favors clear inputs + early validation. |
| 127 | +- Preserve cross-platform behavior; no hardcoded Unix-only paths or shell assumptions. |
| 128 | + |
| 129 | +### Imports |
| 130 | + |
| 131 | +- Standard Go order: stdlib, third-party, internal project imports. |
| 132 | +- Blank lines between groups as `goimports` produces. |
| 133 | +- Alias `github.com/opmodel/cli/pkg/errors` as `oerrors`. |
| 134 | +- No unnecessary aliases unless collision or strong clarity reason. |
| 135 | + |
| 136 | +### Types and APIs |
| 137 | + |
| 138 | +- Concrete structs as return values; interfaces at boundaries for testability. |
| 139 | +- No `interface{}` / `any` unless API genuinely needs open-ended data. |
| 140 | +- Config, flags, render inputs: strongly typed. |
| 141 | +- Propagate `context.Context` through I/O, Kubernetes calls, longer workflows. |
| 142 | +- Fresh CUE contexts per command/workflow, not one global mutable context. |
| 143 | + |
| 144 | +### Naming |
| 145 | + |
| 146 | +- Exported: PascalCase; unexported: camelCase. |
| 147 | +- Descriptive domain names: `ReleaseSelectorFlags`, `ResolveModulePath`, `BootstrapRegistry`. |
| 148 | +- Booleans read naturally: `HasWarnings`, `configHasProviders`. |
| 149 | +- Error sentinels follow Go conventions; linter enables `errname` + revive naming rules. |
| 150 | +- Package names: short, lowercase, responsibility-focused. |
| 151 | + |
| 152 | +### Error handling |
| 153 | + |
| 154 | +- Validate early, fail before execution on invalid flags/config/inputs. |
| 155 | +- Wrap errors with context via `%w`: `fmt.Errorf("loading module: %w", err)`. |
| 156 | +- Actionable user-facing errors with hints over raw internal failures. |
| 157 | +- Reuse `pkg/errors` types, especially `DetailError` + validation helpers. |
| 158 | +- Commands use `RunE`, return errors — no print-and-exit inline. |
| 159 | +- Preserve sentinel errors via wrapping for `errors.Is` / `errors.As`. |
| 160 | + |
| 161 | +### Control flow and package boundaries |
| 162 | + |
| 163 | +- Commands parse flags + delegate; no core business logic. |
| 164 | +- `internal/` depends on `pkg/`; `pkg/` stays reusable + command-agnostic. |
| 165 | +- Output formatting separate from data generation. |
| 166 | +- Small focused functions over large multipurpose helpers. |
| 167 | + |
| 168 | +### Tests |
| 169 | + |
| 170 | +- Table-driven tests for multiple scenarios. |
| 171 | +- `require` for setup/fatal preconditions; `assert` for non-fatal expectations. |
| 172 | +- `t.Helper()` in test helpers. |
| 173 | +- `t.TempDir()` over manual fixture dirs when practical. |
| 174 | +- Name `TestXxx` with behavior suffixes, e.g. `TestRenderFromReleaseFile_NilConfig`. |
| 175 | + |
| 176 | +## Lint Configuration Highlights |
| 177 | + |
| 178 | +- `golangci-lint` runs in readonly module download mode. |
| 179 | +- Key linters: `errorlint`, `errname`, `gocritic`, `gocyclo`, `gosec`, `revive`, `staticcheck`, `tparallel`. |
| 180 | +- `nolint` comments must be specific with explanation. |
| 181 | +- `gocyclo` threshold: 15; refactor before complexity grows. |
| 182 | +- Tests relax `dupl`, `errcheck`, `goconst`, `gosec`. |
| 183 | +- `examples/`, `experiments/`, `third_party/`, `builtin/` excluded from lint/format. |
| 184 | + |
| 185 | +## Documentation And Output Conventions |
| 186 | + |
| 187 | +- ASCII-safe output in docs, examples, terminal text. |
| 188 | +- Box-drawing: `[x]` / `[ ]` not Unicode checkmarks. |
| 189 | +- CLI docs: emphasize what happened + how to fix failures. |
| 190 | +- Follow SemVer + Conventional Commits for user-visible changes. |
| 191 | + |
| 192 | +## Agent Checklist |
| 193 | + |
| 194 | +- Read touched package + nearby tests before editing. |
| 195 | +- Run targeted tests first, broader checks if warranted. |
| 196 | +- Changed formatting files → run `task fmt`. |
| 197 | +- Changed behavior → run smallest relevant `go test` + affected task. |
| 198 | +- Before finishing substantial work → `task lint` + relevant test suite. |
0 commit comments