Gleam has no workspace concept. Trellis is it.
One binary gives a Gleam monorepo task fan-out, dependency-graph
introspection, changelogs, versioning, and publish orchestration —
all derived from the gleam.toml files you already have.
$curl --proto '=https' --tlsv1.2 -LsSf https://github.com/tylerbutler/trellis/releases/latest/download/trellis-installer.sh | sh lat_core (1.2.0)
lat_mid (0.5.0)
└─ lat_core
lat_cli (0.3.1)
├─ lat_core
└─ lat_mid
examples (0.0.0)
└─ lat_cli You're maintaining glue by hand.
Without workspaces, every multi-package Gleam repo rebuilds the same machinery out of bash loops, YAML blocks, and duplicated config. This ledger is from a real workspace, before trellis:
| Where it lives | What's maintained by hand | How it fails |
|---|---|---|
justfile | The package list, in topological order, as a space-separated string | A new package is silently excluded from every recipe |
justfile | ~15 near-identical for pkg in … bash loops | Copy-paste drift between recipes; strictly serial execution |
.changie.yaml | One config block per package: label, key, changelog path, version regex | A forgotten block means a package can't be versioned or released |
publish.yml | A hand-written mirror of the path-dependency graph | Nothing verifies it when a package gains a dependency |
release.yml | 25 lines of inline sed/grep patching locked versions | Untestable regex logic living inside CI YAML |
Every row in that table is derivable from one file format the ecosystem already uses.
Configure nothing that can be derived.
Verify anything that must be duplicated.
# This table marks the workspace root.# Only `members` is required.[tools.trellis]members = ["packages/*", "examples"]ignore-release = ["examples"]
[tools.trellis.tasks.lint]command = "gleam run -m glinter"
[tools.trellis.publish]tag-format = "{name}-v{version}"From that, trellis computes:
- The member set and its topological order — new packages join every command the moment their directory matches a glob.
- Change impact —
--since origin/mainmaps a diff to owning packages, plus the reverse-dependency closure. Only test what a PR touched. - Publish order and path-dep rewrites — dependencies publish first, path references become Hex versions automatically.
- A tag per releasable member — and a
doctorthat fails loudly when anything drifts.
One binary, the whole lifecycle.
The same tool runs locally and in CI. Commands work from anywhere in
the workspace — the root is found by walking up, like
git or cargo.
-
Every day
run- Run a task across members, graph-parallel by default
list- Members in topological order — dependencies first
graph- The dependency graph, as text, DOT, Mermaid, or JSON
exec- Any command, in each member directory
doctor- Validate workspace invariants; non-zero exit on any error
-
Cut a release
changelog- Native fragment engine — no second tool to install
version- Plan and apply bumps from unreleased fragments
tag- Compare versions against git tags; create what's missing
publish- To Hex, in dependency order, path deps rewritten
lockfile- Patch locked versions without touching the network
-
In CI
ci- Structured JSON for GitHub Actions matrices and outputs
changelog check- Fail a PR that changes a package without logging it
release pr- Open the release pull request, versions applied
It fails loudly on drift.
Some things can't be derived — locked versions, changelogs, tags,
toolchain pins. For those, trellis doctor verifies every
invariant that used to be enforced by hope, and exits non-zero the
moment one breaks.
checked: member globs resolve and every member has a parseable gleam.toml
checked: path dependencies stay inside the workspace; graph is acyclic
checked: ignore-release globs match members; no releasable member depends on an unreleasable one
checked: tag format produces a unique tag per releasable member
checked: manifest.toml locked versions match workspace-internal gleam.toml versions
checked: each releasable member's version is not behind its CHANGELOG
checked: unreleased changelog fragments parse and reference valid packages and kinds
checked: gleam on PATH matches the .tool-versions pin (advisory)
ok: 4 member(s), 0 warning(s) Install it in a second.
One prebuilt binary — the same distribution model as
just and ratchet. Zero runtime
dependencies, SLSA build provenance, installs in CI in about a second.
- Shell (Linux, macOS)
-
$curl --proto '=https' --tlsv1.2 -LsSf https://github.com/tylerbutler/trellis/releases/latest/download/trellis-installer.sh | sh - Homebrew
-
$brew install tylerbutler/tap/trellis - mise / asdf (pin it in .tool-versions)
-
$mise use "ubi:tylerbutler/trellis@0.1.0" - From source
-
$cargo install --git https://github.com/tylerbutler/trellis
Windows installer and pinned-version URLs are on the releases page. Then start with the configuration guide — it's one TOML table.