anvil.yaml
anvil.yaml tells Anvil how to compile a Go project.
It is project metadata for the compiler and CLI. It does not replace go.mod,
and it does not list Go dependencies. It tells Anvil where the application
lives, which packages to scan, where generated code goes, which rules run, and
which command profiles exist.
Minimal File
Section titled “Minimal File”project: name: portal version: v1
main: ./cmd/api
scan: dirs: - ./api - ./internal
generator: output: ./internal/anvil/anvil_autogenerated.go package: anvilgen
compiler: rules: - ./tools/anvil/rules/http_write_policy.starThe file is optional. When no config exists, compiler commands use the current directory, default package patterns, and the default generated output path.
Profiles
Section titled “Profiles”Profiles are sparse overlays. A profile can override only the fields it needs:
profiles: prod: project: version: v1-prod compiler: rules: - ./tools/anvil/rules/http_write_policy.star - ./tools/anvil/rules/no_debug_routes.starRun with:
anvil generate --profile prod .The same profile can also be selected with -p prod or the ANVIL_PROFILE
environment variable. A CLI profile flag wins over ANVIL_PROFILE.
Selecting a profile requires anvil.yaml or anvil.yml to exist in the project
directory. If the file is missing, or if the requested profile is not defined,
the command fails as a usage error before scanning packages.
Most file and command paths in the base config and profile are resolved against
the directory containing anvil.yaml. That includes main,
generator.output, generator.output_dir, generate.output,
generate.output_dir, build.main, build.output, compiler.rules,
testbed.scripts, dev.main, dev.main_path, dev.work_dir,
dev.watch_dirs, stage rules, and command directories.
scan.dirs and scan.patterns are package loading inputs, not rewritten file
paths. They are interpreted by Go package loading from the project directory.
Overlay rules:
- Strings override only when the profile value is non-empty.
- Slices replace the base slice when the profile slice is non-empty.
dev.envand commandenvmaps merge, with profile values winning.- Stage and command maps merge by key.
runandargvare replaced together when the profile defines either one.- Inside a profile, both
generatorandgenerateare accepted. Profilegenerateoverlays profilegeneratorfor generation fields. Prefer one spelling per profile so the final value is obvious.
Scan Configuration
Section titled “Scan Configuration”scan: dirs: - ./api patterns: - ./api/... exclude: - ./internal/legacyscan.patterns accepts raw Go package patterns. When it is set, it wins over
scan.dirs.
scan.dirs is the friendlier form. Anvil turns each directory into a package
pattern such as ./api/....
scan.exclude is parsed and carried by config. The scanner does not filter
packages with this field.
Generation Configuration
Section titled “Generation Configuration”Both generator and generate are accepted. generator is the preferred name.
generate exists for compatibility with earlier internal configs.
generator: output: ./internal/anvil/anvil_autogenerated.go output_dir: ./internal/anvil package: anvilgen patterns: - ./api/... stages: - prodPrecedence:
- CLI output argument, when provided
generator.outputgenerate.outputgenerator.output_dirgenerate.output_dir- default output path
If the selected output value ends with .go, Anvil treats it as the generated
file. Otherwise Anvil treats it as a directory and writes
anvil_autogenerated.go inside it.
If generator.package is empty, Anvil derives the package name from the output
directory. Non-letter and non-digit characters become _. If the directory
name starts with a digit, Anvil prefixes it with anvilgen_.
generator is the canonical section. generate is read as a compatibility
alias. When both are present, generator wins for fields it defines.
generator.patterns is used only when scan.patterns and scan.dirs are both
empty.
generator.stages is parsed by config. The CLI does not use it to select stage
rules. All stages.*.rules entries are appended in sorted stage-name order.
Use profiles when different environments need different rule sets.
Compiler Rules
Section titled “Compiler Rules”compiler: rules: - ./tools/anvil/rules/http_write_policy.starThese files are loaded as Starlark rules during generate and lint.
generate and lint also run Anvil’s built-in manifest rules. manifest,
openapi, and testbed build from the manifest without running configured
rules.
Stage rules are appended after compiler rules, sorted by stage name:
stages: prod: rules: - ./tools/anvil/rules/no_debug_routes.starRule file paths are resolved against the config file directory.
Testbed Scripts
Section titled “Testbed Scripts”testbed: scripts: - ./tools/anvil/testbeds/projects.yamlTestbed scripts are application YAML files. They are merged into generated
suites before anvil testbed prints JSON or runs cases.
Dev And Build
Section titled “Dev And Build”build: main: ./cmd/api output: ./bin/portal tags: - prod
dev: main: ./cmd/api work_dir: . watch_dirs: - ./api - ./internal watch_exts: - .go - .yaml argv: ["go", "run", "./cmd/api"] env: APP_ENV: devThe main package is selected in this order:
build.main- top-level
main dev.main_pathdev.main.
dev.run and dev.argv are used by anvil dev. commands.<name> entries are
used by anvil run <name>.
Use either dev.run or dev.argv, not both. argv runs directly without a
shell. run goes through the system shell.
When watch_exts entries omit the leading dot, Anvil adds it. The default
watch extensions are .go, .yaml, .yml, and .env. The dev watcher skips
.git, vendor, node_modules, bin, dist, and the generated output
directory.
Commands
Section titled “Commands”commands: test: argv: ["go", "test", "./..."] dir: . env: APP_ENV: testCommands are named workflows for local development and CI. Keep them as clear executable definitions, not hidden application logic.