Skip to content

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.

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.star

The file is optional. When no config exists, compiler commands use the current directory, default package patterns, and the default generated output path.

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.star

Run with:

Terminal window
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.env and command env maps merge, with profile values winning.
  • Stage and command maps merge by key.
  • run and argv are replaced together when the profile defines either one.
  • Inside a profile, both generator and generate are accepted. Profile generate overlays profile generator for generation fields. Prefer one spelling per profile so the final value is obvious.
scan:
dirs:
- ./api
patterns:
- ./api/...
exclude:
- ./internal/legacy

scan.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.

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:
- prod

Precedence:

  1. CLI output argument, when provided
  2. generator.output
  3. generate.output
  4. generator.output_dir
  5. generate.output_dir
  6. 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:
- ./tools/anvil/rules/http_write_policy.star

These 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.star

Rule file paths are resolved against the config file directory.

testbed:
scripts:
- ./tools/anvil/testbeds/projects.yaml

Testbed scripts are application YAML files. They are merged into generated suites before anvil testbed prints JSON or runs cases.

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: dev

The main package is selected in this order:

  1. build.main
  2. top-level main
  3. dev.main_path
  4. dev.main
  5. .

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:
test:
argv: ["go", "test", "./..."]
dir: .
env:
APP_ENV: test

Commands are named workflows for local development and CI. Keep them as clear executable definitions, not hidden application logic.