From 46ff6b7bc77a250fed854f03aeca1403374126e2 Mon Sep 17 00:00:00 2001 From: Snider Date: Fri, 13 Mar 2026 13:38:01 +0000 Subject: [PATCH] docs: add CLAUDE.md project instructions Co-Authored-By: Virgil --- CLAUDE.md | 71 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index ab6322b..5882fde 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,37 +1,68 @@ # CLAUDE.md +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + ## Project Overview -`core/go-ansible` is a pure Go Ansible playbook engine. It parses YAML playbooks, inventories, and roles, then executes tasks on remote hosts via SSH. 174 module implementations, Jinja2-compatible templating, privilege escalation (become), and event-driven callbacks. +`core/go-ansible` is a pure Go Ansible playbook engine. It parses YAML playbooks, inventories, and roles, then executes tasks on remote hosts via SSH. 174 module implementations, Jinja2-compatible templating, privilege escalation (become), and event-driven callbacks. This is a library — there is no standalone binary. The CLI integration lives in `cmd/ansible/` and is compiled as part of the `core` CLI binary. -## Build & Development +## Build & Test ```bash -go test ./... -go test -race ./... +go build ./... # verify compilation +go test ./... # run all tests +go test -race ./... # with race detection +go test -run TestParsePlaybook_Good_SimplePlay # single test +go test -v ./... # verbose output +go work sync # if in a Go workspace ``` +No SSH access is needed for tests — the suite uses a mock SSH client (`mock_ssh_test.go`). + ## Architecture -Single `ansible` package: +Single flat `ansible` package with four layers: -- `types.go` — Playbook, Play, Task, TaskResult, Inventory, Host, Facts structs -- `parser.go` — YAML parser for playbooks, inventories, tasks, roles -- `executor.go` — Task execution engine with module dispatch, templating, condition evaluation -- `modules.go` — 30+ module implementations (shell, copy, apt, systemd, git, docker-compose, etc.) -- `ssh.go` — SSH client with known_hosts verification, become/sudo, file transfer +``` +Playbook YAML ──► Parser ──► []Play ──► Executor ──► Module Handlers ──► SSH Client ──► Remote Host + │ │ +Inventory YAML ──► Parser ──► Inventory Callbacks (OnPlayStart, OnTaskEnd, ...) +``` -## Dependencies +- **`types.go`** — Core structs (`Playbook`, `Play`, `Task`, `TaskResult`, `Inventory`, `Host`, `Facts`) and `KnownModules` registry (68 entries: both FQCN `ansible.builtin.*` and short forms). +- **`parser.go`** — YAML parsing for playbooks, inventories, tasks, and roles. Custom `Task.UnmarshalYAML` scans map keys against `KnownModules` to extract the module name and args (since Ansible embeds the module name as a YAML key, not a fixed field). Free-form syntax (`shell: echo hello`) is stored as `Args["_raw_params"]`. Iterator variants (`ParsePlaybookIter`, `ParseTasksIter`, etc.) return `iter.Seq` values. +- **`executor.go`** — Orchestration engine: host resolution from inventory, play execution order (gather facts → pre_tasks → roles → tasks → post_tasks → notified handlers), `when:` condition evaluation, `{{ }}` Jinja2-style templating with filter support, loop execution, block/rescue/always, handler notification. +- **`modules.go`** — 41 module handler implementations dispatched via a `switch` on the normalised module name. Each handler extracts args via `getStringArg`/`getBoolArg`, constructs shell commands, runs them via SSH, and returns a `TaskResult`. +- **`ssh.go`** — SSH client with lazy connection, auth chain (key file → default keys → password), `known_hosts` verification, become/sudo wrapping, file transfer via `cat >` piped through stdin. +- **`cmd/ansible/`** — CLI command registration via `core/cli`. Provides `ansible ` and `ansible test ` subcommands with flags for inventory, limit, tags, extra-vars, verbosity, and check mode. -- `go-log` — Structured logging -- `golang.org/x/crypto` — SSH protocol -- `gopkg.in/yaml.v3` — YAML parsing -- `testify` — Test assertions +## Adding a New Module + +1. Add both FQCN and short form to `KnownModules` in `types.go` +2. Add the dispatch case in `executeModule` switch in `modules.go` +3. Implement `module{Name}(ctx, client, args)` method on `Executor` in `modules.go` +4. Write tests in the appropriate `modules_*_test.go` file using mock SSH infrastructure + +If adding new YAML keys to `Task`, update the `knownKeys` map in `Task.UnmarshalYAML` (`parser.go`) to prevent them being mistaken for module names. + +## Test Organisation + +| File | Coverage | +|------|----------| +| `types_test.go` | YAML unmarshalling for Task, RoleRef, Inventory, Facts | +| `parser_test.go` | Playbook, inventory, and task file parsing | +| `executor_test.go` | Executor lifecycle, conditions, templating, loops, tags | +| `ssh_test.go` | SSH client construction and defaults | +| `modules_cmd_test.go` | Command modules: shell, command, raw, script | +| `modules_file_test.go` | File modules: copy, template, file, lineinfile, stat, slurp, fetch, get_url | +| `modules_svc_test.go` | Service modules: service, systemd, user, group | +| `modules_infra_test.go` | Infrastructure modules: apt, pip, git, unarchive, ufw, docker_compose | +| `modules_adv_test.go` | Advanced modules: debug, fail, assert, set_fact, pause, wait_for, uri, blockinfile, cron, hostname, sysctl, reboot | ## Coding Standards -- UK English -- All functions have typed params/returns -- Tests use testify + mock SSH client -- Test naming: `_Good`, `_Bad`, `_Ugly` suffixes -- License: EUPL-1.2 +- **UK English** in comments and documentation (colour, organisation, centre) +- Test naming: `_Good` (happy path), `_Bad` (expected errors), `_Ugly` (edge cases/panics) +- Use `log.E(scope, message, err)` from `go-log` for errors in SSH/parser code; `fmt.Errorf` with `%w` in executor code +- Tests use `testify/assert` (soft) and `testify/require` (hard) +- Licence: EUPL-1.2