Latest releasev1.0.0Release notes

Workspace tooling for Gleam monorepos.Derived from gleam.toml.

Run tasks across your Gleam packages, inspect their dependencies, and manage changelogs, versions, and publishing from one binary.

$curl --proto '=https' --tlsv1.2 -LsSf https://github.com/tylerbutler/trellis/releases/latest/download/trellis-gleam-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
package_a (0.0.0)
  └─ lat_cli
Output from the example workspace. Trellis computes the graph from path dependencies.

The glue this replaces.

The lattice inventory records the bash loops, YAML blocks, and duplicated configuration below. Each row is workspace information that was maintained by hand.

Where it livesWhat's maintained by handWhat can drift
justfileThe package list, in topological order, as a space-separated stringA new package is silently excluded from every recipe
justfile~15 near-identical for pkg in … bash loopsCopy-paste drift between recipes; strictly serial execution
.changie.yamlOne config block per package: label, key, changelog path, version regexA forgotten block means a package can't be versioned or released
publish.ymlA hand-written mirror of the path_dependency graphNothing verifies it when a package gains a dependency
release.yml25 lines of inline sed/grep patching locked versionsUntestable regex logic living inside CI YAML

Trellis derives the information behind each row from gleam.toml, a file format the ecosystem already uses.

Configure nothing that can be derived.
Verify anything that must be duplicated.

gleam.toml
# The table's presence marks the workspace root.
# Every key is optional — members auto-discover from git.
[tools.trellis]
members = ["packages/*", "examples/*"]
# Exclusions are scoped by task. `@release` covers changelog,
# versioning, tagging, publishing, and release CI.
exclude = { docs = ["examples/*"], "@release" = ["examples/*"] }
[tools.trellis.tasks.lint]
command = "gleam run -m glinter"
[tools.trellis.publish]
exact_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, and path references become Hex versions automatically.
  • A tag per releasable package, plus a doctor that fails loudly when anything drifts.

Opinionated, but modular.

Trellis has one way of doing each job: TOML change fragments, a tag per package, bumps derived from fragment kinds. But the jobs are independent, and nothing requires the piece above it, so you adopt a layer at a time and keep whatever already works.

  1. Just the task runner

    run, exec, list, and graph need no configuration at all, because members are auto-discovered from git. Your changelog tool and CI stay untouched.

  2. Add changelogs and versioning

    Fragments, version plan, and version apply manage bumps and changelogs without trellis owning your release workflow, or any CI at all.

  3. Add publishing

    tag and publish push to Hex in dependency order, from your own scripts or workflows.

  4. The full pipeline

    ci, changelog check, and release pr turn GitHub Actions workflows into thin triggers around trellis commands.

One workspace. Different task sets.

Examples, benchmarks, and internal tools can remain workspace members for builds, tests, and graphing without needing generated docs or a release on Hex.

exclude gives each built-in or custom task its own member-path globs. The reserved @release key covers changelogs, versioning, tags, publishing, and release CI.

Configure package exclusions →
Memberbuild / testdocsrelease
packages/*includedincludedincluded
examples/*includedexcludedexcluded
$trellis list --releasable
lat_core  hex
lat_mid   hex
lat_cli   hex
package_a — the examples/ member — still appears in trellis list, graph, exec, and tasks that do not exclude it. Only the configured task sets change.

One binary across the lifecycle.

The same tool runs locally and in CI. Commands work from anywhere in the workspace, since the root is found by walking up, like git or cargo.

  1. Run tasks across packages

    Build, test, and run custom tasks in dependency order. Select the packages affected by a change, locally or in CI.

    Read the task-running guide →
  2. Plan versions and changelogs

    Record changes as TOML fragments. Review the version plan before applying package bumps and generated changelogs.

    Read the versioning guide →
  3. Automate release workflows

    Generate GitHub Actions matrices, check changelog coverage, and open release pull requests from the same workspace model.

    Read the CI recipes →

Checks for drift.

Some things can't be derived: locked versions, changelogs, tags, toolchain pins. For those, trellis doctor checks the invariants that remain explicit and exits non-zero when one breaks.

$trellis doctor
checked: member globs resolve and every package has a parseable gleam.toml
checked: path dependencies stay inside the workspace; graph is acyclic
checked: task exclusion globs match members; no package depends on one unavailable at its release lifecycle
checked: tag format produces a unique tag per releasable package
checked: manifest.toml locked versions match workspace-internal gleam.toml versions
checked: each releasable package's version is not behind its CHANGELOG
checked: unreleased changelog fragments parse and reference valid packages, kinds, and categories
checked: [tools.trellis] has no unrecognized keys
checked: packages agree on the external dependencies they share
checked: gleam on PATH matches the .tool-versions pin (advisory)

ok: 4 package(s) (1 workspace, 0 git_only, 3 hex), 0 warning(s)

Install a prebuilt binary.

Trellis has no runtime dependencies and includes SLSA build provenance. Its shell installer typically completes in about a second in CI.

Shell (Linux, macOS)
$curl --proto '=https' --tlsv1.2 -LsSf https://github.com/tylerbutler/trellis/releases/latest/download/trellis-gleam-installer.sh | sh
Homebrew
$brew install tylerbutler/tap/trellis
mise / asdf (pin it in .tool-versions)
$mise use "github:tylerbutler/trellis@1.0.0"
From source
$cargo install --git https://github.com/tylerbutler/trellis

Windows installer and pinned-version URLs are on thereleases page. From there, trellis doctor works immediately, with no setup. Run trellis init only when you want to configure something; the configuration guide covers what it writes.