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
| Ecosystem | Package type | Discovery sources | Version and dependency updates | Lockfile behavior | Built-in registry publishing |
|---|---|---|---|---|---|
| Cargo | cargo | Cargo.toml workspaces and standalone crates | Cargo.toml package versions and internal dependency requirements | Direct Cargo.lock rewrite by default; configure cargo generate-lockfile, cargo check, or another command when you need package-manager resolution | crates.io |
| npm-family | npm | npm workspaces, pnpm workspaces, Bun workspaces, and standalone package.json packages | package.json versions and dependency ranges | Direct package-lock.json, pnpm-lock.yaml, bun.lock, and bun.lockb updates by default; command overrides support package-manager refreshes | npm |
| Deno | deno | Deno workspaces and standalone deno.json / deno.jsonc packages | Deno manifest versions, exports/imports metadata, and dependency references | Direct deno.lock update when possible; no inferred lockfile command | jsr |
| Dart / Flutter | dart, flutter | Dart and Flutter workspaces plus standalone pubspec.yaml packages | pubspec.yaml versions and dependency ranges | Direct pubspec.lock update by default; configure dart pub get or flutter pub get when you need full solver refreshes | pub.dev |
| Python | python | uv workspaces, Poetry projects, and standalone pyproject.toml packages | PEP 621 [project] and Poetry [tool.poetry] package versions plus dependency specifiers | Does not mutate uv.lock or poetry.lock directly; infers uv lock and poetry lock --no-update commands; unknown Python lockfiles are skipped | pypi |
| Go | go | Standalone go.mod modules | Internal require directives in go.mod; package versions stay in VCS tags | Does not mutate go.sum directly; infers go mod tidy so the Go toolchain refreshes go.mod and checksum data | Go module proxy via VCS tags |
| GitHub Actions | github_actions | Configured [package.<id>] declarations with action.yml (or action.yaml) in the package directory | None: releases are git tags; a sibling package.json version field is synced when present | Not applicable, no lockfiles | None: 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_filesentries, 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.lockis 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.jsonpackages - internal workspace dependencies that use npm-compatible version ranges
npm-family behavior:
- package ids come from
package.jsonnames - internal dependency ranges default to the
^prefix dependencies,devDependencies, andpeerDependenciesparticipate in dependency updatesmonochange versions synccan repair npm internal dependency ranges outside a release while preservingworkspace:*protocol references- direct lockfile support covers
package-lock.json,pnpm-lock.yaml,bun.lock, andbun.lockb - built-in publishing targets the public
npmregistry - GitHub npm trusted-publishing diagnostics are built in; registry-side enrollment stays manual or external, and trusted npm publishes use the
npmCLI 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.jsonordeno.jsoncpackage manifests- Deno workspace members
importsentries that connect internal packages- packages published to
jsr
Deno behavior:
- internal dependency ranges default to
^ importsare the primary dependency fielddeno.lockcan 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
fluttersection - Dart or Flutter workspace layouts
- packages published to
pub.dev
Dart / Flutter behavior:
- package type is canonically
dartfor both pure Dart and Flutter packages; Flutter packages are detected frompubspec.yamlmetadata and published withflutter pub publishwhen appropriate - internal dependency ranges default to
^ dependenciesanddev_dependenciesparticipate in dependency updatesmonochange versions synccan repair Dart internal dependency ranges outside a release, including converting internalpath:references to version constraints whenresolution: workspaceis enabledpubspec.lockcan be rewritten directly by default- configure
dart pub getorflutter pub getas 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:
- If the repository root has a
pyproject.tomlwith uv workspace members, monochange expands the member globs and reads each member manifest. - monochange then scans for standalone
pyproject.tomlfiles 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
dependenciesare runtime dependencies - PEP 621
optional-dependenciesare development/optional dependency edges for release-planning purposes - Poetry
dependenciesare runtime dependencies, except the specialpythonconstraint is skipped - Poetry dependency groups under
[tool.poetry.group.<name>.dependencies]are development dependencies - release preparation rewrites
pyproject.tomlpackage versions and matching dependency specifiers while preserving extras such ashttpx[cli]
Python lockfile behavior is command-based by design:
uv.lockinfersuv lockpoetry.lockinferspoetry lock --no-update- unknown Python lockfile names are ignored rather than guessed
- configuring
[ecosystems.python].lockfile_commandsoverrides 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
moduledirective - internal dependency ranges default to exact Go module versions with a leading
v, matching Go module semantics requiredirectives participate in dependency updates, including groupedrequire (...)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
prepareandpreviewcan plan Go releases without a manifest version field go.sumis treated as checksum data, not as a lockfile to patch directly- monochange infers
go mod tidywhengo.mod/go.sumchanges need package-manager refreshes - built-in publishing creates VCS tags: root modules use
v1.2.3, while submodules use path-prefixed tags such asapi/v1.2.3 - readiness and publish checks query the Go module proxy for
<module>/@v/<version>.infovisibility
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, andinitial_version = "0.1.0"; every field can be overridden explicitly - discovery reads the package directory and synthesizes the release identity from
action.yml(oraction.yaml); the manifest itself carries no version - a sibling
package.jsonversionfield is synced to the released version when present; setignore_ecosystem_versioned_files = trueor declare explicitversioned_filesto control this - release tags follow
version_format; multi-action repos should keep the defaultnamespacedformat so tags look likemy-action/v1.2.3 - floating tag aliases (
floating_tags) move to each release, reproducing the commonv1.2/v1moving 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.