Rename the reusable workflows to release.yml and update-release.yml, add UPX compression for release binaries, and sync the standalone update-release workflow with the active release pipeline fixes. Update README, AGENTS, compliance notes, and changelog references to match the new workflow names and usage patterns.
307 lines
12 KiB
Markdown
307 lines
12 KiB
Markdown
# vociferate
|
|
|
|
[](https://git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=push-validation.yml&branch=main&event=push)
|
|
[](https://git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=release.yml)
|
|
[](https://git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=update-release.yml)
|
|
[](https://s3.hrafn.xyz/aether-workflow-report-artefacts/vociferate/branch/main/coverage.html)
|
|
|
|
`vociferate` is an `Æther` release orchestration tool written in Go for repositories that
|
|
want changelog-driven versioning, automated release preparation, and repeatable
|
|
tag publication.
|
|
|
|
It started as release preparation glue, but it now covers the full release path:
|
|
recommend the next semantic version, promote `Unreleased` changelog entries,
|
|
commit and tag a release, and publish release notes/assets from the tagged
|
|
revision.
|
|
|
|
## Use In Other Repositories
|
|
|
|
Vociferate ships composite actions covering release preparation, release publication, coverage badge publishing, and pull request decoration.
|
|
Release tags now exist; pin all action and reusable-workflow references to the same released tag (for example, `@v1.1.0`) instead of `@main`.
|
|
|
|
For agentic coding partners, see [`AGENTS.md`](AGENTS.md) for a direct integration playbook, selection matrix, and copy-paste workflow patterns.
|
|
|
|
### `prepare` — update files, commit, and push tag
|
|
|
|
```yaml
|
|
name: 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
|
|
|
|
- uses: https://git.hrafn.xyz/aether/vociferate/prepare@v1.1.0
|
|
with:
|
|
version: ${{ inputs.version }}
|
|
|
|
publish:
|
|
needs: prepare
|
|
uses: https://git.hrafn.xyz/aether/vociferate/.gitea/workflows/update-release.yml@v1.1.0
|
|
with:
|
|
tag: ${{ needs.prepare.outputs.version }}
|
|
secrets: inherit
|
|
```
|
|
|
|
Downloads a prebuilt vociferate binary, runs it to update `CHANGELOG.md` and
|
|
`release-version`, then commits those changes to the default branch and pushes
|
|
the release tag. Does not require Go on the runner.
|
|
|
|
For repositories that embed the version inside source code, pass `version-file`
|
|
and `version-pattern`:
|
|
|
|
```yaml
|
|
- uses: https://git.hrafn.xyz/aether/vociferate/prepare@v1.1.0
|
|
with:
|
|
token: ${{ secrets.RELEASE_PAT }}
|
|
version-file: internal/myapp/version/version.go
|
|
version-pattern: 'const Version = "([^"]+)"'
|
|
git-add-files: CHANGELOG.md internal/myapp/version/version.go
|
|
```
|
|
|
|
`prepare` requires a PAT input for authenticated commit/push/tag operations.
|
|
Pass `token: ${{ secrets.RELEASE_PAT }}` when invoking the action.
|
|
|
|
### `publish` — create release with changelog notes
|
|
|
|
```yaml
|
|
name: Update Release
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: Semantic version to publish (for example v1.2.3)
|
|
required: true
|
|
|
|
jobs:
|
|
release:
|
|
uses: https://git.hrafn.xyz/aether/vociferate/.gitea/workflows/update-release.yml@v1.1.0
|
|
with:
|
|
tag: ${{ inputs.tag }}
|
|
secrets: inherit
|
|
```
|
|
|
|
Reads the matching section from `CHANGELOG.md` and creates or updates the
|
|
Gitea/GitHub release with those notes. The `version` input is optional — when
|
|
omitted it is derived from the current tag ref automatically.
|
|
|
|
The reusable `Update Release` workflow now runs preflight checks before publish to
|
|
fail fast when the release token is missing or lacks API access. Set
|
|
`secrets.RELEASE_PAT` and use it for prepare/publish release operations.
|
|
|
|
The `publish` action outputs `release-id` so you can upload additional release
|
|
assets after it runs:
|
|
|
|
```yaml
|
|
- id: publish
|
|
uses: https://git.hrafn.xyz/aether/vociferate/publish@v1.1.0
|
|
|
|
- name: Upload my binary
|
|
run: |
|
|
curl --fail-with-body -X POST \
|
|
-H "Authorization: token ${{ secrets.RELEASE_PAT }}" \
|
|
-H "Content-Type: application/octet-stream" \
|
|
"${{ github.api_url }}/repos/${{ github.repository }}/releases/${{ steps.publish.outputs.release-id }}/assets?name=myapp" \
|
|
--data-binary "@dist/myapp"
|
|
```
|
|
|
|
### `coverage-badge` - publish coverage report and badge
|
|
|
|
Run your coverage tests first, then call the action to generate `coverage.html`, `coverage-badge.svg`, and `coverage-summary.json`, upload them to S3-compatible storage, and emit output URLs.
|
|
|
|
```yaml
|
|
- name: Run tests with coverage
|
|
run: go test -covermode=atomic -coverprofile=coverage.out ./...
|
|
|
|
- id: coverage
|
|
uses: https://git.hrafn.xyz/aether/vociferate/coverage-badge@v1.1.0
|
|
with:
|
|
artefact-bucket-name: ${{ vars.ARTEFACT_BUCKET_NAME }}
|
|
artefact-bucket-endpoint: ${{ vars.ARTEFACT_BUCKET_ENDPONT }}
|
|
|
|
- name: Print coverage links
|
|
run: |
|
|
echo "Report: ${{ steps.coverage.outputs.report-url }}"
|
|
echo "Badge: ${{ steps.coverage.outputs.badge-url }}"
|
|
```
|
|
|
|
### `decorate-pr` - annotate pull requests with coverage and changes
|
|
|
|
Decorate pull requests with coverage badges, coverage percentages, and unreleased changelog entries. The action creates a new comment or updates an existing one on each run.
|
|
|
|
`decorate-pr` also runs a preflight comment API check so workflows fail early
|
|
with a clear message when token permissions are insufficient.
|
|
|
|
#### Basic Usage
|
|
|
|
```yaml
|
|
- name: Run tests with coverage
|
|
run: go test -covermode=atomic -coverprofile=coverage.out ./...
|
|
|
|
- id: coverage
|
|
uses: https://git.hrafn.xyz/aether/vociferate/coverage-badge@v1.1.0
|
|
with:
|
|
artefact-bucket-name: ${{ vars.ARTEFACT_BUCKET_NAME }}
|
|
artefact-bucket-endpoint: ${{ vars.ARTEFACT_BUCKET_ENDPONT }}
|
|
|
|
- name: Decorate pull request
|
|
if: github.event_name == 'pull_request'
|
|
uses: https://git.hrafn.xyz/aether/vociferate/decorate-pr@v1.1.0
|
|
with:
|
|
coverage-percentage: ${{ steps.coverage.outputs.total }}
|
|
badge-url: ${{ steps.coverage.outputs.badge-url }}
|
|
```
|
|
|
|
#### Changelog Gate (Strict Mode)
|
|
|
|
Enable changelog validation to enforce that code changes include `Unreleased` changelog entries. The gate fails the workflow in strict mode, or warns in soft mode:
|
|
|
|
```yaml
|
|
- name: Decorate pull request with changelog gate
|
|
if: github.event_name == 'pull_request'
|
|
uses: https://git.hrafn.xyz/aether/vociferate/decorate-pr@v1.1.0
|
|
with:
|
|
coverage-percentage: ${{ steps.coverage.outputs.total }}
|
|
badge-url: ${{ steps.coverage.outputs.badge-url }}
|
|
enable-changelog-gate: true
|
|
changelog-gate-mode: strict
|
|
changelog-gate-required-for: "code,behavior,security,workflow,tooling"
|
|
changelog-gate-allow-docs-only: true
|
|
changelog-gate-docs-globs: "docs/**,**.md,**.txt,**.rst"
|
|
changelog-gate-skip-labels: "skip-changelog"
|
|
```
|
|
|
|
The gate automatically:
|
|
|
|
- Parses diffs to detect docs-only PRs (skips requirement for doc-only changes)
|
|
- Counts `Unreleased` additions using section-aware parsing (ignores edits outside the section)
|
|
- Checks PR labels for skip exemptions (for example, `skip-changelog`)
|
|
- Outputs decision status and remediation guidance in the PR comment
|
|
- Handles both strict (fail) and soft (warn) modes
|
|
|
|
Decision outputs enable downstream workflow logic:
|
|
|
|
```yaml
|
|
- name: Decorate PR and check gate
|
|
id: decorate
|
|
if: github.event_name == 'pull_request'
|
|
uses: https://git.hrafn.xyz/aether/vociferate/decorate-pr@v1.1.0
|
|
with:
|
|
coverage-percentage: ${{ steps.coverage.outputs.total }}
|
|
badge-url: ${{ steps.coverage.outputs.badge-url }}
|
|
enable-changelog-gate: true
|
|
changelog-gate-mode: soft
|
|
|
|
- name: Gate decision
|
|
run: |
|
|
echo "Gate passed: ${{ steps.decorate.outputs.gate-passed }}"
|
|
echo "Is docs-only PR: ${{ steps.decorate.outputs.docs-only }}"
|
|
echo "Unreleased additions: ${{ steps.decorate.outputs.unreleased-additions-count }}"
|
|
if [[ "${{ steps.decorate.outputs.gate-passed }}" == "false" ]]; then
|
|
echo "Gate failure reason: ${{ steps.decorate.outputs.gate-failure-reason }}"
|
|
fi
|
|
```
|
|
|
|
The action automatically finds existing vociferate comments by their marker and updates them instead of creating duplicates. This keeps PR timelines clean while keeping review information current.
|
|
|
|
## Why The Name
|
|
|
|
> **vociferate** _(verb)_: to cry out loudly or forcefully.
|
|
|
|
Long story short: vociferating into the Æther is synonymous screaming into the
|
|
void.
|
|
|
|
If updating changelogs and documenting releases has ever felt like that, this
|
|
tool is for you.
|
|
|
|
## Build
|
|
|
|
Build with just:
|
|
|
|
```bash
|
|
just go-build
|
|
```
|
|
|
|
Or directly with Go:
|
|
|
|
```bash
|
|
go build -o dist/vociferate ./cmd/vociferate
|
|
```
|
|
|
|
## Usage
|
|
|
|
Prepare release files:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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.
|
|
|
|
During prepare, vociferate can normalize changelog heading links when it can determine the repository URL (from CI environment variables or `origin` git remote). Actions automatically forward `${{ vars.VOCIFERATE_REPOSITORY_URL }}` to `VOCIFERATE_REPOSITORY_URL`, which has highest priority for changelog link generation. This value should be the server/base URL only, for example `https://git.hrafn.xyz` or `https://git.hrafn.xyz/git`, not a full repository URL.
|
|
|
|
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
|
|
|
|
```bash
|
|
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.
|
|
- `Prepare Release` then calls `Do Release` directly via reusable `workflow_call` with the resolved tag.
|
|
- `Do Release` reads the matching changelog section from that tagged revision, creates or updates the release, and uploads prebuilt binaries.
|
|
|
|
Calling `Do Release` directly avoids environments where tag pushes from workflow tokens do not emit a follow-up workflow trigger event.
|
|
|
|
## 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.
|