PrepareRelease
What it does
PrepareRelease is the core release execution step.
It discovers packages, loads authored changesets, computes the release plan, updates manifests and changelogs, and prepares the structured release result that later steps can consume.
In other words: most release-oriented commands are really PrepareRelease plus something else.
Why use it
Use PrepareRelease whenever the command needs real release state.
It is the step that unlocks:
- release file updates
- changelog rendering
- release target calculation
- structured
release.*template context - the cached
.monochange/release-manifest.jsonartifact exposed asmanifest.path - later steps such as
CommitRelease,PublishRelease,OpenReleaseRequest, andCommentReleasedIssues
If your command eventually needs release metadata, start with PrepareRelease rather than trying to reconstruct that state in shell.
Inputs
format:markdown,text, orjsonwrite_empty_release_record: boolean; write a release record even when no packages were releasedrelease_json: boolean; write the release record (.monochange/releases/<hash>/release.json) during dry-run preview. By defaultmonochange previewskips the release record; pass--release-jsonto write it.
Step-level when condition
All CLI steps support an optional when = "..." condition.
If the expression resolves to false at runtime, monochange skips the step and continues with the next step.
when = "{{ inputs.enabled }}"
Step-level always_run flag
All CLI steps support an optional always_run = true flag.
When set, the step executes even if a previous step in the same command has failed. This is useful for cleanup, notification, or dry-run preview steps that must run regardless of earlier outcomes.
always_run = true
Prerequisites
None. PrepareRelease is the producer step for the rest of the release workflow.
Side effects and outputs
PrepareRelease is stateful.
It can produce:
- updated manifests
- updated changelogs
- deleted or consumed changeset files
- release target information
- a cached release manifest at
.monochange/release-manifest.json - final command output in markdown, text, or JSON form
- structured
release.*template values for laterCommandsteps manifest.pathfor laterCommandsteps that need the on-disk JSON artifact
Built-in release-oriented commands default their human-readable format input to text. Use markdown for a raw Markdown artifact or json for automation.
When you only need the resolved package and group versions, use the dedicated DisplayVersions step or the built-in monochange versions command instead of overloading PrepareRelease.
It also fills the shorthand template values commonly used by Command steps:
{{ version }}{{ group_version }}{{ released_packages }}{{ changed_files }}{{ changesets }}
Example
[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]]
type = "PrepareRelease"
inputs = ["format"]
Composition ideas
Prepare and run a custom follow-up command
[cli.release-with-notes]
help_text = "Prepare a release and print a custom summary"
[[cli.release-with-notes.inputs]]
name = "format"
type = "choice"
choices = ["text", "json", "json-min"]
default = "text"
[[cli.release-with-notes.steps]]
type = "PrepareRelease"
inputs = ["format"]
[[cli.release-with-notes.steps]]
type = "Command"
command = "echo Releasing {{ release.version }} for {{ released_packages }}"
shell = true
Prepare and then branch into provider automation
Typical production commands look like:
PrepareRelease→PublishReleasePrepareRelease→OpenReleaseRequestPrepareRelease→CommitReleasePrepareRelease→Command
Prepare once, consume several outputs
Because the later steps all depend on the same prepared state, you should generally do one PrepareRelease and then fan out from it with several typed steps rather than trying to run several independent release commands.
Reuse prepared state across separate commands
When you do need to split the workflow across separate commands, monochange can reuse a prepared release artifact instead of recomputing the release plan from scratch.
When a repository defines workflow wrappers, the default cache path is automatic. For example, a repo with release and release-pr wrappers might run:
monochange step prepare-release
monochange step open-release-request --dry-run
The first PrepareRelease step stores prepared state in .monochange/local/prepared-release-cache.json, and later commands with a PrepareRelease step reuse it only while every input it was computed from still matches: the pending changeset bytes and set, package manifests and their ancestor workspace manifests, monochange.toml, prerelease state, release records, the git HEAD, and the workspace status. Because the changeset bytes are fingerprinted, editing a changeset’s severity or body in place — even when it is untracked or already dirty — replans instead of reusing the stale plan.
.monochange/local/ is the only directory under .monochange/ that should be gitignored. monochange adds it to .git/info/exclude automatically, so reusable prepared state, the cached release manifest, and other local release metadata do not pollute reviewable commits.
Never add .monochange/ as a whole to .gitignore. Release records under .monochange/releases/<id>/release.json and .monochange/prerelease-state.json are committed state that CommitRelease, publish-readiness, tag-release, and provider release automation read from git history. Ignoring them makes releases unpublishable.
If your configured workflow exposes a prepared_release input and you need to pass the artifact between explicit jobs or custom commands, wire that input to PrepareRelease and pass the artifact path:
monochange run release --prepared-release /tmp/release-plan.json
monochange step open-release-request --prepared-release /tmp/release-plan.json --format json
Here release and release-request are example workflow names; use the names shown by monochange help for your repository.
If the artifact is stale, monochange falls back to a fresh PrepareRelease run instead of trusting outdated release data.
Good fit / bad fit
Good fit:
- any release workflow
- commands that need release metadata
- commands that need changelog or version updates
Bad fit:
- simple validation-only CI gates
- discovery-only inspection commands
- post-release repair flows (
RetargetReleaseis separate)
Common mistakes
- putting
PublishReleaseorOpenReleaseRequestbeforePrepareRelease - assuming
PrepareReleaseis just a read-only planner in non-dry-run mode - forgetting that later
Commandsteps can consume its structured output directly - forgetting that
--quietsuppresses output but does not replace--dry-run