From e66ceeb12d2b0a5b879b69bcf38f0aaa67add2f3 Mon Sep 17 00:00:00 2001 From: Enno Gotthold Date: Fri, 17 Jul 2026 11:17:46 +0200 Subject: [PATCH 1/2] docs: remove retired go report card --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 489bc36..2ae175a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/spf13/cobra.svg)](https://pkg.go.dev/github.com/cobbler/cli) -[![Go Report Card](https://goreportcard.com/badge/github.com/cobbler/cli)](https://goreportcard.com/report/github.com/cobbler/cli) [![Documentation Status](https://readthedocs.org/projects/cobbler-cli/badge/?version=latest)](https://cobbler-cli.readthedocs.io/en/latest/?badge=latest) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/eef9814e0e7145ddb908bbdf07c35dd4)](https://app.codacy.com/gh/cobbler/cli/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/eef9814e0e7145ddb908bbdf07c35dd4)](https://app.codacy.com/gh/cobbler/cli/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage) From 870861f18c9bc6b9a83413c65bb327b270efd9cf Mon Sep 17 00:00:00 2001 From: Enno Gotthold Date: Fri, 17 Jul 2026 11:23:50 +0200 Subject: [PATCH 2/2] docs: add AGENTS.md guideance file This also creates a symlink for CLAUDE.md. --- AGENTS.md | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 1 + 2 files changed, 129 insertions(+) create mode 100644 AGENTS.md create mode 120000 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6165873 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,128 @@ +# AGENTS.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project overview + +`cobbler-cli` is an independent Go CLI client for the [Cobbler](https://cobbler.readthedocs.io) provisioning server. +It talks to a Cobbler server over XML-RPC via the `github.com/cobbler/cobblerclient` library. The CLI is built with +`spf13/cobra` (commands) and `spf13/viper` (config file / env var loading). + +Current line targets Cobbler **4.0.0** backend and cobblerclient `v1.x` — this was a clean break from the `v0.x` CLI +line, which talks to Cobbler 3.3.x via cobblerclient `v0.5.x`. See `MIGRATION.md` for the full list of breaking changes +(removed item types, moved interface flags, UID-vs-name reference semantics) and `CHANGELOG.md` for the current +unreleased changes. + +## Build, lint, test + +```bash +make build # go build -o cobbler main.go, then regenerates shell completions +make run # build and run the binary +make test # runs testing/start.sh then `go test -v -coverprofile=coverage.out -covermode=atomic ./...` +make clean # go clean, remove binary and generated completions +``` + +Linting is `golangci-lint` (v2.9.0, run via `golangci-lint run` — no repo-local `.golangci.yml`, defaults from CI at +`.github/workflows/lint.yml`). Docs (`docs/`) are linted separately with `rstcheck` and `doc8`. + +### Tests are integration tests against a real Cobbler server + +There are no mocks — `cmd/*_test.go` files execute the actual cobra commands (`rootCmd.Execute()`) against a live +Cobbler XML-RPC server. `make test` / `./testing/start.sh ` will: + +1. Clone/checkout a pinned commit of `github.com/cobbler/cobbler` into `testing/cobbler_source`. +2. Download and extract a legacy Ubuntu 20.04 ISO into `extracted_iso_image/` (used as fake kernel/initrd fixtures for + distros). +3. Build a `cobbler-dev` Docker image and bring up `testing/compose.yml`. +4. Poll the server URL until Cobbler answers, then run `go test`. + +This requires Docker and `xorriso` locally. To run a single test **after** the server is already up (e.g. iterating on +one file without re-running the whole setup): + +```bash +go test -v -run Test_DistroAddCmd ./cmd/... +``` + +Tests read connection settings from `testing/.cobbler.yaml` (passed via `--config` in test args, or loaded through +`setupClient(t)` in `cmd/testing.go`). Test helpers of note: + +- `cobbler.FailOnError(t, err)` — from the cobblerclient package, fails the test on non-nil error. +- `FailOnNonEmptyStream(t, buf)` (`cmd/testing.go`) — asserts stderr stayed empty. +- Every test that creates an object registers a `defer` cleanup that deletes it via the client directly (not through the + CLI). + +### Commit messages + +Commit messages are linted with commitlint (`commitlint.config.mjs`, conventional-commits) via a pre-commit hook +(`.pre-commit-config.yaml`). Header ≤ 72 chars, body/footer lines ≤ 120 chars. + +## Architecture + +### Command structure + +All CLI code lives in `cmd/`. Every Cobbler item type (`distro`, `profile`, `system`, `image`, `menu`, `repo`, etc.) +has its own `.go` file following the same shape: + +- `NewCmd() (*cobra.Command, error)` builds the parent command (e.g. `distro`) and wires up its subcommands + (`add`, `copy`, `edit`, `find`, `list`, `remove`, `rename`, `report`, and often `export`). +- `NewAddCmd`, `NewEditCmd`, etc. build each subcommand. `add`/`copy`/`edit`/`rename` all funnel field + updates through a shared `updateFromFlags(cmd, item)` function that walks `cmd.Flags().Visit(...)` and only + touches fields whose flags were explicitly set — this is what makes partial edits work without clobbering unset + fields. +- Every `RunE` starts with `generateCobblerClient()` (`cmd/utils.go`) to log into the server before doing anything + else. +- New group item types (`distro_group`, `profile_group`, `system_group` — added for 4.0.0) share logic in + `cmd/group_common.go` instead of each having their own copy. + +`cmd/root.go` (`NewRootCmd`) is the single place every top-level command is registered; add new item commands there. + +### Flag metadata (`cmd/metadata.go`) + +Flags aren't declared ad hoc — they're defined once as data in `cmd/metadata.go` using the generic `FlagMetadata[T]` +struct (`Name`, `DefaultValue`, `Usage`, `IsInheritable`), grouped into maps like `distroStringFlagMetadata`, +`profileMapFlagMetadata`, `systemBoolFlagMetadata`, etc. `cmd/item.go` has generic registration helpers +(`addStringFlags`, `addBoolFlags`, `addIntFlags`, `addFloatFlags`, `addStringSliceFlags`, `addMapFlags`) that iterate +these maps and call the matching `cobra.Command.Flags()` method. + +For fields that support Cobbler's "inherit from parent" semantics (`IsInheritable: true`), the flag registration helpers +automatically also add a `---inherit` boolean sibling flag (e.g. `--owners` / `--owners-inherit`) and mark them +mutually exclusive where applicable. `updateFromFlags` checks whether the `-inherit` flag was `.Changed` to decide +whether to set `IsInherited` or the concrete value. + +When adding a new flag to an item type: add an entry to the relevant metadata map in `cmd/metadata.go`, then add a +`case` for it in that item's `updateFromFlags` switch in `.go` (or `group_common.go` for group types). + +### Value objects and "in-place" edits + +Fields like `AutoinstallMeta`, `KernelOptions`, `KernelOptionsPost`, `BootLoaders`, `Owners` are cobblerclient +`Value[T]` wrapper objects with `Data` and `IsInherited`. Some commands (`distro edit/copy/rename`, similarly for other +item types) accept an `--in-place` flag: when set, map-valued fields (kernel options, autoinstall meta, template files) +are updated via `Client.ModifyItemInPlace(itemType, name, field, value)` (merge semantics on the server) instead of +being replaced wholesale via `Client.Update`. This distinction matters — read the `inPlace` branching in +`updateFromFlags` before changing that logic. + +### Cross-cutting helpers (`cmd/utils.go`, `cmd/item.go`) + +- `printStructured` / `printField` / `printValueStructured` (`cmd/root.go`) — reflection-based generic printer used by + every `report` subcommand to dump an item's fields, handling `Value[T]` wrappers, nested `NetworkInterface` maps, + and `ctime`/`mtime` float→UTC-time conversion. +- `printDumpVars` (`cmd/utils.go`) — separate reflection-based printer for `dumpvars` output (raw + `map[string]interface{}`, not a struct). +- `FindItemNames` (`cmd/item.go`) — shared implementation backing every ` find` subcommand. Builds a criteria + map from whatever flags the user set, and supports pagination (`--page`/`--items-per-page`, added via + `addPaginationFlags`) by routing through `Client.FindItemsPaged` instead of `Client.FindItemNames`. +- `RemoveItemRecursive` (`cmd/item.go`) — shared implementation for recursive item deletion (`--recursive`). +- Export subcommands (` export`) marshal items to JSON or YAML (`--format`); shared via `writeExport` in + `cmd/group_common.go` for group types, inlined per-item elsewhere (e.g. `NewDistroExportCmd`). + +### Network interfaces are a separate command + +Unlike `distro`/`profile`/`system`, network interfaces are managed by the standalone `cobbler interface` command +(`cmd/interface.go`), not flags on `cobbler system`. This is a 4.0.0-era change — see `MIGRATION.md` for the full +old-flag → new-flag mapping if working on interface-related code. + +### Config resolution + +`cmd/root.go`'s `initConfig()` (run via `cobra.OnInitialize`) loads `$HOME/.cobbler.yaml` (or `--config `) through +viper, with defaults `server_url=http://127.0.0.1/cobbler_api`, `server_username=cobbler`, `server_password=cobbler`, +overridable by environment variables (`viper.AutomaticEnv()`). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file