Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Ecosystems

monochange uses ecosystem adapters to translate native package-manager files into one release-planning model. Each adapter answers the same questions:

  • which package manifests exist in the repository?
  • which packages depend on other packages?
  • where should a package version and internal dependency references be rewritten during a release?
  • should lockfiles be rewritten directly, refreshed with a command, or left to external tooling?
  • can monochange publish the package directly, or should publication stay external?

Capability matrix

EcosystemPackage typeDiscovery sourcesVersion and dependency updatesLockfile behaviorBuilt-in registry publishing
CargocargoCargo.toml workspaces and standalone cratesCargo.toml package versions and internal dependency requirementsDirect Cargo.lock rewrite by default; configure cargo generate-lockfile, cargo check, or another command when you need package-manager resolutioncrates.io
npm-familynpmnpm workspaces, pnpm workspaces, Bun workspaces, and standalone package.json packagespackage.json versions and dependency rangesDirect package-lock.json, pnpm-lock.yaml, bun.lock, and bun.lockb updates by default; command overrides support package-manager refreshesnpm
DenodenoDeno workspaces and standalone deno.json / deno.jsonc packagesDeno manifest versions, exports/imports metadata, and dependency referencesDirect deno.lock update when possible; no inferred lockfile commandjsr
Dart / Flutterdart, flutterDart and Flutter workspaces plus standalone pubspec.yaml packagespubspec.yaml versions and dependency rangesDirect pubspec.lock update by default; configure dart pub get or flutter pub get when you need full solver refreshespub.dev
Pythonpythonuv workspaces, Poetry projects, and standalone pyproject.toml packagesPEP 621 [project] and Poetry [tool.poetry] package versions plus dependency specifiersDoes not mutate uv.lock or poetry.lock directly; infers uv lock and poetry lock --no-update commands; unknown Python lockfiles are skippedpypi
GogoStandalone go.mod modulesInternal require directives in go.mod; package versions stay in VCS tagsDoes not mutate go.sum directly; infers go mod tidy so the Go toolchain refreshes go.mod and checksum dataGo module proxy via VCS tags
GitHub Actionsgithub_actionsConfigured [package.<id>] declarations with action.yml (or action.yaml) in the package directoryNone: releases are git tags; a sibling package.json version field is synced when presentNot applicable, no lockfilesNone: the tag and GitHub release are the publish

The built-in publishing column is intentionally narrower than release planning. It lists only the canonical public registry for each supported ecosystem; private registries and custom publication flows should use mode = "external".

Shared behavior across ecosystems

All supported ecosystems feed the same planner. After discovery, monochange can:

  • render package ids and manifest paths relative to the repository root
  • normalize dependency edges into one graph
  • apply [group.<id>] version synchronization rules
  • propagate dependent bumps through internal dependency edges
  • update native manifests during monochange run release
  • update extra versioned_files entries, including regex-managed files
  • render changelogs and release notes from .changeset/*.md
  • create durable release records and post-merge tags

[ecosystems.<name>] configuration controls settings such as dependency-version prefixes, extra versioned files, publish defaults, and lockfile commands. Discovery still scans every supported ecosystem regardless of [ecosystems.*].enabled, roots, or exclude toggles.

Syncing internal dependency versions

monochange versions sync updates existing internal dependency references outside a release. (The bare monochange versions alias is deprecated.) It discovers workspace package versions, finds supported manifests that reference another workspace package, and rewrites those references to the canonical package version. Use --dry-run first to print the planned edits without writing files.

monochange versions sync --dry-run
monochange versions sync --strategy exact

The --strategy flag accepts default, exact, caret, or compatible. default uses each supported ecosystem’s normal constraint style; for monochange versions that means each ecosystem’s configured or default constraint style. Dart version sync scans dependencies, dev_dependencies, and dependency_overrides; when a pubspec uses resolution: workspace, path references to internal packages are converted to version constraints. npm version sync scans package dependency sections and leaves workspace:* protocol references alone. Cargo, Deno, Go, and Python manifests are also rewritten when they reference another workspace package.

Internal dependency versions lists the exact constraint each strategy writes per ecosystem. versions sync cannot write custom prefixes such as ~ or =; to stamp internal dependency references with those, use a typed versioned_files entry with an explicit prefix, or set [ecosystems.<name>] dependency_version_prefix to change the prefix typed versioned files write by default (see Versioned files).

Cargo

Cargo support is designed for Rust crates that keep version data in Cargo.toml and dependency resolution in Cargo.lock.

Use Cargo support when your repository has:

  • a root Cargo workspace with members
  • standalone crates outside a workspace
  • internal crate dependencies that should move together when one crate is released
  • crates published to crates.io

Cargo-specific behavior:

  • package ids come from each crate manifest
  • dependency references use Cargo’s native requirement style; the default dependency version prefix is empty
  • Cargo.lock is updated directly by default for fast release preparation
  • incomplete or complex lockfile cases can be delegated to explicit lockfile commands
  • built-in publishing targets crates.io
  • publish readiness validates common crates.io requirements, including publish, description, and license metadata

npm, pnpm, and Bun

The npm-family adapter covers JavaScript and TypeScript packages that share package.json as their manifest format.

Use npm-family support when your repository has:

  • npm workspaces declared in package.json
  • pnpm workspaces declared in pnpm-workspace.yaml
  • Bun workspaces and Bun lockfiles
  • standalone package.json packages
  • internal workspace dependencies that use npm-compatible version ranges

npm-family behavior:

  • package ids come from package.json names
  • internal dependency ranges default to the ^ prefix
  • dependencies, devDependencies, and peerDependencies participate in dependency updates
  • monochange versions sync can repair npm internal dependency ranges outside a release while preserving workspace:* protocol references
  • direct lockfile support covers package-lock.json, pnpm-lock.yaml, bun.lock, and bun.lockb
  • built-in publishing targets the public npm registry
  • GitHub npm trusted-publishing diagnostics are built in; registry-side enrollment stays manual or external, and trusted npm publishes use the npm CLI directly

Deno

Deno support is for packages described by deno.json or deno.jsonc, including workspaces that publish to JSR.

Use Deno support when your repository has:

  • deno.json or deno.jsonc package manifests
  • Deno workspace members
  • imports entries that connect internal packages
  • packages published to jsr

Deno behavior:

  • internal dependency ranges default to ^
  • imports are the primary dependency field
  • deno.lock can be updated directly when present
  • monochange does not infer a default Deno lockfile command; configure one if your release flow needs deno cache, deno task, or another resolver step
  • built-in publishing targets jsr

Dart and Flutter

Dart and Flutter share the canonical dart ecosystem settings because both use pubspec.yaml and Pub’s version constraints. The legacy flutter config and CLI filter spelling is still accepted as an alias, but normalized package records use the Dart ecosystem with Flutter metadata.

Use Dart / Flutter support when your repository has:

  • pure Dart packages
  • Flutter packages with a flutter section
  • Dart or Flutter workspace layouts
  • packages published to pub.dev

Dart / Flutter behavior:

  • package type is canonically dart for both pure Dart and Flutter packages; Flutter packages are detected from pubspec.yaml metadata and published with flutter pub publish when appropriate
  • internal dependency ranges default to ^
  • dependencies and dev_dependencies participate in dependency updates
  • monochange versions sync can repair Dart internal dependency ranges outside a release, including converting internal path: references to version constraints when resolution: workspace is enabled
  • pubspec.lock can be rewritten directly by default
  • configure dart pub get or flutter pub get as lockfile commands when you need the Pub solver to refresh files instead of the direct updater
  • built-in publishing targets pub.dev

Python

Python support is centered on pyproject.toml. It covers modern PEP 621 projects, Poetry projects, uv workspaces, and standalone packages discovered by scanning for manifests.

Use Python support when your repository has:

  • a uv workspace declared under [tool.uv.workspace]
  • PEP 621 package metadata under [project]
  • Poetry package metadata under [tool.poetry]
  • standalone Python packages with pyproject.toml
  • internal dependencies that should receive version bumps alongside released workspace packages

Python discovery works in two passes:

  1. If the repository root has a pyproject.toml with uv workspace members, monochange expands the member globs and reads each member manifest.
  2. monochange then scans for standalone pyproject.toml files that were not already included by the uv workspace pass.

When a manifest has both PEP 621 and Poetry metadata, monochange prefers [project]. If [project].dynamic contains "version", monochange treats the package version as dynamic and does not rewrite the version field.

Python version and dependency behavior:

  • package names and dependency names are normalized using Python’s PEP 503 style normalization for dependency graph matching
  • PEP 440 versions are parsed into the shared semantic-version model when possible
  • internal dependency ranges default to the >= prefix
  • PEP 621 dependencies are runtime dependencies
  • PEP 621 optional-dependencies are development/optional dependency edges for release-planning purposes
  • Poetry dependencies are runtime dependencies, except the special python constraint is skipped
  • Poetry dependency groups under [tool.poetry.group.<name>.dependencies] are development dependencies
  • release preparation rewrites pyproject.toml package versions and matching dependency specifiers while preserving extras such as httpx[cli]

Python lockfile behavior is command-based by design:

  • uv.lock infers uv lock
  • poetry.lock infers poetry lock --no-update
  • unknown Python lockfile names are ignored rather than guessed
  • configuring [ecosystems.python].lockfile_commands overrides the inferred commands

Built-in Python publishing targets PyPI. monochange builds Python artifacts with uv build --out-dir dist and publishes them with uv publish, using --trusted-publishing always when trusted publishing is enabled and --trusted-publishing never otherwise. Placeholder publishing creates a minimal Hatchling project with a normalized module directory under src/.

Example Python package configuration:

[package.api]
path = "services/api"
type = "python"
changelog = true

[package.api.publish]
enabled = true
mode = "builtin"
registry = "pypi"
trusted_publishing = true

[ecosystems.python]
dependency_version_prefix = ">="
# Optional: override inferred uv/Poetry lockfile commands.
lockfile_commands = [{ command = "uv lock", cwd = "." }]

Go

Go support is centered on go.mod files. Go module versions live in VCS tags rather than manifest fields, so monochange updates internal require directives and records enough metadata for tag-based publishing.

Use Go support when your repository has:

  • one or more modules declared by go.mod
  • multi-module layouts where submodules need path-prefixed tags such as api/v1.2.3
  • internal module dependencies that should receive version bumps alongside released workspace modules
  • packages published through normal Go module proxy discovery

Go behavior:

  • package ids come from the module path in the module directive
  • internal dependency ranges default to exact Go module versions with a leading v, matching Go module semantics
  • require directives participate in dependency updates, including grouped require (...) blocks
  • Go v2+ semantic import versioning remains encoded in module paths, not a separate manifest version field
  • release planning resolves each module’s current version from its latest release tag, so prepare and preview can plan Go releases without a manifest version field
  • go.sum is treated as checksum data, not as a lockfile to patch directly
  • monochange infers go mod tidy when go.mod / go.sum changes need package-manager refreshes
  • built-in publishing creates VCS tags: root modules use v1.2.3, while submodules use path-prefixed tags such as api/v1.2.3
  • readiness and publish checks query the Go module proxy for <module>/@v/<version>.info visibility

Example Go package configuration:

[package.api]
path = "services/api"
type = "go"
changelog = true

[package.api.publish]
enabled = true
mode = "builtin"
registry = "go_proxy"
trusted_publishing = false

[ecosystems.go]
# Optional: override inferred tidy commands.
lockfile_commands = [{ command = "go mod tidy", cwd = "services/api" }]

GitHub Actions

type = "github_actions" targets repositories released as a git tag plus a provider release. GitHub Actions can be consumed as npm packages, Docker images, composites, or plain scripts, so there is no registry publish step: the tag and the GitHub release are the publish.

Example configuration:

[package.actions]
path = "."
type = "github_actions"
version_format = "primary"

[source]
provider = "github"
owner = "acme"
repo = "actions"

[source.releases]
enabled = true
source = "monochange"

GitHub Actions behavior:

  • the type preset implies version_source = "tag", tag = true, release = true, publish.enabled = false, and initial_version = "0.1.0"; every field can be overridden explicitly
  • discovery reads the package directory and synthesizes the release identity from action.yml (or action.yaml); the manifest itself carries no version
  • a sibling package.json version field is synced to the released version when present; set ignore_ecosystem_versioned_files = true or declare explicit versioned_files to control this
  • release tags follow version_format; multi-action repos should keep the default namespaced format so tags look like my-action/v1.2.3
  • floating tag aliases (floating_tags) move to each release, reproducing the common v1.2 / v1 moving tags

Choosing external publishing

Use mode = "external" when an ecosystem or registry is not handled by monochange’s built-in publisher, or when your organization needs custom signing, provenance, approval, rate-limit, private-registry behavior, a Python publishing toolchain other than the built-in uv build / uv publish flow, or a Go publishing workflow that signs, pushes, or annotates tags outside monochange.

That keeps the package in release planning while leaving upload mechanics to your existing publishing workflow.