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
$trellis graph
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
Real output. The graph is computed from path dependencies — never declared.

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.

gleam.toml
# 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/main maps 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 doctor that 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.

  1. 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
  2. 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
  3. 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.

$trellis doctor
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.