go-ansible/CLAUDE.md
Virgil 5609471945
Some checks are pending
CI / test (push) Waiting to run
CI / auto-fix (push) Waiting to run
CI / auto-merge (push) Waiting to run
Add replace module support
2026-04-03 13:31:06 +00:00

68 lines
4.7 KiB
Markdown

# 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. 42 module handler implementations (plus 3 community modules), 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 & Test
```bash
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 flat `ansible` package with four layers:
```
Playbook YAML ──► Parser ──► []Play ──► Executor ──► Module Handlers ──► SSH Client ──► Remote Host
│ │
Inventory YAML ──► Parser ──► Inventory Callbacks (OnPlayStart, OnTaskEnd, ...)
```
- **`types.go`** — Core structs (`Playbook`, `Play`, `Task`, `TaskResult`, `Inventory`, `Host`, `Facts`) and `KnownModules` registry (96 entries: both FQCN `ansible.builtin.*` and short forms, plus compatibility aliases).
- **`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`** — 50 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 <playbook>` and `ansible test <host>` subcommands with flags for inventory, limit, tags, extra-vars, verbosity, and check mode.
## 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** in comments and documentation (colour, organisation, centre)
- Test naming: `_Good` (happy path), `_Bad` (expected errors), `_Ugly` (edge cases/panics)
- Use `coreerr.E(scope, message, err)` from `go-log` for all errors in production code (never `fmt.Errorf`)
- Tests use `testify/assert` (soft) and `testify/require` (hard)
- Licence: EUPL-1.2