Skip to main content
dispat logo

dispat

tests workflow statusstatement coverage

dispat is a release tool for polyglot monorepos. It reads your conventional commits to find which packages changed and calculates their next semantic versions. Then it propagates those bumps to dependants and builds every package in dependency order. Finally, dispat publishes them in parallel, writing changelogs, git tags, and GitHub releases on the way out.

Polyglot is the point. A package is a folder and a stage is a shell command. This means npm, Go, Cargo, Maven, .NET, Python, Ruby, Dart, Docker, iOS, and Android sit in one dependency graph and release together.

You install one binary and write one config file. You run no daemon, manage no state file, and operate no cache.

libraries

Lightweight libraries, usable on their own

Parsing commit messages and reading and rewriting dependency manifests are problems far older than releases, so dispat keeps all three as standalone Go modules with no dependency on the CLI, on git or on a network. The manifest pair shares its vocabulary through pkg/manifest (dependency kinds, manifest file-name rules, PEP 503 normalisation) so the reader and the writer can never drift apart.

pkg/ccme: the commit parser

Conventional Commits, Monorepo Extension: a strict superset of Conventional Commits 1.0.0 that adds scopes as packages, propagation depth and prerelease channels. No regular expressions: one left-to-right index scan with a byte of lookahead, no backtracking, no recursion, O(n) time and O(1) working space, which is what matters when the input is untrusted commit messages in CI. The specification is vendored beside it as SPEC.md, and every section reference in the code points into it.

pkg/scanner: the manifest reader

Thin per-format parsers turning every manifest below into one ecosystem-neutral shape: declared identity, dependencies, ranges and local-path signals. No SBOM machinery, no lockfile resolution, no network; bounded reads, deterministic order, and a partial result even when one file fails to parse.

pkg/writer: the manifest writer

Format-preserving in-place edits for every manifest the scanner reads: only the version text being changed is replaced, and every other byte (indentation, key order, comments) survives verbatim. Writes are atomic (temp file, fsync, rename) and skipped when nothing changed, and the result separates what was applied from what was deliberately left alone, such as a value that defers to a Maven property or a workspace inheritance.

Languages and manifests the reader and the writer support

Language / ecosystemManifests read and rewritten
JavaScript, TypeScript: npm, pnpm, Yarnpackage.json
Gogo.mod
Rust: CargoCargo.toml
Python: PEP 621, PEP 735, Poetry, pippyproject.toml, requirements*.txt
PHP: Composercomposer.json
Java, Kotlin, Scala: Mavenpom.xml
C#, F#, VB: .NET, NuGet*.csproj, *.fsproj, *.vbproj, *.nuspec, Directory.Packages.props, packages.config
Dart, Flutter: pubpubspec.yaml
Ruby: Bundler, RubyGemsGemfile, *.gemspec
Swift, Objective-C: iOS, CocoaPodsInfo.plist, project.pbxproj, Podfile, *.podspec
Kotlin, Java: Android, GradleAndroidManifest.xml, libs.versions.toml, build.gradle(.kts)
Docker: images and ComposeDockerfile, Containerfile, compose.yaml, docker-compose.yml, and their .override spellings
UnityPackages/manifest.json, ProjectSettings/ProjectSettings.asset
Godotproject.godot, plugin.cfg, export_presets.cfg
Unreal Engine*.uproject, *.uplugin, Config/DefaultGame.ini, Config/DefaultEngine.ini
Defoldgame.project
O3DEproject.json, gem.json

The mobile formats also carry a build number beside their marketing version (CFBundleVersion, android:versionCode, CURRENT_PROJECT_VERSION): the scanner reads it, no version write ever moves it, and --set-build is the write that does. dispat compute derives a monorepo's dependency graph from these files, and autoVersion rewrites them at the version stage.

install

Install

One command, and no runtime to install first. The script downloads the binary for your platform, checks it against the checksum GitHub published, and puts it on your PATH.

Linux and macOS
curl -fsSL https://raw.githubusercontent.com/yohimik/dispat/main/install.sh | sh
Linux and macOS, with wget
wget -qO- https://raw.githubusercontent.com/yohimik/dispat/main/install.sh | sh
Windows
irm https://raw.githubusercontent.com/yohimik/dispat/main/install.ps1 | iex

After that the binary keeps itself current: dispat self-update replaces it with the latest release and keeps the old one beside it for a week in case you want it back. Every command mentions a newer release on its way out, so you find out without going looking.

More ways to install (go install, the GitHub Action, the container images) and how to pin a version are in Getting started.

in production

Projects using dispat

The first monorepo dispat releases is its own: every tag, changelog, GitHub release and container image of this project, and this documentation site, ship through a dispat run.

dispat itself: this repository is a polyglot Go, npm, and Docker workspace. It is released by the dispat binary built from its own checkout, and it has been since the very first release. The first stable run cut eleven packages in one release. It rewrote the go.mod files of six Go modules to the released versions, regenerated go.sum files, and tagged each module the way Go expects (pkg/ccme/v1.0.0, services/dispat/v1.0.0). This keeps go install github.com/yohimik/dispat/services/dispat@latest working. It also attached six cross-compiled binaries to the CLI's GitHub release, published the four container images, and released the versioned documentation site. Every package received its own tag, changelog entry, and GitHub release.

webxash3d-fwgs: a WebAssembly port of the Xash3D-FWGS game engine. It uses a real docker on docker on npm provider chain, four levels deep. It runs parallel builds from the engine package up to the modded server image.

read on

The documentation

lineage

Inspiration

dispat stands on the shoulders of two things:

  • Lerna, and the workspaces of npm and pnpm it grew up beside. Between them they proved that many packages in one repository can share a dependency graph. They also proved that versioning and publishing all of them can be one command. dispat takes that idea beyond JavaScript and rebuilds it around an explicit dependency graph and an explicit error model.
  • Conventional Commits: commit messages as machine-readable release intent. The dispat parser, pkg/ccme, implements a strict superset of Conventional Commits 1.0.0 that adds the monorepo dimension. It treats scopes as packages and handles propagation depth and prerelease channels.

community

Have questions or issues?

Want to share a project you release with dispat? Come and say hello on Discord. Bugs and feature requests are welcome as GitHub issues too, whichever suits you better.