Skip to content

Continuous integration

CI is built in. There is no root config file and no external service to wire up: work is declared by project.toml files scattered through the tree, one per buildable unit.

[project]
name = "api"
language = "go"
[test]
command = "go test ./..."
timeout = "10m"
[deps]
internal = ["libs/auth"]

garden discovers every project.toml and uses them to scope what builds, what tests run, and how changed files map to work. Full schema: project.toml, or garden docs project.

This is the payoff of declaring dependencies. A change in libs/auth selects api’s tests too, because api named it in [deps].internal — while leaving the other two hundred projects alone.

In a monorepo holding the whole company, running everything on every change isn’t an option. Selection is what makes keeping it all in one repo practical.

Per revision, on every push. Then again in the merge queue against the rebase before landing, and once more on trunk afterwards.

The part that differs most from other CI systems. A failing check doesn’t just turn a status red — it posts an inline review thread on the failing line, authored by ci-bot. Those threads:

  • block the merge until resolved,
  • reopen when the test re-fails, even if a human resolved them,
  • resolve themselves when the test passes.

So a failing test lands in the same place as human feedback and clears the same way: fix it and push a new revision.

Terminal window
garden ci list --pr 42 # every check on the head revision
garden ci logs build --pr 42 # streams live, replays if done
garden ci tests --pr 42 # parsed test results
garden ci rerun build --pr 42 # new attempt

garden pr checks 42 is the quick summary and exits non-zero if anything failed or is pending — usable as a gate in a script.

Beyond [test], a project can declare rules that fire on matching changes, and codegen that re-runs when its inputs change:

[[rule]]
name = "lint"
match = ["**/*.go"]
run = "golangci-lint run"
[generate]
commands = ["buf generate"]
triggers = ["proto/**/*.proto"]