Files
vociferate/README.md
Micheal Wilkinson 8c2835fe2e feat: derive recommended version from changelog; no version file required
- RecommendedTag now reads the current version from the most recent
  released section heading in the changelog (## [x.y.z] - ...) when no
  --version-file flag is given, removing the dependency on a separate
  version file for recommendation.
- When the changelog contains no prior releases, the base version
  defaults to 0.0.0, so the first recommended tag is v1.0.0 (or higher
  depending on unreleased content).
- Prepare creates the release-version file if it does not already exist,
  so new repositories do not need to pre-seed it.
- Add tests covering changelog-based version resolution, first-release
  default, and automatic file creation.
- Update README and changelog unreleased section to document the new
  behaviour.
2026-03-20 20:10:13 +00:00

5.1 KiB

vociferate

A reusable release preparation tool for Go repositories.

Build

Build with just:

just go-build

Or directly with Go:

go build -o dist/vociferate ./cmd/vociferate

Usage

Prepare release files:

go run ./cmd/vociferate --version v1.2.3 --date 2026-03-20 --root .

In the provided workflow and composite action, version is optional. When it is omitted, vociferate computes and uses the recommended next version automatically.

Recommend next release tag from changelog content:

go run ./cmd/vociferate --recommend --root .

Flags

  • --version semantic version to release (with or without leading v).
  • --date release date in YYYY-MM-DD format.
  • --recommend print recommended next tag based on ## [Unreleased].
  • --root repository root directory.
  • --version-file path to version source file relative to --root.
  • --version-pattern regexp with exactly one capture group for version value.
  • --changelog path to changelog file relative to --root.

Defaults:

  • version-file: release-version
  • version-pattern: ^\s*([^\r\n]+)\s*$
  • changelog: changelog.md

When no --version-file flag is provided, vociferate derives the current version from the most recent released section heading in the changelog (## [x.y.z] - ...). If no prior releases exist, it defaults to 0.0.0 and recommends v1.0.0 as the first tag.

When running --version, the release-version file is created automatically if it does not exist, so new repositories do not need to pre-seed it.

Repositories that keep the version inside source code should pass explicit --version-file and --version-pattern values; in that case the version file is used directly instead of the changelog.

Testing

just go-test

Release Flow

Releases use two workflows:

  • Prepare Release runs on demand, updates release-version and changelog.md, commits those changes back to main, and pushes the release tag.
  • Do Release runs from the pushed tag, reads the matching changelog section from that tagged revision, creates or updates the release, and uploads prebuilt binaries.

This split matters because release notes must be generated from the tagged commit that already contains the promoted changelog section.

Release Artifacts

The tag-driven Do Release workflow publishes prebuilt vociferate binaries for:

  • linux/amd64
  • linux/arm64

It also uploads checksums.txt for integrity verification. If a release already exists for the same tag, the workflow updates its release notes and replaces matching asset filenames so reruns stay in sync.

Reuse In Other Repositories

You can reuse vociferate in two ways.

Use the composite action directly in your prepare workflow:

- name: Prepare release files
  uses: git.hrafn.xyz/aether/vociferate@v1.0.0
  with:
    version-file: internal/myapp/version/version.go
    version-pattern: 'const Version = "([^"]+)"'
    changelog: changelog.md

Set version only when you want to override the recommended version. Pin the composite action to a released tag. It downloads a prebuilt Linux binary from vociferate releases and caches it on the runner, so it does not require installing Go. If your repository uses the default plain-text release-version file, you can omit version-file and version-pattern entirely.

A complete release setup should also split preparation from publication. For example:

name: Prepare Release

on:
  workflow_dispatch:
    inputs:
      version:
        description: Optional semantic version override.
        required: false

jobs:
  prepare:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Prepare release files
        id: prepare
        uses: git.hrafn.xyz/aether/vociferate@v1.0.0
        with:
          version: ${{ inputs.version }}

      - name: Commit and push prepared release
        run: |
          set -euo pipefail
          tag="${{ steps.prepare.outputs.version }}"
          git config user.name "gitea-actions[bot]"
          git config user.email "gitea-actions[bot]@users.noreply.local"
          git add changelog.md release-version
          git commit -m "release: prepare ${tag}"
          git tag "$tag"
          git push origin HEAD
          git push origin "$tag"

Then use a separate tag workflow to publish the release from the tagged revision:

name: Do Release

on:
  push:
    tags:
      - "v*.*.*"

jobs:
  release:
    uses: aether/vociferate/.gitea/workflows/do-release.yml@main
    secrets: inherit

Call the reusable prepare workflow:

name: Prepare Release

on:
  workflow_dispatch:
    inputs:
      version:
        description: Optional semantic version override.
        required: false

jobs:
  release:
    uses: aether/vociferate/.gitea/workflows/prepare-release.yml@main
    with:
      version: ${{ inputs.version }}
    secrets: inherit

And publish from tags with:

name: Do Release

on:
  push:
    tags:
      - "v*.*.*"

jobs:
  release:
    uses: aether/vociferate/.gitea/workflows/do-release.yml@main
    secrets: inherit