Configuration

A [tools.trellis] table in a gleam.toml marks the workspace root — there is no separate config file. The root manifest may be config-only, or a regular Gleam package that also anchors the workspace. Only members is required; everything else trellis needs is derived from the member manifests.

gleam.toml
# gleam.toml at the repo root
[tools.trellis]
members = ["packages/*", "examples"]
# Matching members participate in task fan-out but are excluded from
# changelog, versioning, tagging, and publishing.
ignore-release = ["examples"]
# Custom tasks for `trellis run <name>`. Built-in verbs (build, test, check,
# format, docs, deps, clean) need no declaration.
[tools.trellis.tasks.lint]
command = "gleam run -m glinter"
needs-deps = true # run `gleam deps download` first if not cached
[tools.trellis.publish]
tag-format = "{name}-v{version}"

Each member is a directory with its own gleam.toml. Path dependencies between members define the graph; cycles and path deps that escape the workspace are rejected with a clear error.

A [tools.trellis] table in a member manifest is a doctor error — it would hijack root discovery, which walks up from the current directory to the first manifest carrying the table, the way git and cargo find their roots.

Keys

Key Required What it does
members yes Globs expanded into package directories. New packages join every command the moment their directory matches.
ignore-release no Members that run tasks but are excluded from changelog, versioning, tagging, and publishing. doctor rejects a releasable member that depends on an unreleasable one.
tasks.<name> no Custom tasks for trellis run. A task with a built-in's name (build, test, …) overrides it. needs-deps = true downloads dependencies first.
publish.tag-format no Template for per-member git tags. Default: {name}-v{version}.
publish.retry no Backoff for Hex rate limits: { attempts, initial-delay, multiplier }.
changelog.kinds no Change kinds and the version bump each implies. The largest bump among a package's unreleased fragments wins.
changelog.*-format no Minijinja templates for rendered changelog sections: version-format, kind-format, change-format.

Release exclusions

Some workspace members need the normal development loop without becoming published packages. Add their paths to ignore-release:

gleam.toml
[tools.trellis]
members = ["packages/*", "examples", "benchmarks/*"]
ignore-release = ["examples", "benchmarks/*"]

The patterns are globs matched against member paths relative to the workspace root, not package names. An excluded member stays in the dependency graph and still participates in run, exec, list, graph, and changed-package selection.

Surface Excluded member behavior
Tasks and graph Included normally, with dependencies and dependents intact.
Changelog and versioning Omitted from checks and plans. Explicit changelog creation is rejected.
Tags, publishing, and release CI Omitted from generated release sets. Explicit publishing is rejected.
Introspection info and JSON output expose releasable: false; list --releasable returns only the release set.
Terminal window
$ trellis list --releasable
lat_core
lat_mid
lat_cli

trellis doctor catches an exclusion glob that matches no member and rejects a releasable package that path-depends on an excluded one. A published package cannot depend on a workspace package that will never exist on Hex.

Changelog configuration

The changelog engine is native — no second tool to install, no config file to keep in sync. Changes live as TOML fragments in .changes/unreleased/; rendering is controlled by small minijinja templates, each with a context of name, version, date, tag, kind, and body as applicable:

[tools.trellis.changelog]
version-format = "## v{{ version }} - {{ date }}" # default
kind-format = "### {{ kind }}" # default
change-format = "- {{ body }}" # default
kinds = [
{ label = "Breaking", bump = "major" },
{ label = "Added", bump = "minor" },
{ label = "Fixed", bump = "patch" },
]

Each package's CHANGELOG.md is a generated file: the source of truth is the version sections stored under .changes/<package>/, and version apply reassembles the changelog from them, newest first.

Verifying it

Run trellis doctor after any config change. It validates that member globs resolve, the graph is acyclic, ignore-release globs match members, the tag format produces unique tags, and locked versions match — exiting non-zero on any error.