11 Commits

Author SHA1 Message Date
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
11 changed files with 198 additions and 31 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
@@ -61,7 +63,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

@@ -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
@@ -98,8 +99,10 @@ jobs:
repo_name="${GITHUB_REPOSITORY##*/}" repo_name="${GITHUB_REPOSITORY##*/}"
prefix="${repo_name}/branch/${GITHUB_REF_NAME}" prefix="${repo_name}/branch/${GITHUB_REF_NAME}"
report_url="${ARTEFACT_BUCKET_ENDPONT%/}/${ARTEFACT_BUCKET_NAME}/${prefix}/coverage.html" display_endpoint="${ARTEFACT_BUCKET_ENDPONT#https://}"
badge_url="${ARTEFACT_BUCKET_ENDPONT%/}/${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-badge.svg" 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.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-badge.svg "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-badge.svg" --content-type image/svg+xml
@@ -110,13 +113,15 @@ jobs:
- name: Add coverage summary - name: Add coverage summary
run: | run: |
set -euo pipefail
{ {
echo '## Coverage' echo '## Coverage'
echo echo
echo '- Total: `${{ steps.coverage.outputs.total }}%`' echo '- Total: `${{ steps.coverage.outputs.total }}%`'
echo '- Report: ${{ steps.upload.outputs.report_url }}' echo '- Report: ${{ steps.upload.outputs.report_url }}'
echo '- Badge: ${{ steps.upload.outputs.badge_url }}' echo '- Badge: ${{ steps.upload.outputs.badge_url }}'
} >> "$GITHUB_STEP_SUMMARY" } >> "$SUMMARY_FILE"
- name: Recommend next release tag on main pushes - name: Recommend next release tag on main pushes
if: ${{ github.ref == 'refs/heads/main' }} if: ${{ github.ref == 'refs/heads/main' }}
@@ -129,7 +134,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 +143,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

View File

@@ -1,9 +1,9 @@
# 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 `aether` 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
@@ -167,7 +167,7 @@ Defaults:
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.

View File

@@ -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

@@ -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,7 +13,20 @@ A `### Breaking` section is used in addition to Keep a Changelog's standard sect
### Added ### Added
### Changed
### Removed
### Fixed
## [0.2.0] - 2026-03-21
### Breaking
### Added
- Added a project LICENSE file. - 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.
### Changed ### Changed
@@ -21,10 +34,13 @@ A `### Breaking` section is used in addition to Keep a Changelog's standard sect
### Fixed ### 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 +76,6 @@ 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/v0.2.0...main
[0.1.0]: http://teapot:3000/aether/vociferate/releases/tag/v0.1.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

@@ -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,26 @@ 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)
}
}

View File

@@ -75,7 +75,7 @@ func (s *PrepareSuite) TestPrepare_UpdatesVersionAndPromotesUnreleasedNotes() {
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() {
@@ -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() {
@@ -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

@@ -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

@@ -1 +1 @@
0.1.0 0.2.0