Skip to content

relwolf.yaml ​

relwolf.yaml in the repository root is optional. Without it, relwolf releases a single root package without file updaters. Everything is configured through this file; there are no CLI flags. Secrets and CI context come from environment variables.

yaml
branch: main # branch to release from; defaults to the CI default branch, then "main"
cliff-config: cliff.toml # git-cliff config; defaults to git-cliff's own discovery/default
rewrite-changelog: false # true: regenerate whole changelogs when the git-cliff config changed since the last release
pre-commit-script: scripts/format.sh # optional, runs before the release commit, may change any file
pull-request-title: 'chore(release): Create release ${tags}' # optional, title of the release MR/PR
prerelease: beta # optional, default for all packages
bump-minor-pre-major: true # optional, default for all packages
patch-types: [fix] # commit types that trigger a patch release
minor-types: [feat] # commit types that trigger a minor release
exclude-paths: ['docs/**'] # changes in these globs never count
bootstrap-sha: 1a2b3c4 # ignore history up to this commit if there is no tag yet
tag-template: '${component}-v${version}'
release-name: '${tag}'
release-emoji: false # true: prefix the default release title with a random emoji
branch-prefix: relwolf/
labels: {pending: 'relwolf: pending', tagged: 'relwolf: tagged'}
draft: false
no-changes-text: 'No changes, version bumped to stay in sync.' # '' to add nothing
assignees: [alice]
reviewers: [bob]
release-commit-message: 'chore(release): Create release ${tags}'
pull-request-body: '...'
separate-pull-requests: false
linked-versions: false # true, or [[services/api, services/web]]
linked-tags: per-package # or single: one tag and release per linked group

packages:
  '.':
    component: app # optional for one package; required for every other package (used as tag prefix)
    changelog-path: CHANGELOG.md # relative to the package path
    initial-version: 1.0.0 # optional, first stable version if no tag exists
    prerelease: rc # optional, overrides the global value
    bump-minor-pre-major: false # optional, overrides the global value
    updaters:
      - type: package-json # default path: package.json
      - type: pom-xml # default path: pom.xml
      - type: gradle # default path: build.gradle (also: build.gradle.kts, gradle.properties)
      - type: generic
        path: deploy/values.yaml

Paths of updaters and the changelog are relative to the package path. Unknown keys are rejected.

Pre-commit script ​

pre-commit-script is the path (relative to the repository root) of a script in the working tree. Once relwolf has computed the release files (changelog, version updates, lockfiles), it writes them into the working tree, runs the script in the repository root and commits everything that changed afterwards, including new files. Use it for steps relwolf cannot do itself, for example formatting the generated changelog:

sh
#!/bin/sh
set -e
pnpm run format
  • The script is executed directly: it needs a shebang line and the executable bit (chmod +x).
  • Install what the script needs (dependencies, tools) in your pipeline before relwolf runs; relwolf does not.
  • Run relwolf on a clean checkout. It aborts if a release file has uncommitted changes. Other uncommitted changes are neither committed nor touched.
  • Afterwards the working tree is reset to HEAD; the changes only exist in the release commit.
  • A non-zero exit code aborts the run. Deleting or renaming files is not supported and aborts the run, too.
  • Files are committed as UTF-8 text; binary files are not supported.

Editor completion ​

A JSON Schema of the file is published at https://relwolf.lupiq.io/schema.json and listed in SchemaStore. Editors with SchemaStore support (VS Code with the Red Hat YAML extension, IntelliJ, …) pick it up automatically for relwolf.yaml and relwolf.yml; no configuration is needed.

To pin the schema explicitly (e.g. in editors without SchemaStore), add this as the first line of the file:

yaml
# yaml-language-server: $schema=https://relwolf.lupiq.io/schema.json

Options ​

KeyLevelDefaultDescription
branchglobalCI default branch, then mainBranch to release from
cliff-configglobalgit-cliff discoveryPath to a git-cliff config
rewrite-changelogglobalfalseRewrite changelogs if the git-cliff config changed, see Changelog
pre-commit-scriptglobalnoneScript run before the release commit, see below
pull-request-titleglobalchore(release): Create release ${tags}Title template, see below
patch-typesglobal[fix]Commit types that trigger a patch release
minor-typesglobal[feat]Commit types that trigger a minor release
exclude-pathsglobalnoneGlobs (from the repo root) whose changes never count
bootstrap-shaglobalnoneStart of the history for packages without a tag
tag-templateglobal${component}-v${version}Tag format, see below
release-nameglobal${tag}Title of the created releases
release-emojiglobalfalseRandom emoji in front of the release title, see below
branch-prefixglobalrelwolf/Prefix of the release branch
labelsglobalrelwolf: pending / relwolf: taggedLabels pending and tagged
draftglobalfalseOpen the release request as draft
no-changes-textglobalNo changes, version bumped to stay in sync.Text for packages without changes (linked versions)
assigneesglobalnoneUsernames, set when the request is created
reviewersglobalnoneUsernames, set when the request is created
release-commit-messageglobalthe titleCommit message of the release commit
pull-request-bodyglobalbuilt inBody template, see Body
separate-pull-requestsglobalfalseOne request per package, see Monorepos
linked-versionsglobalfalsePackages sharing one version, see Monorepos
linked-tagsglobalper-packagesingle: one tag and release per linked group
prereleaseglobal, packagenonePre-release identifier
bump-minor-pre-majorglobal, packagetrueBreaking changes only bump minor before 1.0.0
componentpackagenoneTag prefix, see Monorepos
changelog-pathpackageCHANGELOG.mdChangelog file, relative to the package
initial-versionpackagecomputed from 0.0.0First stable version if no tag exists
updaterspackagenoneFiles to update

Commit types ​

patch-types and minor-types are complete lists (they replace the defaults, so list fix again if you want to keep it). A type may only be in one list. Breaking changes (! or BREAKING CHANGE) always release a major version. exclude-paths are globs such as docs/**; commits that only touch them are ignored for the version and the changelog. bootstrap-sha is only used while a package has no tag: the history up to and including that commit is ignored.

Tags and release names ​

tag-template contains ${version} exactly once and may contain ${component} (once). Without a component, the placeholder and the separator behind it are dropped, so the default yields v1.2.3 or api-v1.2.3. With several packages the template must contain ${component}. Only letters, digits and ._/@- are allowed as literal characters.

release-name supports ${tag}, ${version} (without prefix) and ${component}.

Release request ​

branch-prefix, labels, draft, assignees and reviewers configure the request. draft and the people are only applied when the request is created; relwolf keeps a draft state and never touches assignees afterwards. Usernames are resolved by the provider; unknown users fail the run.

no-changes-text is written below the changelog entry and into the release notes of a package that was only released to follow the version of linked packages. Set it to an empty string to leave such entries as git-cliff renders them.

Release title and text ​

The default body contains two editable blocks, Release title and Release text. Edit them in the description of the release request to individualise the releases:

  • The title is a template with ${tag}, ${version} and ${component} and applies to every release in the request. It starts as your release-name. With release-emoji: true it is prefixed with a random emoji (only objects, nature and food, no people or animals), for example 🚀 ${tag}. The emoji is chosen once when the request is created; later updates keep whatever the block contains.
  • The text is Markdown and is added above the changelog notes of every release. HTML comments are ignored, so the initial hint adds nothing. Leave the block as it is to add no text.
  • Your edits survive updates of the request; only the changelog notes are regenerated.

Custom pull-request-body templates need the ${release} placeholder to offer these blocks, and release-emoji requires it.

Title and commit message ​

pull-request-title sets the title of the release request. It is also the message of the release commit, unless release-commit-message is set (same placeholders, may span several lines).

PlaceholderReplaced with
${tags}All tags of the release, comma separated, e.g. v1.2.3 or v1.2.3, api-v0.4.0
${version}The bare version (1.2.3) if a single package is released, otherwise the same as ${tags}
yaml
pull-request-title: 'chore: release ${version}'

Unknown placeholders fail the run. Keep a type that does not bump the version (chore, docs, …); a feat or fix title would count as a releasable commit once the release is squash-merged.

Body ​

pull-request-body is a complete template with the placeholders ${tags}, ${version}, ${releases} (bullet list), ${hint} (note about RELWOLF_RELEASE_AS, otherwise empty), ${release} (the editable release title and text, see above) and ${notes} (the release notes, required exactly once). The markers relwolf needs to track the release are always appended.

Typical setups ​

yaml
packages:
  '.':
    updaters:
      - type: package-json
yaml
packages:
  '.':
    updaters:
      - type: pom-xml
yaml
packages:
  '.':
    updaters:
      - type: gradle
yaml
packages:
  '.':
    updaters:
      - type: generic
        path: deploy/values.yaml
Developed by lupiqlupiq