brel 0.7.0

better-releases cli tool
# brel

`brel` is a CLI that scaffolds and runs the better-releases workflow.

## Commands

- `brel init` generates a managed GitHub Actions workflow.
- `brel release-pr` computes the next version, updates configured files, commits, pushes, and creates/updates a release PR.
- `brel next-version` computes the next releasable version and prints it as plain SemVer.
- `brel validate` validates a config file and prints warnings for ignored keys.

## `release-pr` Prerequisites

- `git` must be available.
- `gh` (GitHub CLI) must be available.
- A GitHub token must be present in `GH_TOKEN` or `GITHUB_TOKEN`.
  - The workflow generated by `brel init` sets `GH_TOKEN: ${{ github.token }}` automatically.

## Config File

`brel` discovers config in this order:

1. `--config <path>` (when provided)
2. `brel.toml`
3. `.brel.toml`

Use `brel validate` to check that an existing config file parses and satisfies the current schema before running other commands.

### Minimal `release-pr` config

```toml
provider = "github"
default_branch = "main"

[release_pr.version_updates]
"package.json" = ["version"]
```

### Full `release_pr` config

```toml
[release_pr.version_updates]
"package.json" = ["version"]
"Cargo.toml" = ["package.version"]

[release_pr.format_overrides]
"Cargo.toml" = "toml"

[release_pr]
release_branch_pattern = "brel/release/v{{version}}"
pr_template_file = ".github/brel/release-pr-body.hbs"

[release_pr.changelog]
enabled = true
output_file = "CHANGELOG.md"

[release_pr.tagging]
enabled = false
tag_template = "v{version}"

[release_pr.commit_author]
name = "brel[bot]"
email = "brel[bot]@users.noreply.github.com"
```

## How Versioning Works

When you run `brel release-pr`:

1. It finds the highest stable SemVer tag that matches `release_pr.tagging.tag_template` (default `v{version}`).
2. If no valid tag exists, it uses `0.0.0`.
3. It scans commits since that tag (or all commits when no tag exists).
4. It picks one bump level from Conventional Commit signals:
   - major: `BREAKING CHANGE` in body/footer, or `!` in the type/scope prefix.
   - minor: `feat: ...`
   - patch: `fix: ...`
5. If no releasable commits are found, it exits successfully with no changes.

`brel next-version` uses the same versioning rules:

- when releasable commits exist, it prints the next version (for example `1.2.3`)
- when none exist, it prints nothing and exits successfully

## How File Updates Work

- `release_pr.version_updates` maps exact repo-relative file paths to selector paths.
- Selector syntax:
  - key: `version`
  - nested key: `package.version`
  - index selector: `packages[0].version`
  - filter selector: `package[name=brel].version`
- Supported file formats:
  - inferred from extension (`.json`, `.toml`)
  - or forced via `release_pr.format_overrides`
- Updates are fail-fast. The command errors if:
  - a file is missing,
  - format cannot be determined,
  - parse fails,
  - a selector is invalid,
  - a selector matches no values,
  - a selector uses index/filter on a non-array segment,
  - a matched value is not a string.
- Match behavior:
  - all values matched by a selector are updated
  - selectors do not create missing keys/paths

Example selectors:

- JSON: `"package.json" = ["version", "tooling.release.version"]`
- JSON with filter: `"package.json" = ["package[name=brel].version"]`
- TOML: `"Cargo.toml" = ["package.version"]`
- Cargo.lock (explicit format override required):

```toml
[release_pr.version_updates]
"Cargo.lock" = ["package[name=brel].version"]

[release_pr.format_overrides]
"Cargo.lock" = "toml"
```

## Changelog Generation (`git-cliff`)

- `brel init` generates a workflow that runs [`orhun/git-cliff-action@v4`]https://github.com/orhun/git-cliff-action by default.
- Configure changelog behavior with `[release_pr.changelog]`:
  - `enabled` (default `true`)
  - `output_file` (default `"CHANGELOG.md"`)
- Generated workflow behavior:
  - computes `next-version` first via `brel next-version`
  - runs `git-cliff` only when a next version exists
  - passes `--unreleased --tag <rendered-tag-template>` so the newest changelog section is versioned instead of `[unreleased]`
- If changelog generation is enabled, `brel release-pr` stages `output_file` in the release commit when that file exists.
- Disable changelog generation:

```toml
[release_pr.changelog]
enabled = false
```

- `brel init` does not create or manage `cliff.toml`; keep that file in your repository if you want custom `git-cliff` rules.

## Branch / Commit / PR Behavior

- Default branch pattern: `brel/release/v{{version}}`
  - Only `{{version}}` is supported as a token.
- `release_pr.tagging.tag_template` controls rendered release tags (default `v{version}`).
  - `tag_template` accepts `{version}` and legacy `{{version}}` (normalized to `{version}`).
  - `tag_template` must include exactly one version token.
- Commit message: `chore(release): <rendered-tag>`
- PR title: `Release <rendered-tag>`
- Commit author defaults to:
  - `name = "brel[bot]"`
  - `email = "brel[bot]@users.noreply.github.com"`
- Push strategy: `--force-with-lease` to `origin`.

For PRs:

- `brel` uses `gh pr list` to find an open managed release PR.
- If found, it updates that PR (continuity wins over recomputing branch name).
- If not found, it creates a new PR.

## Tagging on Merge

- Optional config: `[release_pr.tagging] enabled = true` (default `false`).
- Tag format config: `[release_pr.tagging] tag_template = "v{version}"` (default shown).
- When enabled, the generated workflow listens for merged pull requests into the configured default branch.
- If the merged PR is managed by `brel` and titled `Release <rendered-tag>`, the workflow validates it against `tag_template`, then creates and pushes that tag when it does not already exist.
- Create repository secret `BREL_TAG_PUSH_TOKEN` before using tagging-on-merge.
  - Use a PAT that can push tags to the repository (fine-grained PAT with `Contents: Read and write`).
  - This is required because pushes done with `GITHUB_TOKEN` do not trigger downstream tag-push workflows.
- `brel init` prints this secret requirement whenever tagging is enabled.

## PR Body Templates

If `release_pr.pr_template_file` is set, `brel` renders that Handlebars template.

Available variables:

- `version`
- `tag`
- `base_branch`
- `release_branch`
- `commits` (array of `{ sha_short, subject }`)

Important: include this marker in your template so future runs can detect and update the same PR:

```html
<!-- managed-by: brel -->
```

If rendering fails, `brel release-pr` exits with an error.

## Typical Usage

Generate workflow once:

```bash
brel init --yes
```

Run release locally:

```bash
GH_TOKEN=... brel release-pr
```

Run with explicit config:

```bash
brel release-pr --config ./configs/release.toml
```

Validate config:

```bash
brel validate
```

Preview the next release version:

```bash
brel next-version
```