19 Commits

Author SHA1 Message Date
gitea-actions[bot]
5dad65cc3b release: prepare v1.0.0 2026-03-21 01:13:41 +00:00
Micheal Wilkinson
e99527f68b docs: refresh README wording and stylization
All checks were successful
Push Validation / coverage-badge (push) Successful in 1m43s
Push Validation / recommend-release (push) Successful in 38s
2026-03-21 00:48:46 +00:00
Micheal Wilkinson
f314d7da1b feat: sync docs action tags during prepare 2026-03-21 00:29:14 +00:00
Micheal Wilkinson
21a68647f3 refactor: normalize changelog filename docs 2026-03-21 00:27:32 +00:00
Micheal Wilkinson
ba715d9965 feat: migrate changelog path to CHANGELOG.md 2026-03-21 00:27:18 +00:00
Micheal Wilkinson
62f637614d test: require CHANGELOG.md defaults 2026-03-21 00:26:43 +00:00
Micheal Wilkinson
7d6ae6f486 docs: pin README examples to release tags 2026-03-21 00:20:08 +00:00
Micheal Wilkinson
16274ea1e5 feat: add reusable coverage-badge action 2026-03-21 00:18:21 +00:00
gitea-actions[bot]
8d9cc33802 release: prepare v0.2.0 2026-03-21 00:14:00 +00:00
Micheal Wilkinson
33e1d7c9cc fix: replace workflow step summaries
All checks were successful
Push Validation / validate (push) Successful in 1m46s
2026-03-21 00:03:26 +00:00
Micheal Wilkinson
4c1a0b87eb docs: sweep markdown display urls
All checks were successful
Push Validation / validate (push) Successful in 1m38s
2026-03-20 23:56:43 +00:00
Micheal Wilkinson
a139417f02 feat: emit protocol-relative display urls 2026-03-20 23:56:42 +00:00
Micheal Wilkinson
788ef1b49d test: require protocol-relative display links 2026-03-20 23:53:34 +00:00
Micheal Wilkinson
f9f2c6ab62 docs: record workflow var repository URL support 2026-03-20 23:52:17 +00:00
Micheal Wilkinson
1959aa33d8 feat: read repository base URL from workflow vars 2026-03-20 23:52:12 +00:00
Micheal Wilkinson
6a83abe4db test: cover base URL repository override 2026-03-20 23:51:57 +00:00
Micheal Wilkinson
2d3c27460f docs: document repository URL override env var 2026-03-20 23:40:45 +00:00
Micheal Wilkinson
edb8508e48 feat: support repository URL override env var 2026-03-20 23:40:23 +00:00
Micheal Wilkinson
f79eda21c1 test: require repository URL override precedence 2026-03-20 23:40:07 +00:00
15 changed files with 714 additions and 139 deletions

View File

@@ -29,6 +29,7 @@ jobs:
shell: bash shell: bash
env: env:
RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }} RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SUMMARY_FILE: ${{ runner.temp }}/do-release-summary.md
steps: steps:
- name: Checkout tagged revision - name: Checkout tagged revision
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -128,7 +129,21 @@ jobs:
echo "- Tag: ${TAG_NAME}" echo "- Tag: ${TAG_NAME}"
echo "- Release notes sourced from changelog entry ${RELEASE_VERSION}." echo "- Release notes sourced from changelog entry ${RELEASE_VERSION}."
echo "- Published assets: vociferate_${RELEASE_VERSION}_linux_amd64, vociferate_${RELEASE_VERSION}_linux_arm64, checksums.txt" echo "- Published assets: vociferate_${RELEASE_VERSION}_linux_amd64, vociferate_${RELEASE_VERSION}_linux_arm64, checksums.txt"
} >> "$GITHUB_STEP_SUMMARY" } >> "$SUMMARY_FILE"
- name: Summary
if: ${{ always() }}
run: |
set -euo pipefail
echo 'Summary'
echo
if [[ -s "$SUMMARY_FILE" ]]; then
cat "$SUMMARY_FILE"
else
echo 'No summary generated.'
fi
validate: validate:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -145,6 +160,8 @@ jobs:
defaults: defaults:
run: run:
shell: bash shell: bash
env:
SUMMARY_FILE: ${{ runner.temp }}/do-release-validate-summary.md
steps: steps:
- name: Checkout tagged revision - name: Checkout tagged revision
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -211,4 +228,18 @@ jobs:
echo "- Asset: ${asset_name}" echo "- Asset: ${asset_name}"
echo "- Binary executed successfully via --help." echo "- Binary executed successfully via --help."
echo "- --recommend failed as expected on the tagged checkout because Unreleased is empty." echo "- --recommend failed as expected on the tagged checkout because Unreleased is empty."
} >> "$GITHUB_STEP_SUMMARY" } >> "$SUMMARY_FILE"
- name: Summary
if: ${{ always() }}
run: |
set -euo pipefail
echo 'Summary'
echo
if [[ -s "$SUMMARY_FILE" ]]; then
cat "$SUMMARY_FILE"
else
echo 'No summary generated.'
fi

View File

@@ -23,6 +23,8 @@ jobs:
defaults: defaults:
run: run:
shell: bash shell: bash
env:
SUMMARY_FILE: ${{ runner.temp }}/prepare-release-summary.md
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -44,13 +46,39 @@ jobs:
id: cache-token id: cache-token
run: echo "value=${GITHUB_SHA}" >> "$GITHUB_OUTPUT" run: echo "value=${GITHUB_SHA}" >> "$GITHUB_OUTPUT"
- name: Resolve release tag
id: resolve-version
run: |
set -euo pipefail
provided_version="$(printf '%s' "${{ inputs.version }}" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
if [[ -z "$provided_version" ]]; then
release_tag="$(go run ./cmd/vociferate --recommend --root .)"
elif [[ "$provided_version" == v* ]]; then
release_tag="$provided_version"
else
release_tag="v${provided_version}"
fi
echo "tag=${release_tag}" >> "$GITHUB_OUTPUT"
- name: Update agent docs action tags
run: |
set -euo pipefail
release_tag="${{ steps.resolve-version.outputs.tag }}"
for file in README.md AGENTS.md; do
sed -E -i "s/@v[0-9]+\.[0-9]+\.[0-9]+/@${release_tag}/g" "$file"
done
- name: Prepare and tag release - name: Prepare and tag release
id: prepare id: prepare
uses: ./prepare uses: ./prepare
env: env:
VOCIFERATE_CACHE_TOKEN: ${{ steps.cache-token.outputs.value }} VOCIFERATE_CACHE_TOKEN: ${{ steps.cache-token.outputs.value }}
with: with:
version: ${{ inputs.version }} version: ${{ steps.resolve-version.outputs.tag }}
git-add-files: CHANGELOG.md release-version README.md AGENTS.md
- name: Summarize prepared release - name: Summarize prepared release
run: | run: |
@@ -61,7 +89,21 @@ jobs:
echo echo
echo "- Tag pushed: ${tag}" echo "- Tag pushed: ${tag}"
echo "- Calling Do Release workflow for ${tag}." echo "- Calling Do Release workflow for ${tag}."
} >> "$GITHUB_STEP_SUMMARY" } >> "$SUMMARY_FILE"
- name: Summary
if: ${{ always() }}
run: |
set -euo pipefail
echo 'Summary'
echo
if [[ -s "$SUMMARY_FILE" ]]; then
cat "$SUMMARY_FILE"
else
echo 'No summary generated.'
fi
publish: publish:
needs: prepare needs: prepare

View File

@@ -8,7 +8,7 @@ on:
- "*" - "*"
jobs: jobs:
validate: coverage-badge:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: docker.io/catthehacker/ubuntu:act-latest container: docker.io/catthehacker/ubuntu:act-latest
defaults: defaults:
@@ -22,6 +22,7 @@ jobs:
AWS_SECRET_ACCESS_KEY: ${{ secrets.ARTEFACT_BUCKET_WRITE_ACCESS_SECRET }} AWS_SECRET_ACCESS_KEY: ${{ secrets.ARTEFACT_BUCKET_WRITE_ACCESS_SECRET }}
AWS_DEFAULT_REGION: ${{ vars.ARTEFACT_BUCKET_REGION }} AWS_DEFAULT_REGION: ${{ vars.ARTEFACT_BUCKET_REGION }}
AWS_EC2_METADATA_DISABLED: true AWS_EC2_METADATA_DISABLED: true
SUMMARY_FILE: ${{ runner.temp }}/push-validation-summary.md
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -34,92 +35,57 @@ jobs:
cache: true cache: true
cache-dependency-path: go.sum cache-dependency-path: go.sum
- name: Install AWS CLI v2
uses: ankurk91/install-aws-cli-action@v1
- name: Verify AWS CLI
run: aws --version
- name: Run full unit test suite with coverage - name: Run full unit test suite with coverage
id: coverage
run: | run: |
set -euo pipefail set -euo pipefail
go test -covermode=atomic -coverprofile=coverage.out ./... go test -covermode=atomic -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
total="$(go tool cover -func=coverage.out | awk '/^total:/ {sub(/%/, "", $3); print $3}')" - name: Publish coverage badge artefacts
printf '{\n "total": "%s"\n}\n' "$total" > coverage-summary.json id: coverage
printf 'total=%s\n' "$total" >> "$GITHUB_OUTPUT" uses: ./coverage-badge
with:
artefact-bucket-name: ${{ vars.ARTEFACT_BUCKET_NAME }}
artefact-bucket-endpoint: ${{ vars.ARTEFACT_BUCKET_ENDPONT }}
summary-file: ${{ env.SUMMARY_FILE }}
- name: Generate coverage badge - name: Summary
env: if: ${{ always() }}
COVERAGE_TOTAL: ${{ steps.coverage.outputs.total }}
run: | run: |
set -euo pipefail set -euo pipefail
color="$(awk -v total="$COVERAGE_TOTAL" 'BEGIN { echo 'Summary'
if (total >= 80) print "brightgreen"; echo
else if (total >= 70) print "green";
else if (total >= 60) print "yellowgreen";
else if (total >= 50) print "yellow";
else print "red";
}')"
cat > coverage-badge.svg <<EOF if [[ -s "$SUMMARY_FILE" ]]; then
<svg xmlns="http://www.w3.org/2000/svg" width="126" height="20" role="img" aria-label="coverage: ${COVERAGE_TOTAL}%"> cat "$SUMMARY_FILE"
<linearGradient id="smooth" x2="0" y2="100%"> else
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/> echo 'No summary generated.'
<stop offset="1" stop-opacity=".1"/> fi
</linearGradient>
<clipPath id="round">
<rect width="126" height="20" rx="3" fill="#fff"/>
</clipPath>
<g clip-path="url(#round)">
<rect width="63" height="20" fill="#555"/>
<rect x="63" width="63" height="20" fill="${color}"/>
<rect width="126" height="20" fill="url(#smooth)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
<text x="32.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
<text x="32.5" y="14">coverage</text>
<text x="93.5" y="15" fill="#010101" fill-opacity=".3">${COVERAGE_TOTAL}%</text>
<text x="93.5" y="14">${COVERAGE_TOTAL}%</text>
</g>
</svg>
EOF
- name: Upload branch coverage artefacts recommend-release:
id: upload runs-on: ubuntu-latest
run: | container: docker.io/catthehacker/ubuntu:act-latest
set -euo pipefail needs: coverage-badge
if: ${{ github.ref == 'refs/heads/main' }}
defaults:
run:
shell: bash
env:
SUMMARY_FILE: ${{ runner.temp }}/push-validation-recommend-summary.md
steps:
- name: Checkout
uses: actions/checkout@v4
aws configure set default.s3.addressing_style path - name: Setup Go
uses: actions/setup-go@v5
repo_name="${GITHUB_REPOSITORY##*/}" with:
prefix="${repo_name}/branch/${GITHUB_REF_NAME}" go-version: '1.26.1'
report_url="${ARTEFACT_BUCKET_ENDPONT%/}/${ARTEFACT_BUCKET_NAME}/${prefix}/coverage.html" check-latest: true
badge_url="${ARTEFACT_BUCKET_ENDPONT%/}/${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-badge.svg" cache: true
cache-dependency-path: go.sum
aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp coverage.html "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage.html" --content-type text/html
aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp coverage-badge.svg "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-badge.svg" --content-type image/svg+xml
aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp coverage-summary.json "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-summary.json" --content-type application/json
printf 'report_url=%s\n' "$report_url" >> "$GITHUB_OUTPUT"
printf 'badge_url=%s\n' "$badge_url" >> "$GITHUB_OUTPUT"
- name: Add coverage summary
run: |
{
echo '## Coverage'
echo
echo '- Total: `${{ steps.coverage.outputs.total }}%`'
echo '- Report: ${{ steps.upload.outputs.report_url }}'
echo '- Badge: ${{ steps.upload.outputs.badge_url }}'
} >> "$GITHUB_STEP_SUMMARY"
- name: Recommend next release tag on main pushes - name: Recommend next release tag on main pushes
if: ${{ github.ref == 'refs/heads/main' }}
run: | run: |
set -euo pipefail set -euo pipefail
@@ -129,7 +95,7 @@ jobs:
echo '## Release Recommendation' echo '## Release Recommendation'
echo echo
echo "- Recommended next tag: \`${recommended_tag}\`" echo "- Recommended next tag: \`${recommended_tag}\`"
} >> "$GITHUB_STEP_SUMMARY" } >> "$SUMMARY_FILE"
else else
recommendation_error="$(tr '\n' ' ' < release-recommendation.err | sed 's/[[:space:]]\+/ /g' | sed 's/^ //; s/ $//')" recommendation_error="$(tr '\n' ' ' < release-recommendation.err | sed 's/[[:space:]]\+/ /g' | sed 's/^ //; s/ $//')"
echo "::warning::${recommendation_error}" echo "::warning::${recommendation_error}"
@@ -138,5 +104,19 @@ jobs:
echo '## Release Recommendation' echo '## Release Recommendation'
echo echo
echo "- No recommended tag emitted: ${recommendation_error}" echo "- No recommended tag emitted: ${recommendation_error}"
} >> "$GITHUB_STEP_SUMMARY" } >> "$SUMMARY_FILE"
fi
- name: Summary
if: ${{ always() }}
run: |
set -euo pipefail
echo 'Summary'
echo
if [[ -s "$SUMMARY_FILE" ]]; then
cat "$SUMMARY_FILE"
else
echo 'No summary generated.'
fi fi

197
AGENTS.md Normal file
View File

@@ -0,0 +1,197 @@
# Agent Integration Guide
This guide is for agentic coding partners that need to integrate the composite actions published by this repository.
## Source Of Truth
Pin all action references to a released tag (for example `@v1.0.0`) and keep all vociferate references on the same tag in a workflow.
Published composite actions:
- `git.hrafn.xyz/aether/vociferate@v1.0.0` (root action)
- `git.hrafn.xyz/aether/vociferate/prepare@v1.0.0`
- `git.hrafn.xyz/aether/vociferate/publish@v1.0.0`
- `git.hrafn.xyz/aether/vociferate/coverage-badge@v1.0.0`
## Action Selection Matrix
Use this when deciding which action to call:
- Choose `prepare` when you need to update changelog/version files, commit, and push a release tag.
- Choose `publish` when a tag already exists and you need to create or update release notes/assets.
- Choose `coverage-badge` after tests have produced `coverage.out` and you need coverage artefacts uploaded.
- Choose root `vociferate` for direct recommend/prepare logic without commit/tag/push behavior.
## Preconditions
Apply these checks before invoking actions:
- Checkout repository first.
- For prepare/publish flows that depend on tags/history, use full history checkout (`fetch-depth: 0`).
- Use valid credentials in `github.token` (or explicit token input for `publish` when needed).
- Set required vars/secrets for coverage uploads:
- `vars.ARTEFACT_BUCKET_NAME`
- `vars.ARTEFACT_BUCKET_ENDPONT`
- `secrets.ARTEFACT_BUCKET_WRITE_ACCESS_KEY`
- `secrets.ARTEFACT_BUCKET_WRITE_ACCESS_SECRET`
- For externally visible changelog links, set `vars.VOCIFERATE_REPOSITORY_URL` to the server/base URL only.
## Changelog Format Guidance
Agents should keep `CHANGELOG.md` in a Keep a Changelog compatible structure because vociferate derives versions and release notes from headings.
Required conventions:
- Keep the top-level heading as `# Changelog`.
- Maintain an `## [Unreleased]` section.
- Keep the standard subsections under `Unreleased` in this order:
- `### Breaking`
- `### Added`
- `### Changed`
- `### Removed`
- `### Fixed`
- Record releases with headings like `## [1.2.3] - YYYY-MM-DD`.
- Use bullet entries under subsections (for example `- Added new publish output`).
- Preserve or regenerate bottom reference links (`[Unreleased]: ...`, `[1.2.3]: ...`) instead of mixing inline heading links.
Semver behavior used by recommendation logic:
- `Breaking` or `Removed` entries trigger a major bump.
- `Added` entries trigger a minor bump.
- Otherwise recommendation falls back to a patch bump.
Minimal template:
```markdown
# Changelog
## [Unreleased]
### Breaking
### Added
### Changed
### Removed
### Fixed
```
## Minimal Integration Patterns
### 1. Prepare Then Publish
```yaml
jobs:
prepare:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: prepare
uses: git.hrafn.xyz/aether/vociferate/prepare@v1.0.0
publish:
needs: prepare
uses: aether/vociferate/.gitea/workflows/do-release.yml@v1.0.0
with:
tag: ${{ needs.prepare.outputs.version }}
secrets: inherit
```
### 2. Publish Existing Tag
```yaml
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: publish
uses: git.hrafn.xyz/aether/vociferate/publish@v1.0.0
with:
version: v1.2.3
```
### 3. Coverage Badge Publication
```yaml
jobs:
coverage:
runs-on: ubuntu-latest
env:
AWS_ACCESS_KEY_ID: ${{ secrets.ARTEFACT_BUCKET_WRITE_ACCESS_KEY }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.ARTEFACT_BUCKET_WRITE_ACCESS_SECRET }}
AWS_DEFAULT_REGION: ${{ vars.ARTEFACT_BUCKET_REGION }}
AWS_EC2_METADATA_DISABLED: true
steps:
- uses: actions/checkout@v4
- name: Run tests with coverage
run: go test -covermode=atomic -coverprofile=coverage.out ./...
- id: badge
uses: git.hrafn.xyz/aether/vociferate/coverage-badge@v1.0.0
with:
artefact-bucket-name: ${{ vars.ARTEFACT_BUCKET_NAME }}
artefact-bucket-endpoint: ${{ vars.ARTEFACT_BUCKET_ENDPONT }}
```
## Inputs And Outputs Cheatsheet
### prepare
Common inputs:
- `version` (optional override)
- `version-file` (optional)
- `version-pattern` (optional)
- `changelog` (optional)
Primary output:
- `version` (resolved tag, for example `v1.2.3`)
### publish
Common inputs:
- `token` (optional, defaults to workflow token)
- `version` (optional if running from tag ref)
- `changelog` (optional)
Primary outputs:
- `release-id`
- `tag`
- `version`
### coverage-badge
Required inputs:
- `artefact-bucket-name`
- `artefact-bucket-endpoint`
Useful optional inputs:
- `coverage-profile` (default `coverage.out`)
- `summary-file` (append markdown summary)
Primary outputs:
- `total`
- `report-url`
- `badge-url`
## Guardrails For Agents
Use these rules to avoid common automation mistakes:
- Do not mix action tags in one workflow update.
- Do not assume a release workflow will run from a tag push in all environments; reusable workflow call paths are supported.
- Do not treat `VOCIFERATE_REPOSITORY_URL` as a full repository URL; it must be a base URL.
- Keep displayed URLs protocol-relative (`//`) when writing markdown/browser-facing outputs.
- If a workflow environment does not support `GITHUB_STEP_SUMMARY`, append markdown to a file and print it in a final `Summary` step.

View File

@@ -2,8 +2,8 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), The format is based on [Keep a Changelog](//keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](//semver.org/spec/v2.0.0.html).
A `### Breaking` section is used in addition to Keep a Changelog's standard sections to explicitly document changes that are backwards-incompatible but would otherwise appear under `### Changed`. Entries under `### Breaking` trigger a major version bump in automated release recommendation logic. A `### Breaking` section is used in addition to Keep a Changelog's standard sections to explicitly document changes that are backwards-incompatible but would otherwise appear under `### Changed`. Entries under `### Breaking` trigger a major version bump in automated release recommendation logic.
@@ -13,18 +13,54 @@ A `### Breaking` section is used in addition to Keep a Changelog's standard sect
### Added ### Added
- Added a project LICENSE file.
### Changed ### Changed
### Removed ### Removed
### Fixed ### Fixed
## [1.0.0] - 2026-03-21
### Breaking
### Added
### Changed
- Canonical changelog filename is now `CHANGELOG.md`, and action/code defaults were updated to match.
- README now uses `Æther` stylization in prose and corrects released-tag guidance wording.
### Removed
### Fixed
## [0.2.0] - 2026-03-21
### Breaking
### Added
- Added a project LICENSE file.
- Root and prepare actions now read `${{ vars.VOCIFERATE_REPOSITORY_URL }}` and forward it to `VOCIFERATE_REPOSITORY_URL` for repository URL override.
- Added a published `coverage-badge` composite action for generating and uploading coverage report/badge artefacts for reuse across repositories.
- Added `AGENTS.md`, an explicit integration guide for agentic coding partners using vociferate composite actions.
### Changed
- Push validation now handles coverage artefact and badge generation in a dedicated `coverage-badge` job, with release recommendation isolated in a separate dependent job.
- Push validation now calls the reusable `./coverage-badge` composite action for coverage badge generation and publication.
### Removed
### Fixed
- Browser-facing URLs emitted in generated changelog links, workflow summaries, and markdown now use protocol-relative `//` forms.
- Release workflows now collect summary markdown into portable temp files and print it in explicit `Summary` steps instead of relying on unsupported `GITHUB_STEP_SUMMARY` output.
- Prepare now recreates the standard `Unreleased` section headers after promoting notes into a tagged release entry. - Prepare now recreates the standard `Unreleased` section headers after promoting notes into a tagged release entry.
- First-release recommendation remains `v1.0.0` when no prior releases exist in the changelog. - First-release recommendation remains `v1.0.0` when no prior releases exist in the changelog.
- Do Release smoke validation now expects `--recommend` to fail on tagged release checkouts where `Unreleased` is intentionally empty. - Do Release smoke validation now expects `--recommend` to fail on tagged release checkouts where `Unreleased` is intentionally empty.
- Changelog reference links now use compare URLs (`previous...current` for releases and `latest...main` for Unreleased), with first release links comparing from the repository's first commit short hash. - Changelog reference links now use compare URLs (`previous...current` for releases and `latest...main` for Unreleased), with first release links comparing from the repository's first commit short hash.
- Repository URL derivation now supports `VOCIFERATE_REPOSITORY_URL` as the highest-priority base-URL override for changelog link generation.
## [0.1.0] - 2026-03-20 ## [0.1.0] - 2026-03-20
@@ -60,5 +96,7 @@ A `### Breaking` section is used in addition to Keep a Changelog's standard sect
- Project/automation rename from `releaseprep` to `vociferate` (entrypoint, package paths, outputs). - Project/automation rename from `releaseprep` to `vociferate` (entrypoint, package paths, outputs).
- README guidance focused on primary cross-repository reuse workflows. - README guidance focused on primary cross-repository reuse workflows.
[Unreleased]: http://teapot:3000/aether/vociferate/src/branch/main [Unreleased]: //git.hrafn.xyz/aether/vociferate/compare/v1.0.0...main
[0.1.0]: http://teapot:3000/aether/vociferate/releases/tag/v0.1.0 [1.0.0]: //git.hrafn.xyz/aether/vociferate/compare/v0.2.0...v1.0.0
[0.2.0]: //git.hrafn.xyz/aether/vociferate/compare/v0.1.0...v0.2.0
[0.1.0]: //git.hrafn.xyz/aether/vociferate/compare/2060af6...v0.1.0

View File

@@ -1,11 +1,11 @@
# vociferate # vociferate
[![Main Validation](https://git.hrafn.xyz/aether/vociferate/actions/workflows/push-validation.yml/badge.svg?branch=main&event=push)](https://git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=push-validation.yml&branch=main&event=push) [![Main Validation](//git.hrafn.xyz/aether/vociferate/actions/workflows/push-validation.yml/badge.svg?branch=main&event=push)](//git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=push-validation.yml&branch=main&event=push)
[![Prepare Release](https://git.hrafn.xyz/aether/vociferate/actions/workflows/prepare-release.yml/badge.svg?event=workflow_dispatch)](https://git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=prepare-release.yml) [![Prepare Release](//git.hrafn.xyz/aether/vociferate/actions/workflows/prepare-release.yml/badge.svg?event=workflow_dispatch)](//git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=prepare-release.yml)
[![Do Release](https://git.hrafn.xyz/aether/vociferate/actions/workflows/do-release.yml/badge.svg?event=push)](https://git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=do-release.yml) [![Do Release](//git.hrafn.xyz/aether/vociferate/actions/workflows/do-release.yml/badge.svg?event=push)](//git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=do-release.yml)
[![Coverage](https://s3.hrafn.xyz/aether-workflow-report-artefacts/vociferate/branch/main/coverage-badge.svg)](https://s3.hrafn.xyz/aether-workflow-report-artefacts/vociferate/branch/main/coverage.html) [![Coverage](//s3.hrafn.xyz/aether-workflow-report-artefacts/vociferate/branch/main/coverage-badge.svg)](//s3.hrafn.xyz/aether-workflow-report-artefacts/vociferate/branch/main/coverage.html)
`vociferate` is an `aether` release orchestration tool written in Go for repositories that `vociferate` is an `Æther` release orchestration tool written in Go for repositories that
want changelog-driven versioning, automated release preparation, and repeatable want changelog-driven versioning, automated release preparation, and repeatable
tag publication. tag publication.
@@ -16,8 +16,10 @@ revision.
## Use In Other Repositories ## Use In Other Repositories
Vociferate ships two composite actions that together cover the full release flow. Vociferate ships three composite actions covering release preparation, release publication, and coverage badge publishing.
Until release tags are created, reference `@main`. Once tags exist again, pin both actions to the same released tag. Release tags now exist; pin all action and reusable-workflow references to the same released tag (for example, `@v1.0.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 ### `prepare` — update files, commit, and push tag
@@ -39,19 +41,19 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: git.hrafn.xyz/aether/vociferate/prepare@main - uses: git.hrafn.xyz/aether/vociferate/prepare@v1.0.0
with: with:
version: ${{ inputs.version }} version: ${{ inputs.version }}
publish: publish:
needs: prepare needs: prepare
uses: aether/vociferate/.gitea/workflows/do-release.yml@main uses: aether/vociferate/.gitea/workflows/do-release.yml@v1.0.0
with: with:
tag: ${{ needs.prepare.outputs.version }} tag: ${{ needs.prepare.outputs.version }}
secrets: inherit secrets: inherit
``` ```
Downloads a prebuilt vociferate binary, runs it to update `changelog.md` and Downloads a prebuilt vociferate binary, runs it to update `CHANGELOG.md` and
`release-version`, then commits those changes to the default branch and pushes `release-version`, then commits those changes to the default branch and pushes
the release tag. Does not require Go on the runner. the release tag. Does not require Go on the runner.
@@ -59,11 +61,11 @@ For repositories that embed the version inside source code, pass `version-file`
and `version-pattern`: and `version-pattern`:
```yaml ```yaml
- uses: git.hrafn.xyz/aether/vociferate/prepare@main - uses: git.hrafn.xyz/aether/vociferate/prepare@v1.0.0
with: with:
version-file: internal/myapp/version/version.go version-file: internal/myapp/version/version.go
version-pattern: 'const Version = "([^"]+)"' version-pattern: 'const Version = "([^"]+)"'
git-add-files: changelog.md internal/myapp/version/version.go git-add-files: CHANGELOG.md internal/myapp/version/version.go
``` ```
`prepare` uses `github.token` internally for authenticated fetch/push operations, `prepare` uses `github.token` internally for authenticated fetch/push operations,
@@ -83,13 +85,13 @@ on:
jobs: jobs:
release: release:
uses: aether/vociferate/.gitea/workflows/do-release.yml@main uses: aether/vociferate/.gitea/workflows/do-release.yml@v1.0.0
with: with:
tag: ${{ inputs.tag }} tag: ${{ inputs.tag }}
secrets: inherit secrets: inherit
``` ```
Reads the matching section from `changelog.md` and creates or updates the Reads the matching section from `CHANGELOG.md` and creates or updates the
Gitea/GitHub release with those notes. The `version` input is optional — when Gitea/GitHub release with those notes. The `version` input is optional — when
omitted it is derived from the current tag ref automatically. omitted it is derived from the current tag ref automatically.
@@ -98,7 +100,7 @@ assets after it runs:
```yaml ```yaml
- id: publish - id: publish
uses: git.hrafn.xyz/aether/vociferate/publish@main uses: git.hrafn.xyz/aether/vociferate/publish@v1.0.0
- name: Upload my binary - name: Upload my binary
run: | run: |
@@ -109,6 +111,26 @@ assets after it runs:
--data-binary "@dist/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: git.hrafn.xyz/aether/vociferate/coverage-badge@v1.0.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 }}"
```
## Why The Name ## Why The Name
> **vociferate** _(verb)_: to cry out loudly or forcefully. > **vociferate** _(verb)_: to cry out loudly or forcefully.
@@ -163,11 +185,11 @@ Defaults:
- `version-file`: `release-version` - `version-file`: `release-version`
- `version-pattern`: `^\s*([^\r\n]+)\s*$` - `version-pattern`: `^\s*([^\r\n]+)\s*$`
- `changelog`: `changelog.md` - `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 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). If you prefer changelog headings to stay plain while tags are being rebuilt, leave the changelog as plain headings and avoid retaining historical release-tag links. 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. 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.
@@ -183,7 +205,7 @@ just go-test
Releases use two workflows: 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` 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. - `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. - `Do Release` reads the matching changelog section from that tagged revision, creates or updates the release, and uploads prebuilt binaries.

View File

@@ -16,7 +16,7 @@ inputs:
changelog: changelog:
description: Path to changelog file relative to repository root. description: Path to changelog file relative to repository root.
required: false required: false
default: changelog.md default: CHANGELOG.md
recommend: recommend:
description: If true, print recommended next release tag. description: If true, print recommended next release tag.
required: false required: false
@@ -126,6 +126,7 @@ runs:
env: env:
VOCIFERATE_BIN: ${{ steps.resolve-binary.outputs.binary_path }} VOCIFERATE_BIN: ${{ steps.resolve-binary.outputs.binary_path }}
USE_BINARY: ${{ steps.resolve-binary.outputs.use_binary }} USE_BINARY: ${{ steps.resolve-binary.outputs.use_binary }}
VOCIFERATE_REPOSITORY_URL: ${{ vars.VOCIFERATE_REPOSITORY_URL }}
run: | run: |
set -euo pipefail set -euo pipefail

View File

@@ -12,7 +12,7 @@ import (
func TestMainRecommendPrintsTag(t *testing.T) { func TestMainRecommendPrintsTag(t *testing.T) {
root := t.TempDir() root := t.TempDir()
writeFile(t, filepath.Join(root, "release-version"), "1.1.6\n") writeFile(t, filepath.Join(root, "release-version"), "1.1.6\n")
writeFile(t, filepath.Join(root, "changelog.md"), "# Changelog\n\n## [Unreleased]\n\n### Added\n\n- Feature.\n\n## [1.1.6] - 2017-12-20\n") writeFile(t, filepath.Join(root, "CHANGELOG.md"), "# Changelog\n\n## [Unreleased]\n\n### Added\n\n- Feature.\n\n## [1.1.6] - 2017-12-20\n")
stdout, stderr, code := runMain(t, "--recommend", "--root", root) stdout, stderr, code := runMain(t, "--recommend", "--root", root)
if code != 0 { if code != 0 {
@@ -37,7 +37,7 @@ func TestMainPrepareUpdatesFiles(t *testing.T) {
root := t.TempDir() root := t.TempDir()
writeFile(t, filepath.Join(root, ".git", "config"), "[remote \"origin\"]\n\turl = git@git.hrafn.xyz:aether/vociferate.git\n") writeFile(t, filepath.Join(root, ".git", "config"), "[remote \"origin\"]\n\turl = git@git.hrafn.xyz:aether/vociferate.git\n")
writeFile(t, filepath.Join(root, "release-version"), "1.1.6\n") writeFile(t, filepath.Join(root, "release-version"), "1.1.6\n")
writeFile(t, filepath.Join(root, "changelog.md"), "# Changelog\n\n## [Unreleased]\n\n### Fixed\n\n- Patch note.\n\n## [1.1.6] - 2017-12-20\n") writeFile(t, filepath.Join(root, "CHANGELOG.md"), "# Changelog\n\n## [Unreleased]\n\n### Fixed\n\n- Patch note.\n\n## [1.1.6] - 2017-12-20\n")
_, stderr, code := runMain(t, "--version", "v1.1.7", "--date", "2026-03-20", "--root", root) _, stderr, code := runMain(t, "--version", "v1.1.7", "--date", "2026-03-20", "--root", root)
if code != 0 { if code != 0 {

168
coverage-badge/action.yml Normal file
View File

@@ -0,0 +1,168 @@
name: vociferate/coverage-badge
description: >
Generate coverage report artefacts, publish them to object storage,
and expose report URLs for workflow summaries.
inputs:
coverage-profile:
description: Path to the Go coverage profile file.
required: false
default: coverage.out
coverage-html:
description: Output path for the rendered HTML coverage report.
required: false
default: coverage.html
coverage-badge:
description: Output path for the generated SVG badge.
required: false
default: coverage-badge.svg
coverage-summary:
description: Output path for the generated coverage summary JSON.
required: false
default: coverage-summary.json
artefact-bucket-name:
description: S3 bucket name for published coverage artefacts.
required: true
artefact-bucket-endpoint:
description: Endpoint URL used for S3-compatible uploads.
required: true
branch-name:
description: Branch name used in the publication path.
required: false
default: ''
repository-name:
description: Repository name used in the publication path.
required: false
default: ''
summary-file:
description: Optional file path to append markdown summary output.
required: false
default: ''
outputs:
total:
description: Computed coverage percentage.
value: ${{ steps.generate.outputs.total }}
report-url:
description: Browser-facing URL for the published coverage report.
value: ${{ steps.upload.outputs.report_url }}
badge-url:
description: Browser-facing URL for the published coverage badge.
value: ${{ steps.upload.outputs.badge_url }}
runs:
using: composite
steps:
- name: Install AWS CLI v2
uses: ankurk91/install-aws-cli-action@v1
- name: Verify AWS CLI
shell: bash
run: aws --version
- name: Generate coverage artefacts
id: generate
shell: bash
env:
COVERAGE_PROFILE: ${{ inputs.coverage-profile }}
COVERAGE_HTML: ${{ inputs.coverage-html }}
COVERAGE_BADGE: ${{ inputs.coverage-badge }}
COVERAGE_SUMMARY: ${{ inputs.coverage-summary }}
run: |
set -euo pipefail
go tool cover -html="$COVERAGE_PROFILE" -o "$COVERAGE_HTML"
total="$(go tool cover -func="$COVERAGE_PROFILE" | awk '/^total:/ {sub(/%/, "", $3); print $3}')"
printf '{\n "total": "%s"\n}\n' "$total" > "$COVERAGE_SUMMARY"
printf 'total=%s\n' "$total" >> "$GITHUB_OUTPUT"
color="$(awk -v total="$total" 'BEGIN {
if (total >= 80) print "brightgreen";
else if (total >= 70) print "green";
else if (total >= 60) print "yellowgreen";
else if (total >= 50) print "yellow";
else print "red";
}')"
cat > "$COVERAGE_BADGE" <<EOF
<svg xmlns="http://www.w3.org/2000/svg" width="126" height="20" role="img" aria-label="coverage: ${total}%">
<linearGradient id="smooth" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="round">
<rect width="126" height="20" rx="3" fill="#fff"/>
</clipPath>
<g clip-path="url(#round)">
<rect width="63" height="20" fill="#555"/>
<rect x="63" width="63" height="20" fill="${color}"/>
<rect width="126" height="20" fill="url(#smooth)"/>
</g>
<g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
<text x="32.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
<text x="32.5" y="14">coverage</text>
<text x="93.5" y="15" fill="#010101" fill-opacity=".3">${total}%</text>
<text x="93.5" y="14">${total}%</text>
</g>
</svg>
EOF
- name: Upload coverage artefacts
id: upload
shell: bash
env:
ARTEFACT_BUCKET_NAME: ${{ inputs.artefact-bucket-name }}
ARTEFACT_BUCKET_ENDPONT: ${{ inputs.artefact-bucket-endpoint }}
INPUT_BRANCH_NAME: ${{ inputs.branch-name }}
INPUT_REPOSITORY_NAME: ${{ inputs.repository-name }}
COVERAGE_HTML: ${{ inputs.coverage-html }}
COVERAGE_BADGE: ${{ inputs.coverage-badge }}
COVERAGE_SUMMARY: ${{ inputs.coverage-summary }}
run: |
set -euo pipefail
aws configure set default.s3.addressing_style path
branch_name="$(printf '%s' "$INPUT_BRANCH_NAME" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
if [[ -z "$branch_name" ]]; then
branch_name="${GITHUB_REF_NAME}"
fi
repo_name="$(printf '%s' "$INPUT_REPOSITORY_NAME" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
if [[ -z "$repo_name" ]]; then
repo_name="${GITHUB_REPOSITORY##*/}"
fi
prefix="${repo_name}/branch/${branch_name}"
display_endpoint="${ARTEFACT_BUCKET_ENDPONT#https://}"
display_endpoint="${display_endpoint#http://}"
report_url="//${display_endpoint%/}/${ARTEFACT_BUCKET_NAME}/${prefix}/coverage.html"
badge_url="//${display_endpoint%/}/${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-badge.svg"
aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp "$COVERAGE_HTML" "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage.html" --content-type text/html
aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp "$COVERAGE_BADGE" "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-badge.svg" --content-type image/svg+xml
aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp "$COVERAGE_SUMMARY" "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-summary.json" --content-type application/json
printf 'report_url=%s\n' "$report_url" >> "$GITHUB_OUTPUT"
printf 'badge_url=%s\n' "$badge_url" >> "$GITHUB_OUTPUT"
- name: Append coverage summary
if: ${{ inputs.summary-file != '' }}
shell: bash
env:
SUMMARY_FILE: ${{ inputs.summary-file }}
TOTAL: ${{ steps.generate.outputs.total }}
REPORT_URL: ${{ steps.upload.outputs.report_url }}
BADGE_URL: ${{ steps.upload.outputs.badge_url }}
run: |
set -euo pipefail
{
echo '## Coverage'
echo
echo "- Total: \`${TOTAL}%\`"
echo "- Report: ${REPORT_URL}"
echo "- Badge: ${BADGE_URL}"
} >> "$SUMMARY_FILE"

View File

@@ -19,7 +19,7 @@ import (
const ( const (
defaultVersionFile = "release-version" defaultVersionFile = "release-version"
defaultVersionExpr = `^\s*([^\r\n]+)\s*$` defaultVersionExpr = `^\s*([^\r\n]+)\s*$`
defaultChangelog = "changelog.md" defaultChangelog = "CHANGELOG.md"
defaultUnreleasedTemplate = "### Breaking\n\n### Added\n\n### Changed\n\n### Removed\n\n### Fixed\n" defaultUnreleasedTemplate = "### Breaking\n\n### Added\n\n### Changed\n\n### Removed\n\n### Fixed\n"
) )
@@ -38,7 +38,7 @@ type Options struct {
// When empty, a line-oriented default matcher is used. // When empty, a line-oriented default matcher is used.
VersionPattern string VersionPattern string
// Changelog is the path to the changelog file, relative to the repository // Changelog is the path to the changelog file, relative to the repository
// root. When empty, changelog.md is used. // root. When empty, CHANGELOG.md is used.
Changelog string Changelog string
} }
@@ -335,6 +335,17 @@ func readLatestChangelogVersion(rootDir, changelogPath string) (string, bool, er
} }
func deriveRepositoryURL(rootDir string) (string, bool) { func deriveRepositoryURL(rootDir string) (string, bool) {
override := strings.TrimSpace(os.Getenv("VOCIFERATE_REPOSITORY_URL"))
if override != "" {
repositoryPath, ok := deriveRepositoryPath(rootDir)
if !ok {
return "", false
}
baseURL := strings.TrimSuffix(strings.TrimSpace(override), "/")
return baseURL + "/" + repositoryPath, true
}
serverURL := strings.TrimSpace(os.Getenv("GITHUB_SERVER_URL")) serverURL := strings.TrimSpace(os.Getenv("GITHUB_SERVER_URL"))
repository := strings.TrimSpace(os.Getenv("GITHUB_REPOSITORY")) repository := strings.TrimSpace(os.Getenv("GITHUB_REPOSITORY"))
if serverURL != "" && repository != "" { if serverURL != "" && repository != "" {
@@ -360,6 +371,38 @@ func deriveRepositoryURL(rootDir string) (string, bool) {
return repoURL, true return repoURL, true
} }
func deriveRepositoryPath(rootDir string) (string, bool) {
repository := strings.TrimSpace(os.Getenv("GITHUB_REPOSITORY"))
if repository != "" {
return strings.TrimPrefix(repository, "/"), true
}
gitConfigPath := filepath.Join(rootDir, ".git", "config")
contents, err := os.ReadFile(gitConfigPath)
if err != nil {
return "", false
}
remoteURL, ok := originRemoteURLFromGitConfig(string(contents))
if !ok {
return "", false
}
repoURL, ok := normalizeRepoURL(remoteURL)
if !ok {
return "", false
}
parsedURL := strings.TrimPrefix(repoURL, "https://")
parsedURL = strings.TrimPrefix(parsedURL, "http://")
slash := strings.Index(parsedURL, "/")
if slash == -1 || slash == len(parsedURL)-1 {
return "", false
}
return parsedURL[slash+1:], true
}
func originRemoteURLFromGitConfig(config string) (string, bool) { func originRemoteURLFromGitConfig(config string) (string, bool) {
inOrigin := false inOrigin := false
for _, line := range strings.Split(config, "\n") { for _, line := range strings.Split(config, "\n") {
@@ -395,7 +438,8 @@ func normalizeRepoURL(remoteURL string) (string, bool) {
} }
if strings.HasPrefix(remoteURL, "http://") || strings.HasPrefix(remoteURL, "https://") { if strings.HasPrefix(remoteURL, "http://") || strings.HasPrefix(remoteURL, "https://") {
return strings.TrimSuffix(remoteURL, ".git"), true normalized := strings.TrimSuffix(strings.TrimSuffix(remoteURL, "/"), ".git")
return normalized, true
} }
if strings.HasPrefix(remoteURL, "ssh://") { if strings.HasPrefix(remoteURL, "ssh://") {
@@ -433,6 +477,8 @@ func addChangelogLinks(text, repoURL, rootDir string) string {
return text return text
} }
displayRepoURL := displayURL(repoURL)
// Normalize headings to plain format, stripping any existing inline links. // Normalize headings to plain format, stripping any existing inline links.
text = unreleasedHeadingRe.ReplaceAllString(text, "## [Unreleased]\n") text = unreleasedHeadingRe.ReplaceAllString(text, "## [Unreleased]\n")
text = releaseHeadingRe.ReplaceAllStringFunc(text, func(match string) string { text = releaseHeadingRe.ReplaceAllStringFunc(text, func(match string) string {
@@ -468,30 +514,41 @@ func addChangelogLinks(text, repoURL, rootDir string) string {
linkDefs := make([]string, 0, len(releasedVersions)+1) linkDefs := make([]string, 0, len(releasedVersions)+1)
if len(releasedVersions) > 0 { if len(releasedVersions) > 0 {
latest := releasedVersions[0] latest := releasedVersions[0]
linkDefs = append(linkDefs, fmt.Sprintf("[Unreleased]: %s", compareURL(repoURL, "v"+latest, "main"))) linkDefs = append(linkDefs, fmt.Sprintf("[Unreleased]: %s", compareURL(displayRepoURL, "v"+latest, "main")))
} else { } else {
linkDefs = append(linkDefs, fmt.Sprintf("[Unreleased]: %s/src/branch/main", repoURL)) linkDefs = append(linkDefs, fmt.Sprintf("[Unreleased]: %s/src/branch/main", displayRepoURL))
} }
firstCommitShort, hasFirstCommit := firstCommitShortHash(rootDir) firstCommitShort, hasFirstCommit := firstCommitShortHash(rootDir)
for i, version := range releasedVersions { for i, version := range releasedVersions {
if i+1 < len(releasedVersions) { if i+1 < len(releasedVersions) {
previousVersion := releasedVersions[i+1] previousVersion := releasedVersions[i+1]
linkDefs = append(linkDefs, fmt.Sprintf("[%s]: %s", version, compareURL(repoURL, "v"+previousVersion, "v"+version))) linkDefs = append(linkDefs, fmt.Sprintf("[%s]: %s", version, compareURL(displayRepoURL, "v"+previousVersion, "v"+version)))
continue continue
} }
if hasFirstCommit { if hasFirstCommit {
linkDefs = append(linkDefs, fmt.Sprintf("[%s]: %s", version, compareURL(repoURL, firstCommitShort, "v"+version))) linkDefs = append(linkDefs, fmt.Sprintf("[%s]: %s", version, compareURL(displayRepoURL, firstCommitShort, "v"+version)))
continue continue
} }
linkDefs = append(linkDefs, fmt.Sprintf("[%s]: %s", version, compareURL(repoURL, "v"+version, "main"))) linkDefs = append(linkDefs, fmt.Sprintf("[%s]: %s", version, compareURL(displayRepoURL, "v"+version, "main")))
} }
return strings.TrimRight(text, "\n") + "\n\n" + strings.Join(linkDefs, "\n") + "\n" return strings.TrimRight(text, "\n") + "\n\n" + strings.Join(linkDefs, "\n") + "\n"
} }
func displayURL(url string) string {
trimmed := strings.TrimSpace(url)
if strings.HasPrefix(trimmed, "https://") {
return "//" + strings.TrimPrefix(trimmed, "https://")
}
if strings.HasPrefix(trimmed, "http://") {
return "//" + strings.TrimPrefix(trimmed, "http://")
}
return trimmed
}
func firstCommitShortHash(rootDir string) (string, bool) { func firstCommitShortHash(rootDir string) (string, bool) {
command := exec.Command("git", "-C", rootDir, "rev-list", "--max-parents=0", "--abbrev-commit", "HEAD") command := exec.Command("git", "-C", rootDir, "rev-list", "--max-parents=0", "--abbrev-commit", "HEAD")
output, err := command.Output() output, err := command.Output()

View File

@@ -16,7 +16,9 @@ func TestNormalizeRepoURL(t *testing.T) {
wantOK bool wantOK bool
}{ }{
{name: "https", remoteURL: "https://git.hrafn.xyz/aether/vociferate.git", wantURL: "https://git.hrafn.xyz/aether/vociferate", wantOK: true}, {name: "https", remoteURL: "https://git.hrafn.xyz/aether/vociferate.git", wantURL: "https://git.hrafn.xyz/aether/vociferate", wantOK: true},
{name: "https trailing slash", remoteURL: "https://git.hrafn.xyz/aether/vociferate/", wantURL: "https://git.hrafn.xyz/aether/vociferate", wantOK: true},
{name: "http", remoteURL: "http://teapot:3000/aether/vociferate.git", wantURL: "http://teapot:3000/aether/vociferate", wantOK: true}, {name: "http", remoteURL: "http://teapot:3000/aether/vociferate.git", wantURL: "http://teapot:3000/aether/vociferate", wantOK: true},
{name: "http trailing slash", remoteURL: "http://teapot:3000/aether/vociferate/", wantURL: "http://teapot:3000/aether/vociferate", wantOK: true},
{name: "ssh with scheme", remoteURL: "ssh://git@git.hrafn.xyz/aether/vociferate.git", wantURL: "https://git.hrafn.xyz/aether/vociferate", wantOK: true}, {name: "ssh with scheme", remoteURL: "ssh://git@git.hrafn.xyz/aether/vociferate.git", wantURL: "https://git.hrafn.xyz/aether/vociferate", wantOK: true},
{name: "scp style", remoteURL: "git@git.hrafn.xyz:aether/vociferate.git", wantURL: "https://git.hrafn.xyz/aether/vociferate", wantOK: true}, {name: "scp style", remoteURL: "git@git.hrafn.xyz:aether/vociferate.git", wantURL: "https://git.hrafn.xyz/aether/vociferate", wantOK: true},
{name: "empty", remoteURL: "", wantURL: "", wantOK: false}, {name: "empty", remoteURL: "", wantURL: "", wantOK: false},
@@ -121,3 +123,39 @@ func TestDeriveRepositoryURLFromGitConfigFallback(t *testing.T) {
t.Fatalf("unexpected repository URL: %q", url) t.Fatalf("unexpected repository URL: %q", url)
} }
} }
func TestDeriveRepositoryURL_UsesOverrideAsHighestPriority(t *testing.T) {
t.Setenv("VOCIFERATE_REPOSITORY_URL", "https://git.hrafn.xyz/git")
t.Setenv("GITHUB_SERVER_URL", "http://teapot:3000")
t.Setenv("GITHUB_REPOSITORY", "aether/vociferate")
root := t.TempDir()
configPath := filepath.Join(root, ".git", "config")
if err := os.MkdirAll(filepath.Dir(configPath), 0o755); err != nil {
t.Fatalf("mkdir .git: %v", err)
}
if err := os.WriteFile(configPath, []byte("[remote \"origin\"]\n\turl = git@different.host:org/other.git\n"), 0o644); err != nil {
t.Fatalf("write git config: %v", err)
}
url, ok := deriveRepositoryURL(root)
if !ok {
t.Fatal("expected repository URL from override")
}
if url != "https://git.hrafn.xyz/git/aether/vociferate" {
t.Fatalf("unexpected repository URL: %q", url)
}
}
func TestResolveOptions_UsesUppercaseChangelogDefault(t *testing.T) {
t.Parallel()
resolved, err := resolveOptions(Options{})
if err != nil {
t.Fatalf("resolveOptions returned unexpected error: %v", err)
}
if resolved.Changelog != "CHANGELOG.md" {
t.Fatalf("resolved changelog = %q, want %q", resolved.Changelog, "CHANGELOG.md")
}
}

View File

@@ -41,7 +41,7 @@ func (s *PrepareSuite) SetupTest() {
)) ))
require.NoError(s.T(), os.WriteFile( require.NoError(s.T(), os.WriteFile(
filepath.Join(s.rootDir, "changelog.md"), filepath.Join(s.rootDir, "CHANGELOG.md"),
[]byte("# Changelog\n\n## [Unreleased]\n\n### Breaking\n\n### Added\n\n- New thing.\n\n### Fixed\n\n- Old thing.\n\n## [1.1.6] - 2017-12-20\n\n### Fixed\n\n- Historical note.\n"), []byte("# Changelog\n\n## [Unreleased]\n\n### Breaking\n\n### Added\n\n- New thing.\n\n### Fixed\n\n- Old thing.\n\n## [1.1.6] - 2017-12-20\n\n### Fixed\n\n- Historical note.\n"),
0o644, 0o644,
)) ))
@@ -72,15 +72,15 @@ func (s *PrepareSuite) TestPrepare_UpdatesVersionAndPromotesUnreleasedNotes() {
require.NoError(s.T(), err) require.NoError(s.T(), err)
require.Equal(s.T(), "1.1.7\n", string(versionBytes)) require.Equal(s.T(), "1.1.7\n", string(versionBytes))
changelogBytes, err := os.ReadFile(filepath.Join(s.rootDir, "changelog.md")) changelogBytes, err := os.ReadFile(filepath.Join(s.rootDir, "CHANGELOG.md"))
require.NoError(s.T(), err) require.NoError(s.T(), err)
firstCommit := firstCommitShortHash(s.T(), s.rootDir) firstCommit := firstCommitShortHash(s.T(), s.rootDir)
require.Equal(s.T(), "# Changelog\n\n## [Unreleased]\n\n### Breaking\n\n### Added\n\n### Changed\n\n### Removed\n\n### Fixed\n\n## [1.1.7] - 2026-03-20\n\n### Breaking\n\n### Added\n\n- New thing.\n\n### Fixed\n\n- Old thing.\n\n## [1.1.6] - 2017-12-20\n\n### Fixed\n\n- Historical note.\n\n[Unreleased]: https://git.hrafn.xyz/aether/vociferate/compare/v1.1.7...main\n[1.1.7]: https://git.hrafn.xyz/aether/vociferate/compare/v1.1.6...v1.1.7\n[1.1.6]: https://git.hrafn.xyz/aether/vociferate/compare/"+firstCommit+"...v1.1.6\n", string(changelogBytes)) require.Equal(s.T(), "# Changelog\n\n## [Unreleased]\n\n### Breaking\n\n### Added\n\n### Changed\n\n### Removed\n\n### Fixed\n\n## [1.1.7] - 2026-03-20\n\n### Breaking\n\n### Added\n\n- New thing.\n\n### Fixed\n\n- Old thing.\n\n## [1.1.6] - 2017-12-20\n\n### Fixed\n\n- Historical note.\n\n[Unreleased]: //git.hrafn.xyz/aether/vociferate/compare/v1.1.7...main\n[1.1.7]: //git.hrafn.xyz/aether/vociferate/compare/v1.1.6...v1.1.7\n[1.1.6]: //git.hrafn.xyz/aether/vociferate/compare/"+firstCommit+"...v1.1.6\n", string(changelogBytes))
} }
func (s *PrepareSuite) TestPrepare_ReturnsErrorWhenUnreleasedSectionMissing() { func (s *PrepareSuite) TestPrepare_ReturnsErrorWhenUnreleasedSectionMissing() {
require.NoError(s.T(), os.WriteFile( require.NoError(s.T(), os.WriteFile(
filepath.Join(s.rootDir, "changelog.md"), filepath.Join(s.rootDir, "CHANGELOG.md"),
[]byte("# Changelog\n\n## [1.1.6] - 2017-12-20\n"), []byte("# Changelog\n\n## [1.1.6] - 2017-12-20\n"),
0o644, 0o644,
)) ))
@@ -92,7 +92,7 @@ func (s *PrepareSuite) TestPrepare_ReturnsErrorWhenUnreleasedSectionMissing() {
func (s *PrepareSuite) TestPrepare_ReturnsErrorWhenUnreleasedSectionIsEmpty() { func (s *PrepareSuite) TestPrepare_ReturnsErrorWhenUnreleasedSectionIsEmpty() {
require.NoError(s.T(), os.WriteFile( require.NoError(s.T(), os.WriteFile(
filepath.Join(s.rootDir, "changelog.md"), filepath.Join(s.rootDir, "CHANGELOG.md"),
[]byte("# Changelog\n\n## [Unreleased]\n\n## [1.1.6] - 2017-12-20\n"), []byte("# Changelog\n\n## [Unreleased]\n\n## [1.1.6] - 2017-12-20\n"),
0o644, 0o644,
)) ))
@@ -111,7 +111,7 @@ func (s *PrepareSuite) TestRecommendedTag_UsesMinorBumpWhenBreakingHeadingIsEmpt
func (s *PrepareSuite) TestRecommendedTag_ReturnsErrorWhenUnreleasedHasOnlyTemplateHeadings() { func (s *PrepareSuite) TestRecommendedTag_ReturnsErrorWhenUnreleasedHasOnlyTemplateHeadings() {
require.NoError(s.T(), os.WriteFile( require.NoError(s.T(), os.WriteFile(
filepath.Join(s.rootDir, "changelog.md"), filepath.Join(s.rootDir, "CHANGELOG.md"),
[]byte("# Changelog\n\n## [Unreleased]\n\n### Breaking\n\n### Added\n\n### Changed\n\n### Removed\n\n### Fixed\n\n## [1.1.6] - 2017-12-20\n"), []byte("# Changelog\n\n## [Unreleased]\n\n### Breaking\n\n### Added\n\n### Changed\n\n### Removed\n\n### Fixed\n\n## [1.1.6] - 2017-12-20\n"),
0o644, 0o644,
)) ))
@@ -123,7 +123,7 @@ func (s *PrepareSuite) TestRecommendedTag_ReturnsErrorWhenUnreleasedHasOnlyTempl
func (s *PrepareSuite) TestRecommendedTag_UsesPatchBumpForFixOnlyChanges() { func (s *PrepareSuite) TestRecommendedTag_UsesPatchBumpForFixOnlyChanges() {
require.NoError(s.T(), os.WriteFile( require.NoError(s.T(), os.WriteFile(
filepath.Join(s.rootDir, "changelog.md"), filepath.Join(s.rootDir, "CHANGELOG.md"),
[]byte("# Changelog\n\n## [Unreleased]\n\n### Fixed\n\n- Patch note.\n\n## [1.1.6] - 2017-12-20\n"), []byte("# Changelog\n\n## [Unreleased]\n\n### Fixed\n\n- Patch note.\n\n## [1.1.6] - 2017-12-20\n"),
0o644, 0o644,
)) ))
@@ -136,7 +136,7 @@ func (s *PrepareSuite) TestRecommendedTag_UsesPatchBumpForFixOnlyChanges() {
func (s *PrepareSuite) TestRecommendedTag_UsesMajorBumpWhenRemovedEntriesExist() { func (s *PrepareSuite) TestRecommendedTag_UsesMajorBumpWhenRemovedEntriesExist() {
require.NoError(s.T(), os.WriteFile( require.NoError(s.T(), os.WriteFile(
filepath.Join(s.rootDir, "changelog.md"), filepath.Join(s.rootDir, "CHANGELOG.md"),
[]byte("# Changelog\n\n## [Unreleased]\n\n### Removed\n\n- Breaking removal.\n\n## [1.1.6] - 2017-12-20\n"), []byte("# Changelog\n\n## [Unreleased]\n\n### Removed\n\n- Breaking removal.\n\n## [1.1.6] - 2017-12-20\n"),
0o644, 0o644,
)) ))
@@ -149,7 +149,7 @@ func (s *PrepareSuite) TestRecommendedTag_UsesMajorBumpWhenRemovedEntriesExist()
func (s *PrepareSuite) TestRecommendedTag_UsesMajorBumpWhenBreakingEntriesExist() { func (s *PrepareSuite) TestRecommendedTag_UsesMajorBumpWhenBreakingEntriesExist() {
require.NoError(s.T(), os.WriteFile( require.NoError(s.T(), os.WriteFile(
filepath.Join(s.rootDir, "changelog.md"), filepath.Join(s.rootDir, "CHANGELOG.md"),
[]byte("# Changelog\n\n## [Unreleased]\n\n### Breaking\n\n- Changed API contract.\n\n### Changed\n\n- Updated defaults.\n\n## [1.1.6] - 2017-12-20\n"), []byte("# Changelog\n\n## [Unreleased]\n\n### Breaking\n\n- Changed API contract.\n\n### Changed\n\n- Updated defaults.\n\n## [1.1.6] - 2017-12-20\n"),
0o644, 0o644,
)) ))
@@ -206,7 +206,7 @@ func (s *PrepareSuite) TestRecommendedTag_UsesChangelogVersionWhenNoVersionFileC
0o644, 0o644,
)) ))
require.NoError(s.T(), os.WriteFile( require.NoError(s.T(), os.WriteFile(
filepath.Join(s.rootDir, "changelog.md"), filepath.Join(s.rootDir, "CHANGELOG.md"),
[]byte("# Changelog\n\n## [Unreleased]\n\n### Fixed\n\n- A fix.\n\n## [3.0.0] - 2026-01-01\n\n### Fixed\n\n- Historical.\n"), []byte("# Changelog\n\n## [Unreleased]\n\n### Fixed\n\n- A fix.\n\n## [3.0.0] - 2026-01-01\n\n### Fixed\n\n- Historical.\n"),
0o644, 0o644,
)) ))
@@ -220,7 +220,7 @@ func (s *PrepareSuite) TestRecommendedTag_UsesChangelogVersionWhenNoVersionFileC
func (s *PrepareSuite) TestRecommendedTag_DefaultsToV1WhenNoPriorReleasesInChangelog() { func (s *PrepareSuite) TestRecommendedTag_DefaultsToV1WhenNoPriorReleasesInChangelog() {
require.NoError(s.T(), os.Remove(filepath.Join(s.rootDir, "release-version"))) require.NoError(s.T(), os.Remove(filepath.Join(s.rootDir, "release-version")))
require.NoError(s.T(), os.WriteFile( require.NoError(s.T(), os.WriteFile(
filepath.Join(s.rootDir, "changelog.md"), filepath.Join(s.rootDir, "CHANGELOG.md"),
[]byte("# Changelog\n\n## [Unreleased]\n\n### Breaking\n\n### Added\n\n- First feature.\n"), []byte("# Changelog\n\n## [Unreleased]\n\n### Breaking\n\n### Added\n\n- First feature.\n"),
0o644, 0o644,
)) ))
@@ -252,7 +252,7 @@ func (s *PrepareSuite) TestRecommendedTag_UsesCustomVersionFileAndPattern() {
0o644, 0o644,
)) ))
require.NoError(s.T(), os.WriteFile( require.NoError(s.T(), os.WriteFile(
filepath.Join(s.rootDir, "changelog.md"), filepath.Join(s.rootDir, "CHANGELOG.md"),
[]byte("# Changelog\n\n## [Unreleased]\n\n### Added\n\n- Feature.\n\n## [2.3.4] - 2026-03-10\n"), []byte("# Changelog\n\n## [Unreleased]\n\n### Added\n\n- Feature.\n\n## [2.3.4] - 2026-03-10\n"),
0o644, 0o644,
)) ))
@@ -273,7 +273,7 @@ func (s *PrepareSuite) TestPrepare_UsesGitHrafnXYZEnvironmentForChangelogLinks()
err := vociferate.Prepare(s.rootDir, "1.1.7", "2026-03-20", vociferate.Options{}) err := vociferate.Prepare(s.rootDir, "1.1.7", "2026-03-20", vociferate.Options{})
require.NoError(s.T(), err) require.NoError(s.T(), err)
changelogBytes, readErr := os.ReadFile(filepath.Join(s.rootDir, "changelog.md")) changelogBytes, readErr := os.ReadFile(filepath.Join(s.rootDir, "CHANGELOG.md"))
require.NoError(s.T(), readErr) require.NoError(s.T(), readErr)
changelog := string(changelogBytes) changelog := string(changelogBytes)
firstCommit := firstCommitShortHash(s.T(), s.rootDir) firstCommit := firstCommitShortHash(s.T(), s.rootDir)
@@ -283,9 +283,9 @@ func (s *PrepareSuite) TestPrepare_UsesGitHrafnXYZEnvironmentForChangelogLinks()
require.Contains(s.T(), changelog, "### Removed\n") require.Contains(s.T(), changelog, "### Removed\n")
require.Contains(s.T(), changelog, "## [1.1.7] - 2026-03-20") require.Contains(s.T(), changelog, "## [1.1.7] - 2026-03-20")
require.Contains(s.T(), changelog, "## [1.1.6] - 2017-12-20") require.Contains(s.T(), changelog, "## [1.1.6] - 2017-12-20")
require.Contains(s.T(), changelog, "[Unreleased]: https://git.hrafn.xyz/aether/vociferate/compare/v1.1.7...main") require.Contains(s.T(), changelog, "[Unreleased]: //git.hrafn.xyz/aether/vociferate/compare/v1.1.7...main")
require.Contains(s.T(), changelog, "[1.1.7]: https://git.hrafn.xyz/aether/vociferate/compare/v1.1.6...v1.1.7") require.Contains(s.T(), changelog, "[1.1.7]: //git.hrafn.xyz/aether/vociferate/compare/v1.1.6...v1.1.7")
require.Contains(s.T(), changelog, "[1.1.6]: https://git.hrafn.xyz/aether/vociferate/compare/"+firstCommit+"...v1.1.6") require.Contains(s.T(), changelog, "[1.1.6]: //git.hrafn.xyz/aether/vociferate/compare/"+firstCommit+"...v1.1.6")
} }
func (s *PrepareSuite) TestPrepare_UsesGitHubEnvironmentForChangelogLinks() { func (s *PrepareSuite) TestPrepare_UsesGitHubEnvironmentForChangelogLinks() {
@@ -295,7 +295,7 @@ func (s *PrepareSuite) TestPrepare_UsesGitHubEnvironmentForChangelogLinks() {
err := vociferate.Prepare(s.rootDir, "1.1.7", "2026-03-20", vociferate.Options{}) err := vociferate.Prepare(s.rootDir, "1.1.7", "2026-03-20", vociferate.Options{})
require.NoError(s.T(), err) require.NoError(s.T(), err)
changelogBytes, readErr := os.ReadFile(filepath.Join(s.rootDir, "changelog.md")) changelogBytes, readErr := os.ReadFile(filepath.Join(s.rootDir, "CHANGELOG.md"))
require.NoError(s.T(), readErr) require.NoError(s.T(), readErr)
changelog := string(changelogBytes) changelog := string(changelogBytes)
firstCommit := firstCommitShortHash(s.T(), s.rootDir) firstCommit := firstCommitShortHash(s.T(), s.rootDir)
@@ -305,7 +305,7 @@ func (s *PrepareSuite) TestPrepare_UsesGitHubEnvironmentForChangelogLinks() {
require.Contains(s.T(), changelog, "### Removed\n") require.Contains(s.T(), changelog, "### Removed\n")
require.Contains(s.T(), changelog, "## [1.1.7] - 2026-03-20") require.Contains(s.T(), changelog, "## [1.1.7] - 2026-03-20")
require.Contains(s.T(), changelog, "## [1.1.6] - 2017-12-20") require.Contains(s.T(), changelog, "## [1.1.6] - 2017-12-20")
require.Contains(s.T(), changelog, "[Unreleased]: https://github.com/aether/vociferate/compare/v1.1.7...main") require.Contains(s.T(), changelog, "[Unreleased]: //github.com/aether/vociferate/compare/v1.1.7...main")
require.Contains(s.T(), changelog, "[1.1.7]: https://github.com/aether/vociferate/compare/v1.1.6...v1.1.7") require.Contains(s.T(), changelog, "[1.1.7]: //github.com/aether/vociferate/compare/v1.1.6...v1.1.7")
require.Contains(s.T(), changelog, "[1.1.6]: https://github.com/aether/vociferate/compare/"+firstCommit+"...v1.1.6") require.Contains(s.T(), changelog, "[1.1.6]: //github.com/aether/vociferate/compare/"+firstCommit+"...v1.1.6")
} }

View File

@@ -26,7 +26,7 @@ inputs:
changelog: changelog:
description: Path to changelog file relative to repository root. description: Path to changelog file relative to repository root.
required: false required: false
default: changelog.md default: CHANGELOG.md
git-user-name: git-user-name:
description: Name for the release commit author. description: Name for the release commit author.
required: false required: false
@@ -38,10 +38,10 @@ inputs:
git-add-files: git-add-files:
description: > description: >
Space-separated list of file paths to stage for the release commit. Space-separated list of file paths to stage for the release commit.
Defaults to changelog.md and release-version. Adjust when using a Defaults to CHANGELOG.md and release-version. Adjust when using a
custom version-file. custom version-file.
required: false required: false
default: 'changelog.md release-version' default: 'CHANGELOG.md release-version'
outputs: outputs:
version: version:
@@ -142,6 +142,7 @@ runs:
VOCIFERATE_BIN: ${{ steps.resolve-binary.outputs.binary_path }} VOCIFERATE_BIN: ${{ steps.resolve-binary.outputs.binary_path }}
USE_BINARY: ${{ steps.resolve-binary.outputs.use_binary }} USE_BINARY: ${{ steps.resolve-binary.outputs.use_binary }}
INPUT_VERSION: ${{ inputs.version }} INPUT_VERSION: ${{ inputs.version }}
VOCIFERATE_REPOSITORY_URL: ${{ vars.VOCIFERATE_REPOSITORY_URL }}
run: | run: |
set -euo pipefail set -euo pipefail

View File

@@ -20,7 +20,7 @@ inputs:
changelog: changelog:
description: Path to changelog file relative to repository root. description: Path to changelog file relative to repository root.
required: false required: false
default: changelog.md default: CHANGELOG.md
outputs: outputs:
release-id: release-id:
@@ -67,7 +67,7 @@ runs:
id: extract-notes id: extract-notes
shell: bash shell: bash
env: env:
CHANGELOG: ${{ inputs.changelog != '' && inputs.changelog || 'changelog.md' }} CHANGELOG: ${{ inputs.changelog != '' && inputs.changelog || 'CHANGELOG.md' }}
RELEASE_VERSION: ${{ steps.resolve-version.outputs.version }} RELEASE_VERSION: ${{ steps.resolve-version.outputs.version }}
RUNNER_TEMP: ${{ runner.temp }} RUNNER_TEMP: ${{ runner.temp }}
run: | run: |

View File

@@ -1 +1 @@
0.1.0 1.0.0