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 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:
[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.
|
$ trellis list --releasablelat_corelat_midlat_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 }}" # defaultkind-format = "### {{ kind }}" # defaultchange-format = "- {{ body }}" # defaultkinds = [ { 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.