- Add internal/notify package: best-effort release notifications to Slack, Microsoft Teams, Google Chat, Telegram, and a generic JSON webhook; every target is independently opt-in and a failed notification never fails the release - Add notify config section (slack_webhook_url, teams_webhook_url, google_chat_webhook_url, telegram_bot_token/telegram_chat_id, webhook_url) with matching env var fallbacks; 100% coverage plus FuzzMessagePayloads - Wire notification sending into run() — fires after tag+push (including --no-release), skipped on --no-push/--no-commit since nothing was published yet - Document the notify section in README, Hugo docs, and .releaser.yml template; update CLAUDE.md architecture/fuzzing tables - Also commit the Apache License 2.0 docs-site footer link (docs/hugo.toml geekdocContentLicense), left uncommitted from a prior change Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
releaser
A CI-friendly release automation tool for GitFlow workflows using Conventional Commits.
Problem
Standard tools like semantic-release are designed for trunk-based development. In a GitFlow setup with versioned release branches (release/1.1, release/1.2), they either fail to respect the branch's version range or require brittle configuration.
releaser is built for this exact workflow: it reads the branch name to pin the major.minor, parses Conventional Commits to determine the patch increment, and handles everything from pom.xml update to GitLab/GitHub tag+release creation.
How it works
release/1.2 branch
└─ last tag: 1.2.3 (or none → start at 1.2.0)
└─ commits since tag → Conventional Commits analysis
└─ next version: 1.2.4
- Branch parsing — extracts
major.minorfrom branch name (e.g.release/1.2→1.2) - Tag discovery — finds the latest tag matching
major.minor.*on the current branch - Commit analysis — parses Conventional Commits between last tag and HEAD
- Version bump — increments patch (the minor is owned by the branch)
- Release — updates
pom.xml/package.json/build.gradle, commits, tags, creates GitLab or GitHub release - Notify — best-effort notification to Slack, Microsoft Teams, Google Chat, Telegram, and/or a generic webhook
Version bump rules
By default, all releasable commits bump the patch component (minor is pinned to the branch). You can override this per commit type via git.bump_rules in .releaser.yml:
| Commit type | Default | Configurable via bump_rules |
|---|---|---|
fix: |
patch | fix: "minor" to bump minor instead |
feat: |
patch | feat: "minor" to bump minor instead |
feat!: / BREAKING CHANGE |
patch | breaking: "minor" to bump minor |
chore:, docs:, etc. |
none | — |
| unparseable msg | none | non-strict mode: silently ignored |
Usage
# Scaffold a default .releaser.yml in the current repository
releaser --init
# Simulate next version (no side effects)
releaser --dry-run
# Full release: update pom.xml + CHANGELOG.md, commit, tag, push, create release
releaser
# Commit and tag locally — skip push and release creation
releaser --no-push
# Push commit and tag but skip creating the release
releaser --no-release
# Update files but stop before committing (review first)
releaser --no-commit
# … then commit manually and re-run:
releaser --tag-only
# Explicitly target a branch (useful in detached HEAD CI)
releaser --branch release/1.2
# Write changelog to a custom file
releaser --changelog-file CHANGES.md
# Show configuration sources, commit list, and version decision
releaser --verbose --dry-run
# Target a specific pom.xml
releaser --pom path/to/pom.xml
# Override tag prefix from CLI (empty = no prefix)
releaser --tag-prefix ""
# Override branch pattern (e.g. also match hotfix/ branches)
releaser --branch-pattern "^(?:.*/)?(?:release|hotfix)/(\d+)\.(\d+)$"
# Write dotenv artifact to a custom path (or "" to disable)
releaser --release-env-file deploy/version.env
Configuration
releaser reads .releaser.yml from the repository root. All fields are optional — missing values fall back to the defaults shown below.
git:
tag_prefix: "" # default: no prefix; set to "v" for v-prefixed tags
branch_pattern: "^(?:.*/)?release/(\\d+)\\.(\\d+)$" # two capture groups: major, minor
commit_message: "chore(release): {version} [skip ci]"
author_name: "" # defaults to git config user.name
author_email: "" # defaults to git config user.email
releasable_types: # default: all three
- fix
- feat
- breaking
bump_rules: # which version component each type bumps
breaking: "patch" # "minor" to bump minor on breaking changes
feat: "patch"
fix: "patch"
maven:
pom_path: "pom.xml" # single pom.xml, relative to repo root
# pom_paths: # multi-module: list overrides pom_path
# - "pom.xml"
# - "module-a/pom.xml"
# - "module-b/pom.xml"
node: # opt-in — no default; omit to skip
# package_json: "package.json" # single path
# package_jsons: # monorepo: list overrides package_json
# - "packages/frontend/package.json"
# - "packages/backend/package.json"
gradle: # opt-in — no default; omit to skip
# build_file: "build.gradle" # Groovy or Kotlin DSL; single path
# build_files: # multi-module: list overrides build_file
# - "build.gradle"
# - "module-a/build.gradle"
python: # opt-in — no default; omit to skip
# pyproject_toml: "pyproject.toml" # PEP 621 [project] or [tool.poetry]
# pyproject_tomls: # monorepo: list overrides pyproject_toml
# - "pyproject.toml"
# - "packages/cli/pyproject.toml"
gitlab:
url: "https://gitlab.example.com" # or env CI_SERVER_URL
token: "" # env GITLAB_TOKEN (never commit this)
project: "" # env CI_PROJECT_ID or CI_PROJECT_PATH
github:
token: "" # env GITHUB_TOKEN (never commit this)
repo: "" # "owner/repo" format
notify: # opt-in — every field independent; failures are warnings, not errors
# slack_webhook_url: "" # or env SLACK_WEBHOOK_URL
# teams_webhook_url: "" # or env TEAMS_WEBHOOK_URL
# google_chat_webhook_url: "" # or env GOOGLE_CHAT_WEBHOOK_URL
# telegram_bot_token: "" # or env TELEGRAM_BOT_TOKEN (both required)
# telegram_chat_id: "" # or env TELEGRAM_CHAT_ID
# webhook_url: "" # generic {"version","notes"} JSON POST; or env RELEASER_WEBHOOK_URL
Environment variables
| Variable | Used for |
|---|---|
GITLAB_TOKEN |
GitLab API auth + HTTPS push auth |
CI_SERVER_URL |
GitLab instance URL |
CI_PROJECT_ID |
GitLab project identifier (numeric) |
CI_PROJECT_PATH |
GitLab project identifier (fallback) |
GITHUB_TOKEN |
GitHub API auth |
SLACK_WEBHOOK_URL |
Slack release notification |
TEAMS_WEBHOOK_URL |
Microsoft Teams release notification |
GOOGLE_CHAT_WEBHOOK_URL |
Google Chat release notification |
TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID |
Telegram release notification (both required) |
RELEASER_WEBHOOK_URL |
Generic webhook release notification |
When both github.* and gitlab.* are configured, GitHub takes precedence. All notify.* targets are independent — any combination may be configured at once, and a failed notification never fails the release.
CI integration (GitLab CI example)
release:
stage: release
image: registry.example.com/releaser:latest
rules:
- if: $CI_COMMIT_BRANCH =~ /^release\/.+$/
variables:
GITLAB_TOKEN: $RELEASE_TOKEN # project/group CI variable with api + write_repository scope
script:
- releaser
artifacts:
reports:
dotenv: release.env # exposes NEXT_VERSION to downstream jobs
License
Apache License 2.0 — see LICENSE.