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 / ecosystem | Manifests read and rewritten |
|---|---|
| JavaScript, TypeScript: npm, pnpm, Yarn | package.json |
| Go | go.mod |
| Rust: Cargo | Cargo.toml |
| Python: PEP 621, PEP 735, Poetry, pip | pyproject.toml, requirements*.txt |
| PHP: Composer | composer.json |
| Java, Kotlin, Scala: Maven | pom.xml |
| C#, F#, VB: .NET, NuGet | *.csproj, *.fsproj, *.vbproj, *.nuspec, Directory.Packages.props, packages.config |
| Dart, Flutter: pub | pubspec.yaml |
| Ruby: Bundler, RubyGems | Gemfile, *.gemspec |
| Swift, Objective-C: iOS, CocoaPods | Info.plist, project.pbxproj, Podfile, *.podspec |
| Kotlin, Java: Android, Gradle | AndroidManifest.xml, libs.versions.toml, build.gradle(.kts) |
| Docker: images and Compose | Dockerfile, Containerfile, compose.yaml, docker-compose.yml, and their .override spellings |
| Unity | Packages/manifest.json, ProjectSettings/ProjectSettings.asset |
| Godot | project.godot, plugin.cfg, export_presets.cfg |
| Unreal Engine | *.uproject, *.uplugin, Config/DefaultGame.ini, Config/DefaultEngine.ini |
| Defold | game.project |
| O3DE | project.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.
curl -fsSL https://raw.githubusercontent.com/yohimik/dispat/main/install.sh | sh
wget -qO- https://raw.githubusercontent.com/yohimik/dispat/main/install.sh | sh
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
- Getting started: install the binary, write one config file, wire the release into CI.
- Concepts: packages and spaces, propagation, the plan, the failure and recovery model.
- Examples: a complete setup per package manager, npm to Docker to Android, and editing every package at once.
- CLI and Configuration: every command, every option.
- Go packages: the commit parser, the manifest reader and the manifest writer, importable on their own.
- Commit messages: the Conventional Commits superset that carries release intent.
- Releasing: shared versions, running the release's own steps yourself, releasing part of the graph, and the release lock.
- Script environment: the
DISPAT_*variables a stage receives. - Architecture, Coverage and Test results: how it is built, and what its test suite reaches and does.
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.