---
description: Configure release roots, workspace discovery, version files, changelog generation, Git policy, and hooks.
---

# Configuration reference

Verso reads `verso.toml` from the current directory unless `--config <PATH>` or `--group <NAME>` is
supplied. `--config` accepts a relative or absolute file path, while `--group core` selects
`verso.core.toml`. The selected file's directory becomes the release root: package patterns,
manifests, changelog, hooks, and Git commands all resolve from there.

Unknown tables and keys are rejected. Paths and patterns written inside `verso.toml` must be relative
to the release root, use forward slashes, and cannot contain parent-directory (`..`) segments.

## Default configuration

```toml filename="verso.toml"
[version]
root_package = "package.json"
require_consistent_versions = true
cargo_manifest_paths = []

[workspaces]
patterns = []
include_root = true
ignore = []
use_gitignore = true

[changelog]
enabled = false
infile = "CHANGELOG.md"
preset = "angular"

[git]
require_clean_worktree = true
commit_message = "chore(release): release v${version}"
tag_name = "v${version}"
push = "atomic"

[hooks]
# before_version = "pnpm test"
# after_version = "pnpm build"
# before_commit = "pnpm lint"
# after_commit = "node scripts/verify-release.mjs"
# before_tag = "pnpm pack"
# after_tag = "node scripts/record-tag.mjs"
# before_push = "pnpm check"
# after_push = "node scripts/notify.mjs"
```

Every table and key is optional. When the default config path is absent and a root package manifest
exists, Verso uses these values without creating a file. An explicitly supplied missing config is
always an error.

<Callout variant="tip" title="Start with the smallest configuration">
  A single package usually needs no file. Most workspaces can start with only a `[workspaces]`
  table; add other sections only when their behavior must differ from the defaults.
</Callout>

## Workspace discovery

When `workspaces.patterns` is empty, Verso first reads `packages` from `pnpm-workspace.yaml`, then the
root manifest's `workspaces` field. If neither defines patterns, only the root package is used.

| Key             | Default | Behavior                                                                                         |
| --------------- | ------- | ------------------------------------------------------------------------------------------------ |
| `patterns`      | `[]`    | Include globs; supports `*`, `**`, `?`, character classes, braces, and leading `!` exclusions    |
| `include_root`  | `true`  | Include the manifest selected by `version.root_package`                                          |
| `ignore`        | `[]`    | Exclude matching paths; a plain segment such as `fixtures` excludes that directory name anywhere |
| `use_gitignore` | `true`  | Honor root and nested `.gitignore` files while walking                                           |

`node_modules` and `.git` are always excluded. If `include_root = false`, `patterns` must be explicit
and nonempty; inferred workspace patterns are not accepted for that combination.

Set `include_root = false` when the root manifest only describes the workspace and should not share
the release group's version.

Each matched directory contributes the first existing manifest in this order:
`package.json`, `package.json5`, `package.yaml`, `package.yml`. Verso changes only the top-level
`version` scalar and preserves the rest of the file's formatting.

## Version sources

| Key                           | Default        | Behavior                                                                                        |
| ----------------------------- | -------------- | ----------------------------------------------------------------------------------------------- |
| `root_package`                | `package.json` | Provides the current version and joins the update when `include_root` is enabled                |
| `require_consistent_versions` | `true`         | Must remain `true`; rejects members whose versions differ so the group has one current version  |
| `cargo_manifest_paths`        | `[]`           | Updates each manifest's `[package].version` and its nearest ancestor `Cargo.lock`, when present |

When the default `root_package` does not exist, Verso may resolve another supported manifest in the
release root. A custom `root_package` is used exactly as configured.

## Release groups

One config defines one release group. Every package and configured Cargo manifest discovered by that
config receives the same target version. `version.require_consistent_versions` must remain `true`;
setting it to `false` is rejected because it would make the group's current version ambiguous.

Use separate configs for independently versioned groups and run one group at a time:

```text
verso.core.toml  -> verso --group core bump minor
verso.ui.toml    -> verso --group ui --version 2.0.0 --yes
```

For a named group, the default `v${version}` template is automatically scoped with the group name:
`core-v1.2.3`, `ui-v1.2.3`, and so on. An explicitly different `git.tag_name` is used unchanged.

`--group <NAME>` and `--config <PATH>` are mutually exclusive. Group names start with an ASCII letter
or digit and may otherwise contain ASCII letters, digits, `-`, and `_`.

## Changelog

Set `changelog.enabled = true` to add a release entry to `changelog.infile`. `changelog.preset`
accepts `angular` and `keep-a-changelog`. Changelog generation runs only for a full release;
`verso bump` leaves the file unchanged.

For either preset, Verso finds the highest reachable SemVer tag matching `git.tag_name`, reads
non-merge commits after it, and classifies Conventional Commit subjects. The `angular` preset uses:

- `fix` -> Bug Fixes
- `feat` -> Features
- `perf` -> Performance Improvements
- `!` or a `BREAKING CHANGE:` footer -> BREAKING CHANGES
- other conventional types -> a type-specific Other Changes section
- non-conventional commits -> omitted

GitHub compare, commit, and issue links are added when the `origin` remote is a recognized GitHub
SSH or HTTPS URL. An empty classified set produces `No classifiable changes.`

The `keep-a-changelog` preset inserts each release immediately after `## [Unreleased]` and maps
`feat` to Added, `fix` to Fixed, and performance, breaking, and other conventional changes to Changed.
It also updates the `[Unreleased]` and release link definitions when a GitHub origin is available.

## Git policy

| Key                      | Default                               | Behavior                                                                       |
| ------------------------ | ------------------------------------- | ------------------------------------------------------------------------------ |
| `require_clean_worktree` | `true`                                | Requires `git status --porcelain` to be empty before writes                    |
| `commit_message`         | `chore(release): release v${version}` | Replaces every `${version}` placeholder with the target                        |
| `tag_name`               | `v${version}`                         | Must contain `${version}`; named groups scope this default with the group name |
| `push`                   | `atomic`                              | Pushes the current upstream branch and exact release tag in one atomic command |

With `require_clean_worktree = false`, unrelated unstaged files are allowed, but the index and every
release file must still be clean. No non-atomic push mode exists.

## Hooks are trusted shell commands

Hooks run from the release root through `sh -c` on macOS/Linux and `cmd /C` on Windows. Empty strings
are treated as disabled. A nonzero exit stops the release at that stage.

`verso bump` runs only `before_version` and `after_version`; commit, tag, and push hooks belong to a
full release.

<Callout variant="warning" title="Hooks run with your permissions">
  Hook commands are printed by dry-run, so never place secrets directly in the config. Pass
  sensitive values through the process environment, keep `verso.toml` reviewable, and leave
  credentialed publishing or GitHub Release creation to tag-triggered CI. See [Release
  workflow](/release-workflow/) for the cleanup behavior of each hook boundary.
</Callout>
