Your first release plan
Use this guide after installation when you want one local, beginner-safe walkthrough.
You will stop at monochange run release --dry-run --format json, so nothing is published.
1. Generate a starter config with monochange init
Run this at the repository root:
monochange init
monochange init detects packages, writes an annotated monochange.toml, and gives you a better starting point than hand-authoring a first config from scratch.
The generated file is intentionally minimal and does not create default [cli.*] workflow aliases. Every built-in step is available directly as an immutable monochange step * command, for example monochange step discover, monochange step create-change-file, and monochange step prepare-release.
Add [cli.*] tables only when you want repository-specific named workflows that chain steps, expose custom inputs, or run shell Command steps.
Commit .monochange/, gitignore only .monochange/local/
monochange writes committed state under .monochange/. The most important file is the release record at .monochange/releases/<id>/release.json, which CommitRelease writes and later publish-readiness, tag-release, and provider release automation read from git history. Prerelease state in .monochange/prerelease-state.json is committed too.
Only the .monochange/local/ directory holds local artifacts. Never add .monochange/ as a whole to .gitignore: ignoring it hides new release records from git and makes releases unpublishable. monochange keeps local state out of git status by adding .monochange/local/ to .git/info/exclude automatically when it writes a local artifact, so local runs should either use the default .monochange/local/ paths or write artifacts to a temporary directory.
Automated CI setup with --provider
When you know which source provider you will use for release automation, include the --provider flag during initialization:
monochange init --provider github
The --provider flag supports github, gitlab, and gitea. When provided, monochange init:
- Configures the
[source]section - adds provider-specific settings for releases and pull/merge requests - Generates provider CLI commands - includes
commit-releaseandrelease-prcommands inmonochange.toml - Creates workflow files (GitHub only) - writes
.github/workflows/release.ymland.github/workflows/changeset-policy.yml - Auto-detects owner/repo - parses
git remote get-url originto pre-populate[source]
Example generated configuration with --provider github:
[source]
provider = "github"
owner = "ifiokjr" # auto-detected from git remote
repo = "monochange" # auto-detected from git remote
[source.releases]
enabled = true
draft = false
prerelease = false
source = "monochange"
branches = ["main", "release/*"]
enforce_for_tags = true
enforce_for_publish = true
enforce_for_commit = false
changeset_context_timeout_seconds = 120
[source.pull_requests]
enabled = true
branch_prefix = "monochange/release"
base = "main"
title = "chore(release): prepare release"
labels = ["release", "automated"]
auto_merge = false
[cli.commit-release]
help_text = "Prepare a release and create a release commit"
[[cli.commit-release.steps]]
type = "PrepareRelease"
name = "plan release"
[[cli.commit-release.steps]]
type = "CommitRelease"
name = "create release commit"
[cli.release-pr]
help_text = "Prepare a release and open a release pull request"
[[cli.release-pr.steps]]
type = "PrepareRelease"
name = "plan release"
[[cli.release-pr.steps]]
type = "OpenReleaseRequest"
name = "open release PR"
The GitHub Actions workflows enable:
- Release automation -
release.ymlrefreshes the release PR on normalmainpushes, then tags and publishes when the merged release commit lands onmain - Changeset policy enforcement -
changeset-policy.ymlvalidates PRs have required changeset coverage
For GitLab and Gitea, the [source] section is configured but workflows are not generated (use their respective CI configuration files).
2. Validate the generated workspace
monochange step validate
This confirms that the generated config and any existing .changeset/*.md files agree with the workspace.
If validation fails, fix the reported problem first, then rerun monochange step validate.
3. Discover the package ids you will actually use
monochange step discover --format json
The most important thing to find in discovery output is the package id you want to target in your first change file.
If you are unsure what id to use later, rerun discovery and copy one from the output.
4. Create one change file
monochange run change --package <id> --bump patch --reason "describe the change"
Most changes should target a package id.
monochange will propagate bumps to dependents and synchronize configured groups for you, so group ids are best reserved for intentionally shared ownership.
5. Preview the release plan safely
monochange run release --dry-run --format json
This is the right stopping point for a first-time user.
You get a concrete preview of the release plan without publishing anything or opening provider requests.
When you are ready to move beyond planning:
- use
monochange step placeholder-publish --dry-run --format jsonif some packages still need a bootstrap0.0.0release so they exist in their registries first - use
monochange step publish-packages --dry-run --format jsonto preview built-in package publication tocrates.io,npm,jsr, orpub.dev - before real package publication, optionally write a readiness artifact with
monochange step publish-readiness --from HEAD --output .monochange/readiness.jsonfor preflight review, then runmonochange step publish-packages - use
monochange step publish-release --dry-run --format jsononly for hosted/provider releases such as GitHub releases
Package ids vs. group ids
Use this rule of thumb:
- package ids first: most authored changes belong to one package
- group ids later: use a group id only when the change is intentionally owned by the whole group
That keeps your first changes simple while still letting monochange synchronize grouped packages when needed.
First-failure recovery
monochange init says a config already exists
Keep the existing monochange.toml, inspect it, and continue with monochange step validate. If you want to regenerate the config from scratch, pass the --force flag:
monochange init --force
monochange step validate reports config or changeset errors
Fix the reported issue first. monochange step validate is the fastest way to get back to a known-good workspace.
monochange run change says the package id is unknown
Run monochange step discover --format json again and copy an id directly from the output.
You are not ready to hand-edit config yet
That is normal. Stay with the generated monochange.toml until the basic flow feels familiar.
When to edit monochange.toml by hand
Most first-time users should not start by writing a large config manually.
Reach for manual edits when you want to:
- rename or reorganize package ids
- define groups with
[group.<id>] - customize changelog paths or formats
- add provider configuration for release publishing or release PRs
- expand the CLI surface beyond the default generated commands
Reference: expanded configuration example
The example below shows the broader package, group, changelog, source-provider, and CLI-command model.
Use it as reference material after the generated config makes sense.
[defaults]
parent_bump = "patch"
warn_on_group_mismatch = true
package_type = "cargo"
[defaults.changelog]
path = "{{ path }}/changelog.md"
format = "keep_a_changelog"
[changelog]
templates = [
"#### {{ summary }}\n\n{{ details }}\n\n{{ context }}",
"#### {{ summary }}\n\n{{ context }}",
"#### {{ summary }}\n\n{{ details }}",
"- {{ summary }}",
]
[package.sdk-core]
path = "crates/sdk_core"
[package.sdk-core.changelog.types]
security = { bump = "patch", section = "Security" }
[package.web-sdk]
path = "packages/web-sdk"
type = "npm"
[package.mobile-sdk]
path = "packages/mobile-sdk"
type = "dart"
[group.sdk]
packages = ["sdk-core", "web-sdk", "mobile-sdk"]
tag = true
release = true
version_format = "primary"
[group.sdk.changelog]
path = "docs/sdk-changelog.md"
format = "monochange"
[source]
provider = "github"
owner = "ifiokjr"
repo = "monochange"
[source.releases]
source = "monochange"
[source.pull_requests]
branch_prefix = "monochange/release"
base = "main"
title = "chore(release): prepare release"
labels = ["release", "automated"]
auto_merge = false
[changesets.affected]
enabled = true
required = true
skip_labels = ["no-changeset-required"]
comment_on_failure = true
changed_paths = ["crates/**", "packages/**", "npm/**", "skills/**"]
ignored_paths = [
"docs/**",
"specs/**",
"readme.md",
"CONTRIBUTING.md",
"license",
]
[cli.discover]
help_text = "Discover packages across supported ecosystems"
[[cli.discover.inputs]]
name = "format"
type = "choice"
choices = ["text", "json", "json-min"]
default = "text"
[[cli.discover.steps]]
name = "discover packages"
type = "Discover"
inputs = ["format"]
[cli.change]
help_text = "Create a change file for one or more packages"
[[cli.change.inputs]]
name = "interactive"
type = "boolean"
short = "i"
[[cli.change.inputs]]
name = "package"
type = "string_list"
[[cli.change.inputs]]
name = "bump"
type = "choice"
choices = ["none", "patch", "minor", "major"]
default = "patch"
[[cli.change.inputs]]
name = "version"
type = "string"
[[cli.change.inputs]]
name = "reason"
type = "string"
[[cli.change.inputs]]
name = "type"
type = "string"
[[cli.change.inputs]]
name = "details"
type = "string"
[[cli.change.inputs]]
name = "output"
type = "path"
[[cli.change.steps]]
name = "create change file"
type = "CreateChangeFile"
inputs = ["interactive", "package", "bump", "version", "type", "reason", "details", "output"]
[cli.release]
help_text = "Prepare a release from discovered change files"
[[cli.release.inputs]]
name = "format"
type = "choice"
choices = ["text", "json", "json-min"]
default = "text"
[[cli.release.steps]]
name = "prepare release"
type = "PrepareRelease"
inputs = ["format"]
[cli.publish-release]
help_text = "Prepare a release and publish provider releases"
[[cli.publish-release.inputs]]
name = "format"
type = "choice"
choices = ["text", "json", "json-min"]
default = "text"
[[cli.publish-release.steps]]
name = "prepare release"
type = "PrepareRelease"
inputs = ["format"]
[[cli.publish-release.steps]]
name = "publish release"
type = "PublishRelease"
inputs = ["format"]
[cli.release-pr]
help_text = "Prepare a release and open or update a provider release request"
[[cli.release-pr.inputs]]
name = "format"
type = "choice"
choices = ["text", "json", "json-min"]
default = "text"
[[cli.release-pr.steps]]
name = "prepare release"
type = "PrepareRelease"
inputs = ["format"]
[[cli.release-pr.steps]]
name = "open release request"
type = "OpenReleaseRequest"
inputs = ["format"]
[cli.affected]
help_text = "Evaluate pull-request changeset policy"
[[cli.affected.inputs]]
name = "format"
type = "choice"
choices = ["text", "json", "json-min"]
default = "text"
[[cli.affected.inputs]]
name = "changed_paths"
type = "string_list"
required = true
[[cli.affected.inputs]]
name = "label"
type = "string_list"
[[cli.affected.steps]]
name = "evaluate affected packages"
type = "AffectedPackages"
inputs = ["format", "changed_paths", "label"]
This guide shows the preferred package/group configuration model together with an expanded CLI command surface.