Skip to content

Release workflows

Versionary supports two release styles, selected with the review-mode config key, and a single run entrypoint that does the right thing based on context.

Review modes

jsonc
{
  "version": 1,
  "release-type": "node",
  "review-mode": "pr" // "pr" (default) | "direct"
}
  • pr (default) — the release PR workflow. Versionary prepares or updates a dedicated release branch with the version bump and changelog, then opens or updates a release PR through the SCM provider. A maintainer reviews and merges; the merge produces a release commit, and the next run publishes the release.
  • direct — the direct workflow. Versionary prepares/updates the release branch but skips creating a review request.

The run command

run is the recommended CI entrypoint. It inspects the most recent commit and auto-dispatches:

                ┌─ last commit is a release commit ─→ publish the release
versionary run ─┤
                └─ otherwise ─→ plan; if there are releasable commits,
                                prepare/update the release PR (or branch),
                                else close any stale release PR and exit

A commit counts as a release commit when its subject looks like chore(release): v1.2.3 (including multi-target and monorepo tag forms) or it carries a Versionary-Release: true footer. This is exactly the commit Versionary writes when preparing a release, so merging a release PR naturally triggers the publish path on the next run.

With separate-release-prs, run also recognizes a merged package-state sidecar as release context. It publishes only the untagged package cohorts represented in the merged tree, then updates or closes the remaining package release PRs against the new trunk state.

run supports:

  • --json — emit a machine-readable result (used by the GitHub Action). The action field is one of noop, pr-prepared, pr-up-to-date, pr-dry-run, release-skipped, release-dry-run, or release-published.
  • --dry-run — compute and report what would happen without pushing branches, opening PRs, or creating tags/releases.

See the CLI reference for the full output shape and the other commands (verify, plan, changelog, pr, release).

A typical CI lifecycle

  1. A feat:/fix: commit lands on main.
  2. CI runs versionary run. No release commit is present, so it opens a release PR: "chore(release): v1.3.0" with the changelog.
  3. More commits land; each run updates the same release PR in place.
  4. A maintainer merges the release PR. The merge commit is a release commit.
  5. The next versionary run takes the publish path: it creates the tag and the GitHub Release.
  6. A separate, release-triggered workflow publishes to your registry.

In a separate-PR monorepo, steps 2–5 happen independently for each package or coupled cohort. Merging one package PR does not require merging the others.

In direct mode, steps 2–4 collapse: the release branch is prepared without a review request, and publishing follows once the release commit is on the branch you run against.

The GitHub Action skips an outdated ordinary push run after the branch advances, which prevents an older run from overwriting release planning based on a newer commit. A commit with the explicit Versionary-Release: true marker is an exception: if it remains an ancestor of the current branch tip, its run may publish that exact commit. An unrelated commit can therefore land while the release checks are running without forcing a recovery PR. If the release commit is no longer on the branch—for example, after a force-push—the Action still skips it.

Idempotency, retries, and recovery

Publishing is idempotent by target tag, so reruns after a partial failure are safe:

  • if a tag already exists, Versionary reuses it instead of recreating it
  • if release metadata already exists for that tag, it is reused
  • if a previous run created/pushed the tag but failed before creating metadata, a rerun creates the missing metadata and proceeds

If CI fails before Versionary reaches the publish job and a corrective commit must land on the base branch, Versionary preserves the untagged pending version. The next run recreates the release PR with an empty release-marker commit on top of the corrected base. It does not plan a later version—even when the corrective commit would ordinarily cause a semantic-version bump. After that PR merges and CI succeeds, Versionary publishes the original pending tags.

Separate package releases recover on their original cohort branches. A tag created for one independent cohort does not block recovery of another. A partially tagged multi-package cohort cannot move to a corrected commit; rerun the original release workflow so its idempotent publish path can finish the remaining targets.

Versionary fails fast when recovery would be unsafe—for example, when a local and a remote tag of the same name point to different commits. The error message includes remediation guidance so CI logs stay actionable.

Author and committer identity

Two distinct identities are involved when releasing through a provider:

  • The token's account owns the GitHub Release, the tag/branch push, and any release-reference comments. There is no API to set these independently of the token.
  • The release commit's committer comes from git's user.name/user.email. When neither is set (a bare CI runner), Versionary defaults it to github-actions[bot], so no git config step is required.

The GitHub Actions guide explains how to choose a token so releases are attributed the way you want.

Released under the MIT License.