3 Commits

Author SHA1 Message Date
Micheal Wilkinson
44f499dc52 docs: record local action path syntax fix
All checks were successful
Push Validation / coverage-badge (push) Successful in 1m14s
Push Validation / recommend-release (push) Successful in 28s
2026-03-21 15:20:38 +00:00
Micheal Wilkinson
43827593e7 fix(actions): mark nested run-vociferate refs as local paths 2026-03-21 15:20:38 +00:00
gitea-actions[bot]
4714bfe272 release: prepare v1.1.0 2026-03-21 15:18:34 +00:00
13 changed files with 400 additions and 702 deletions

View File

@@ -28,116 +28,38 @@ jobs:
run: run:
shell: bash shell: bash
env: env:
RELEASE_TOKEN: ${{ secrets.RELEASE_PAT }} RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN || secrets.GITEA_TOKEN }}
SUMMARY_FILE: ${{ runner.temp }}/do-release-summary.md SUMMARY_FILE: ${{ runner.temp }}/do-release-summary.md
steps: steps:
- name: Checkout - name: Checkout tagged revision
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
ref: ${{ github.ref }}
- name: Fetch and detect release tag - name: Checkout requested tag
id: detect-tag if: ${{ inputs.tag != '' }}
run: |
set -euo pipefail
# Fetch all tags from origin first
git fetch origin --tags --force --quiet 2>/dev/null || true
# Check if HEAD is at a tag (prepare-release may have just tagged it)
if head_tag="$(git describe --exact-match --tags HEAD 2>/dev/null)" && [[ -n "$head_tag" ]]; then
echo "detected_tag=$head_tag" >> "$GITHUB_OUTPUT"
exit 0
fi
# Fall back to finding the most recent tag
if latest_tag="$(git describe --tags --abbrev=0 2>/dev/null)" && [[ -n "$latest_tag" ]]; then
echo "detected_tag=$latest_tag" >> "$GITHUB_OUTPUT"
fi
- name: Resolve release version
id: resolve-version
env:
INPUT_TAG: ${{ inputs.tag }}
CALLER_TAG: ${{ needs.prepare.outputs.tag }}
DETECTED_TAG: ${{ steps.detect-tag.outputs.detected_tag }}
run: |
set -euo pipefail
normalize_candidate() {
local raw="$1"
raw="$(printf '%s' "$raw" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
# Teacup can surface expression strings as %!t(string=value); unwrap it.
if [[ "$raw" =~ ^%\!t\(string=(.*)\)$ ]]; then
raw="${BASH_REMATCH[1]}"
fi
raw="$(printf '%s' "$raw" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
printf '%s' "$raw"
}
input_tag="$(normalize_candidate "${INPUT_TAG}")"
caller_tag="$(normalize_candidate "${CALLER_TAG}")"
detected_tag="$(normalize_candidate "${DETECTED_TAG}")"
# Try explicit input first.
requested_tag="$input_tag"
# Fall back to caller workflow output when workflow_call input forwarding is unreliable.
if [[ -z "$requested_tag" && -n "$caller_tag" ]]; then
requested_tag="$caller_tag"
fi
# Fall back to detected tag if neither input nor caller tag is available.
if [[ -z "$requested_tag" && -n "$detected_tag" ]]; then
requested_tag="$detected_tag"
fi
# Try GITHUB_REF if still empty
if [[ -z "$requested_tag" && "$GITHUB_REF" == refs/tags/* ]]; then
requested_tag="${GITHUB_REF#refs/tags/}"
fi
if [[ -n "$requested_tag" ]]; then
# Normalize to v-prefixed format
normalized="${requested_tag#v}"
tag="v${normalized}"
else
echo "Error: Could not resolve release version" >&2
echo " - inputs.tag(raw): '$INPUT_TAG'" >&2
echo " - needs.prepare.outputs.tag(raw): '$CALLER_TAG'" >&2
echo " - detected_tag(raw): '${DETECTED_TAG}'" >&2
echo " - GITHUB_REF: '$GITHUB_REF'" >&2
exit 1
fi
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
echo "version=${normalized}" >> "$GITHUB_OUTPUT"
- name: Checkout release tag
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
ref: refs/tags/${{ steps.resolve-version.outputs.tag }} ref: ${{ startsWith(inputs.tag, 'v') && format('refs/tags/{0}', inputs.tag) || format('refs/tags/v{0}', inputs.tag) }}
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version: '1.26.1'
check-latest: false check-latest: true
cache: true cache: true
cache-dependency-path: go.sum cache-dependency-path: go.sum
- name: Preflight release API access - name: Preflight release API access
env: env:
TAG_NAME: ${{ steps.resolve-version.outputs.tag }} REQUESTED_TAG: ${{ inputs.tag }}
run: | run: |
set -euo pipefail set -euo pipefail
if [[ -z "${RELEASE_TOKEN:-}" ]]; then if [[ -z "${RELEASE_TOKEN:-}" ]]; then
echo "No release token available. Set secrets.RELEASE_PAT." >&2 echo "No release token available. Set GITEA_TOKEN (or GITHUB_TOKEN on GitHub)." >&2
exit 1 exit 1
fi fi
@@ -154,17 +76,22 @@ jobs:
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${repo_api}/releases?limit=1" >/dev/null "${repo_api}/releases?limit=1" >/dev/null
if ! git rev-parse --verify --quiet "refs/tags/${TAG_NAME}" >/dev/null; then requested_tag="$(printf '%s' "${REQUESTED_TAG:-}" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
echo "Tag ${TAG_NAME} was not found in the checked out repository." >&2 if [[ -n "$requested_tag" ]]; then
exit 1 normalized_tag="${requested_tag#v}"
tag_ref="refs/tags/v${normalized_tag}"
if ! git rev-parse --verify --quiet "$tag_ref" >/dev/null; then
echo "Requested tag ${tag_ref#refs/tags/} was not found in the checked out repository." >&2
exit 1
fi
fi fi
- name: Create or update release - name: Create or update release
id: publish id: publish
uses: ./publish uses: ./publish
with: with:
token: ${{ secrets.RELEASE_PAT }} token: ${{ secrets.GITHUB_TOKEN || secrets.GITEA_TOKEN }}
version: ${{ steps.resolve-version.outputs.version }} version: ${{ inputs.tag }}
- name: Build release binaries - name: Build release binaries
env: env:
@@ -284,7 +211,7 @@ jobs:
- name: Download released binary - name: Download released binary
env: env:
TOKEN: ${{ secrets.RELEASE_PAT }} TOKEN: ${{ secrets.GITHUB_TOKEN || secrets.GITEA_TOKEN }}
TAG_NAME: ${{ needs.release.outputs.tag }} TAG_NAME: ${{ needs.release.outputs.tag }}
RELEASE_VERSION: ${{ needs.release.outputs.version }} RELEASE_VERSION: ${{ needs.release.outputs.version }}
ASSET_ARCH: ${{ matrix.asset_arch }} ASSET_ARCH: ${{ matrix.asset_arch }}

View File

@@ -34,8 +34,8 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version: '1.26.1'
check-latest: false check-latest: true
cache: true cache: true
cache-dependency-path: go.sum cache-dependency-path: go.sum
@@ -45,15 +45,8 @@ jobs:
- name: Module hygiene - name: Module hygiene
run: | run: |
set -euo pipefail set -euo pipefail
go mod tidy go mod tidy
go mod verify
if ! go mod verify; then
echo "go mod verify failed; refreshing module cache and retrying" >&2
go clean -modcache
go mod download
go mod verify
fi
- name: Restore cached gosec binary - name: Restore cached gosec binary
id: cache-gosec id: cache-gosec
@@ -77,8 +70,6 @@ jobs:
- name: Run govulncheck - name: Run govulncheck
uses: golang/govulncheck-action@v1.0.4 uses: golang/govulncheck-action@v1.0.4
with: with:
go-version-file: go.mod
check-latest: false
go-package: ./... go-package: ./...
cache: true cache: true
cache-dependency-path: go.sum cache-dependency-path: go.sum
@@ -124,21 +115,24 @@ jobs:
VOCIFERATE_CACHE_TOKEN: ${{ steps.cache-token.outputs.value }} VOCIFERATE_CACHE_TOKEN: ${{ steps.cache-token.outputs.value }}
with: with:
version: ${{ steps.resolve-version.outputs.tag }} version: ${{ steps.resolve-version.outputs.tag }}
token: ${{ secrets.RELEASE_PAT }}
git-add-files: CHANGELOG.md release-version README.md AGENTS.md git-add-files: CHANGELOG.md release-version README.md AGENTS.md
- name: Summary - name: Summarize prepared release
if: ${{ always() }}
run: | run: |
set -euo pipefail set -euo pipefail
tag="${{ steps.prepare.outputs.version }}" tag="${{ steps.prepare.outputs.version }}"
{ {
echo "## Release Prepared" echo "## Release Prepared"
echo echo
echo "- Tag pushed: ${tag}" echo "- Tag pushed: ${tag}"
echo "- Calling Do Release workflow for ${tag}."
} >> "$SUMMARY_FILE" } >> "$SUMMARY_FILE"
- name: Summary
if: ${{ always() }}
run: |
set -euo pipefail
echo 'Summary' echo 'Summary'
echo echo
@@ -148,301 +142,9 @@ jobs:
echo 'No summary generated.' echo 'No summary generated.'
fi fi
release: publish:
runs-on: ubuntu-latest
container: docker.io/catthehacker/ubuntu:act-latest
needs: prepare needs: prepare
outputs: uses: ./.gitea/workflows/do-release.yml
tag: ${{ steps.publish.outputs.tag }} with:
version: ${{ steps.publish.outputs.version }} tag: ${{ needs.prepare.outputs.tag }}
defaults: secrets: inherit
run:
shell: bash
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_PAT }}
SUMMARY_FILE: ${{ runner.temp }}/release-summary.md
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve release version
id: resolve-version
env:
PREPARE_TAG: ${{ needs.prepare.outputs.tag }}
run: |
set -euo pipefail
tag="$(printf '%s' "${PREPARE_TAG}" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
# Unwrap Teacup expression strings if present.
if [[ "${tag}" =~ ^%\!t\(string=(.*)\)$ ]]; then
tag="${BASH_REMATCH[1]}"
fi
normalized="${tag#v}"
tag="v${normalized}"
echo "tag=${tag}" >> "$GITHUB_OUTPUT"
echo "version=${normalized}" >> "$GITHUB_OUTPUT"
- name: Checkout release tag
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: refs/tags/${{ steps.resolve-version.outputs.tag }}
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
check-latest: false
cache: true
cache-dependency-path: go.sum
- name: Preflight release API access
env:
TAG_NAME: ${{ steps.resolve-version.outputs.tag }}
run: |
set -euo pipefail
if [[ -z "${RELEASE_TOKEN:-}" ]]; then
echo "No release token available. Set secrets.RELEASE_PAT." >&2
exit 1
fi
api_base="${GITHUB_API_URL:-${GITHUB_SERVER_URL%/}/api/v1}"
repo_api="${api_base}/repos/${GITHUB_REPOSITORY}"
curl --fail-with-body -sS \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: application/json" \
"${repo_api}" >/dev/null
curl --fail-with-body -sS \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: application/json" \
"${repo_api}/releases?limit=1" >/dev/null
if ! git rev-parse --verify --quiet "refs/tags/${TAG_NAME}" >/dev/null; then
echo "Tag ${TAG_NAME} was not found in the checked out repository." >&2
exit 1
fi
- name: Create or update release
id: publish
uses: ./publish
with:
token: ${{ secrets.RELEASE_PAT }}
version: ${{ steps.resolve-version.outputs.version }}
- name: Build release binaries
env:
RELEASE_VERSION: ${{ steps.publish.outputs.version }}
run: |
set -euo pipefail
mkdir -p dist
for target in linux/amd64 linux/arm64; do
os="${target%/*}"
arch="${target#*/}"
bin="vociferate_${RELEASE_VERSION}_${os}_${arch}"
GOOS="$os" GOARCH="$arch" go build -trimpath -ldflags="-s -w" -o "dist/${bin}" ./cmd/vociferate
done
(
cd dist
shasum -a 256 * > checksums.txt
)
- name: Upload release binaries
env:
RELEASE_ID: ${{ steps.publish.outputs.release-id }}
RELEASE_VERSION: ${{ steps.publish.outputs.version }}
run: |
set -euo pipefail
raw_release_id="$(printf '%s' "${RELEASE_ID:-}" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
if [[ "$raw_release_id" =~ ^%\!t\(string=(.*)\)$ ]]; then
raw_release_id="${BASH_REMATCH[1]}"
fi
release_id="$(printf '%s' "$raw_release_id" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
if ! [[ "$release_id" =~ ^[0-9]+$ ]]; then
echo "Invalid release id from publish step: '${RELEASE_ID}'" >&2
exit 1
fi
release_detail_api="${GITHUB_API_URL:-${GITHUB_SERVER_URL%/}/api/v1}/repos/${GITHUB_REPOSITORY}/releases/${release_id}"
if ! curl --fail-with-body -sS \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: application/json" \
"$release_detail_api" >/dev/null; then
echo "Resolved release endpoint is not accessible: ${release_detail_api}" >&2
exit 1
fi
release_api="${GITHUB_API_URL:-${GITHUB_SERVER_URL%/}/api/v1}/repos/${GITHUB_REPOSITORY}/releases/${release_id}/assets"
for asset in dist/*; do
name="$(basename "$asset")"
assets_json="$(curl -sS --fail-with-body \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: application/json" \
"${release_api}")"
escaped_name="$(printf '%s' "$name" | sed 's/[][(){}.^$*+?|\\/]/\\&/g')"
existing_asset_id="$(printf '%s' "$assets_json" | tr -d '\n' | sed -n "s/.*{\"id\":\([0-9][0-9]*\)[^}]*\"name\":\"${escaped_name}\".*/\1/p")"
if [[ -n "$existing_asset_id" ]]; then
curl --fail-with-body \
-X DELETE \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: application/json" \
"${release_api}/${existing_asset_id}"
fi
curl --fail-with-body \
-X POST \
-H "Authorization: token ${RELEASE_TOKEN}" \
-H "Content-Type: application/octet-stream" \
"${release_api}?name=${name}" \
--data-binary "@${asset}"
done
- name: Summary
if: ${{ always() }}
env:
TAG_NAME: ${{ steps.publish.outputs.tag }}
RELEASE_VERSION: ${{ steps.publish.outputs.version }}
PUBLISH_OUTCOME: ${{ steps.publish.outcome }}
run: |
set -euo pipefail
if [[ "${PUBLISH_OUTCOME}" == "success" ]]; then
{
echo "## Release Published"
echo
echo "- Tag: ${TAG_NAME}"
echo "- Release notes sourced from changelog entry ${RELEASE_VERSION}."
echo "- Published assets: vociferate_${RELEASE_VERSION}_linux_amd64, vociferate_${RELEASE_VERSION}_linux_arm64, checksums.txt"
} >> "$SUMMARY_FILE"
else
{
echo "## Release Failed"
echo
echo "- Tag: ${TAG_NAME:-unknown}"
echo "- Create or update release step did not complete successfully."
} >> "$SUMMARY_FILE"
fi
echo 'Summary'
echo
if [[ -s "$SUMMARY_FILE" ]]; then
cat "$SUMMARY_FILE"
else
echo 'No summary generated.'
fi
validate:
runs-on: ubuntu-latest
container: docker.io/catthehacker/ubuntu:act-latest
needs: release
strategy:
fail-fast: false
matrix:
include:
- asset_arch: amd64
run_command: ./vociferate
- asset_arch: arm64
run_command: qemu-aarch64-static ./vociferate
defaults:
run:
shell: bash
env:
SUMMARY_FILE: ${{ runner.temp }}/validate-summary.md
steps:
- name: Checkout tagged revision
uses: actions/checkout@v4
with:
ref: refs/tags/${{ needs.release.outputs.tag }}
- name: Install arm64 emulator
if: ${{ matrix.asset_arch == 'arm64' }}
run: |
set -euo pipefail
apt-get update
apt-get install -y qemu-user-static
- name: Download released binary
env:
TOKEN: ${{ secrets.RELEASE_PAT }}
TAG_NAME: ${{ needs.release.outputs.tag }}
RELEASE_VERSION: ${{ needs.release.outputs.version }}
ASSET_ARCH: ${{ matrix.asset_arch }}
SERVER_URL: ${{ github.server_url }}
run: |
set -euo pipefail
asset_name="vociferate_${RELEASE_VERSION}_linux_${ASSET_ARCH}"
asset_url="${SERVER_URL}/aether/vociferate/releases/download/${TAG_NAME}/${asset_name}"
curl --fail --location \
-H "Authorization: token ${TOKEN}" \
-o vociferate \
"$asset_url"
chmod +x vociferate
echo "asset_name=${asset_name}" >> "$GITHUB_ENV"
- name: Smoke test released binary
env:
RUN_COMMAND: ${{ matrix.run_command }}
TAG_NAME: ${{ needs.release.outputs.tag }}
run: |
set -euo pipefail
${RUN_COMMAND} --help >/dev/null
recommend_stderr="$(mktemp)"
if ${RUN_COMMAND} --recommend --root . >/dev/null 2>"${recommend_stderr}"; then
echo "Expected --recommend to fail on the tagged release checkout" >&2
exit 1
fi
recommend_error="$(cat "${recommend_stderr}")"
case "${recommend_error}" in
*"unreleased section is empty"*)
;;
*)
echo "Unexpected recommend failure output: ${recommend_error}" >&2
exit 1
;;
esac
{
echo "## Released Binary Validation (${{ matrix.asset_arch }})"
echo
echo "- Release tag: ${TAG_NAME}"
echo "- Asset: ${asset_name}"
echo "- Binary executed successfully via --help."
echo "- --recommend failed as expected on the tagged checkout because Unreleased is empty."
} >> "$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

@@ -30,8 +30,8 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version: '1.26.1'
check-latest: false check-latest: true
cache: true cache: true
cache-dependency-path: go.sum cache-dependency-path: go.sum
@@ -41,15 +41,8 @@ jobs:
- name: Module hygiene - name: Module hygiene
run: | run: |
set -euo pipefail set -euo pipefail
go mod tidy go mod tidy
go mod verify
if ! go mod verify; then
echo "go mod verify failed; refreshing module cache and retrying" >&2
go clean -modcache
go mod download
go mod verify
fi
- name: Restore cached gosec binary - name: Restore cached gosec binary
id: cache-gosec id: cache-gosec
@@ -73,8 +66,6 @@ jobs:
- name: Run govulncheck - name: Run govulncheck
uses: golang/govulncheck-action@v1.0.4 uses: golang/govulncheck-action@v1.0.4
with: with:
go-version-file: go.mod
check-latest: false
go-package: ./... go-package: ./...
cache: true cache: true
cache-dependency-path: go.sum cache-dependency-path: go.sum
@@ -124,8 +115,8 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@v5 uses: actions/setup-go@v5
with: with:
go-version-file: go.mod go-version: '1.26.1'
check-latest: false check-latest: true
cache: true cache: true
cache-dependency-path: go.sum cache-dependency-path: go.sum

View File

@@ -30,7 +30,7 @@ Apply these checks before invoking actions:
- Checkout repository first. - Checkout repository first.
- For prepare/publish flows that depend on tags/history, use full history checkout (`fetch-depth: 0`). - For prepare/publish flows that depend on tags/history, use full history checkout (`fetch-depth: 0`).
- Use `secrets.RELEASE_PAT` for release/tag/update operations (prepare/publish/do-release) so tag pushes trigger downstream workflows reliably. - Use valid credentials for release/comment API calls. On GitHub, `secrets.GITHUB_TOKEN` is used; on self-hosted Gitea, set `secrets.GITEA_TOKEN`.
- `do-release` and `decorate-pr` now run preflight API checks and fail fast when token credentials are missing or insufficient. - `do-release` and `decorate-pr` now run preflight API checks and fail fast when token credentials are missing or insufficient.
- Set required vars/secrets for coverage uploads: - Set required vars/secrets for coverage uploads:
- `vars.ARTEFACT_BUCKET_NAME` - `vars.ARTEFACT_BUCKET_NAME`

View File

@@ -13,18 +13,6 @@ A `### Breaking` section is used in addition to Keep a Changelog's standard sect
### Added ### Added
### Changed
### Removed
### Fixed
## [1.1.0] - 2026-03-21
### Breaking
### Added
- Added changelog gate validation to `decorate-pr` action for enforcing changelog updates on qualifying code changes. - Added changelog gate validation to `decorate-pr` action for enforcing changelog updates on qualifying code changes.
- Changelog gate modes: `strict` (fails job on violation) and `soft` (warns via PR comment). - Changelog gate modes: `strict` (fails job on violation) and `soft` (warns via PR comment).
- Docs-only PR exemption with customizable glob patterns for documentation files. - Docs-only PR exemption with customizable glob patterns for documentation files.
@@ -41,27 +29,18 @@ A `### Breaking` section is used in addition to Keep a Changelog's standard sect
- `decorate-pr` now reads Unreleased changelog content through the `vociferate` Go CLI instead of maintaining separate shell parsing logic in the composite action. - `decorate-pr` now reads Unreleased changelog content through the `vociferate` Go CLI instead of maintaining separate shell parsing logic in the composite action.
- `publish` now extracts tagged release notes through the `vociferate` Go CLI instead of duplicating changelog section parsing in shell. - `publish` now extracts tagged release notes through the `vociferate` Go CLI instead of duplicating changelog section parsing in shell.
- Composite actions now share a centralized `run-vociferate` orchestration flow, with binary-versus-source execution delegated through shared composite actions and single-use runtime/download logic folded back into `run-vociferate.binary`. - Composite actions now share a centralized `run-vociferate` orchestration flow, with binary-versus-source execution delegated through shared composite actions and single-use runtime/download logic folded back into `run-vociferate.binary`.
- `run-vociferate` now contains both binary and source execution flows directly in a single action implementation, removing nested local action wrappers for better runner compatibility. - `run-vociferate/binary` and `run-vociferate/code` are now nested under `run-vociferate/` so callers reference them as `./run-vociferate/binary` and `./run-vociferate/code`.
- Release automation now requires `secrets.RELEASE_PAT` for prepare/publish/do-release operations instead of defaulting to `GITHUB_TOKEN`/`GITEA_TOKEN`.
### Removed ### Removed
### Fixed ### Fixed
- Prevented `govulncheck-action` from defaulting to `setup-go` version `stable` by explicitly setting `go-version-file` and disabling `check-latest`, avoiding unauthenticated GitHub API rate-limit failures on self-hosted/act-style runners. - Fixed `decorate-pr/action.yml` YAML validation by extracting PR comment rendering into `decorate-pr/build-comment.sh`, removing the duplicated changelog extraction step, and correcting the gate failure output reference.
- Made `do-release` version resolution resilient to `workflow_call` input passing issues by adding a separate tag detection step that fetches and discovers the latest tag from origin as a fallback when `inputs.tag` is empty, enabling proper operation even when Gitea's workflow_call doesn't pass inputs through correctly. - Fixed docs-only detection in `decorate-pr` changelog gate: file list was iterated in a piped subshell so `docs_only` never propagated to the parent scope; replaced pipe with process substitution.
- Fixed version resolution in `do-release` workflow by moving version calculation before checkout, resolving from inputs/git tags, and always passing explicit version to `publish` action.
- Fixed tag detection in `do-release` to prioritize the tag at current HEAD (created by `prepare-release`) over the globally latest tag, ensuring correct version is detected when called from `prepare-release` workflow.
- Fixed `do-release` workflow_call resolution on Teacup runners by explicitly falling back to `needs.prepare.outputs.tag` and normalizing `%!t(string=...)` wrapped values before choosing a release tag.
- Fixed release-chain triggering by using a PAT for release commit/tag pushes so downstream release workflows are triggered reliably.
- Made `publish` action version resolution more robust with clearer error messages when version input is missing and workflow is not running from a tag push.
- Fixed `do-release` workflow to always checkout the resolved release tag, eliminating conditional checkout logic that could skip the checkout when called from `prepare-release` workflow.
- Pinned `securego/gosec` and `golang/govulncheck-action` to concrete version tags (`v2.22.4` and `v1.0.4`) so self-hosted Gitea runners can resolve them via direct git clone without relying on the GitHub Actions floating-tag API. - Pinned `securego/gosec` and `golang/govulncheck-action` to concrete version tags (`v2.22.4` and `v1.0.4`) so self-hosted Gitea runners can resolve them via direct git clone without relying on the GitHub Actions floating-tag API.
- Restored explicit gosec caching by storing a pinned `v2.22.4` binary under `${{ runner.temp }}/gosec-bin` with `actions/cache@v4`, so CI keeps fast security scans while still using the Go 1.26 toolchain from `setup-go`. - Restored explicit gosec caching by storing a pinned `v2.22.4` binary under `${{ runner.temp }}/gosec-bin` with `actions/cache@v4`, so CI keeps fast security scans while still using the Go 1.26 toolchain from `setup-go`.
- Replaced `securego/gosec` composite action with a direct `go install github.com/securego/gosec/v2/cmd/gosec@v2.22.4 && gosec ./...` run step so gosec uses the Go 1.26 toolchain installed by `setup-go` rather than the action's bundled Go 1.24 binary which ignores `GOTOOLCHAIN=auto`. - Replaced `securego/gosec` composite action with a direct `go install github.com/securego/gosec/v2/cmd/gosec@v2.22.4 && gosec ./...` run step so gosec uses the Go 1.26 toolchain installed by `setup-go` rather than the action's bundled Go 1.24 binary which ignores `GOTOOLCHAIN=auto`.
- Fixed nested local composite-action references to use repository-local `./run-vociferate` paths so strict runners do not misparse parent-directory (`../`) action references as malformed remote coordinates. - Fixed nested local composite-action references to use `./../run-vociferate` (instead of `../run-vociferate`) so strict runners that enforce `{org}/{repo}[/path]@ref` for non-local paths correctly classify them as local actions.
- Consolidated `run-vociferate` binary and source execution flows directly into the main `run-vociferate` action to avoid nested local-action path resolution issues on strict runners.
- Hardened workflow module hygiene by retrying `go mod verify` after a module-cache refresh (`go clean -modcache` + `go mod download`) when runners report modified cached dependency directories.
## [1.0.2] - 2026-03-21 ## [1.0.2] - 2026-03-21
@@ -168,10 +147,9 @@ 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]: https://git.hrafn.xyz/aether/vociferate/compare/v1.1.0...main [Unreleased]: https://git.hrafn.xyz/aether/vociferate/compare/v1.0.2...main
[1.1.0]: https://git.hrafn.xyz/aether/vociferate/compare/v1.0.2...v1.1.0
[1.0.2]: https://git.hrafn.xyz/aether/vociferate/compare/v1.0.1...v1.0.2 [1.0.2]: https://git.hrafn.xyz/aether/vociferate/compare/v1.0.1...v1.0.2
[1.0.1]: https://git.hrafn.xyz/aether/vociferate/compare/v1.0.0...v1.0.1 [1.0.1]: https://git.hrafn.xyz/aether/vociferate/compare/v1.0.0...v1.0.1
[1.0.0]: https://git.hrafn.xyz/aether/vociferate/compare/v0.2.0...v1.0.0 [1.0.0]: https://git.hrafn.xyz/aether/vociferate/compare/v0.2.0...v1.0.0
[0.2.0]: https://git.hrafn.xyz/aether/vociferate/compare/v0.1.0...v0.2.0 [0.2.0]: https://git.hrafn.xyz/aether/vociferate/compare/v0.1.0...v0.2.0
[0.1.0]: https://git.hrafn.xyz/aether/vociferate/compare/81dced6...v0.1.0 [0.1.0]: https://git.hrafn.xyz/aether/vociferate/compare/2060af6...v0.1.0

View File

@@ -63,14 +63,13 @@ and `version-pattern`:
```yaml ```yaml
- uses: https://git.hrafn.xyz/aether/vociferate/prepare@v1.1.0 - uses: https://git.hrafn.xyz/aether/vociferate/prepare@v1.1.0
with: with:
token: ${{ secrets.RELEASE_PAT }}
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` requires a PAT input for authenticated commit/push/tag operations. `prepare` uses `github.token` internally for authenticated fetch/push operations,
Pass `token: ${{ secrets.RELEASE_PAT }}` when invoking the action. so no token input is required.
### `publish` — create release with changelog notes ### `publish` — create release with changelog notes
@@ -97,8 +96,9 @@ 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.
The reusable `Do Release` workflow now runs preflight checks before publish to The reusable `Do Release` workflow now runs preflight checks before publish to
fail fast when the release token is missing or lacks API access. Set fail fast when the release token is missing or lacks API access. On
`secrets.RELEASE_PAT` and use it for prepare/publish release operations. self-hosted Gitea, set `secrets.GITEA_TOKEN`; on GitHub, `secrets.GITHUB_TOKEN`
is used automatically.
The `publish` action outputs `release-id` so you can upload additional release The `publish` action outputs `release-id` so you can upload additional release
assets after it runs: assets after it runs:
@@ -110,7 +110,7 @@ assets after it runs:
- name: Upload my binary - name: Upload my binary
run: | run: |
curl --fail-with-body -X POST \ curl --fail-with-body -X POST \
-H "Authorization: token ${{ secrets.RELEASE_PAT }}" \ -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Content-Type: application/octet-stream" \ -H "Content-Type: application/octet-stream" \
"${{ github.api_url }}/repos/${{ github.repository }}/releases/${{ steps.publish.outputs.release-id }}/assets?name=myapp" \ "${{ github.api_url }}/repos/${{ github.repository }}/releases/${{ steps.publish.outputs.release-id }}/assets?name=myapp" \
--data-binary "@dist/myapp" --data-binary "@dist/myapp"

View File

@@ -164,7 +164,7 @@ runs:
- name: Extract changelog unreleased entries - name: Extract changelog unreleased entries
id: extract-changelog id: extract-changelog
if: steps.changelog-file.outputs.exists == 'true' if: steps.changelog-file.outputs.exists == 'true'
uses: ./run-vociferate uses: ./../run-vociferate
with: with:
root: ${{ github.workspace }} root: ${{ github.workspace }}
changelog: ${{ inputs.changelog }} changelog: ${{ inputs.changelog }}

View File

@@ -42,11 +42,6 @@ inputs:
custom version-file. custom version-file.
required: false required: false
default: 'CHANGELOG.md release-version' default: 'CHANGELOG.md release-version'
token:
description: >
Personal access token used to authenticate commit, push, and tag
operations. Required to ensure downstream workflows trigger on tag push.
required: true
outputs: outputs:
version: version:
@@ -71,7 +66,7 @@ runs:
- name: Recommend version - name: Recommend version
id: recommend-version id: recommend-version
if: steps.normalize-version.outputs.value == '' if: steps.normalize-version.outputs.value == ''
uses: ./run-vociferate uses: ./../run-vociferate
with: with:
root: ${{ github.workspace }} root: ${{ github.workspace }}
version-file: ${{ inputs.version-file }} version-file: ${{ inputs.version-file }}
@@ -107,7 +102,7 @@ runs:
printf 'value=%s\n' "$(date -u +%F)" >> "$GITHUB_OUTPUT" printf 'value=%s\n' "$(date -u +%F)" >> "$GITHUB_OUTPUT"
- name: Prepare release files - name: Prepare release files
uses: ./run-vociferate uses: ./../run-vociferate
with: with:
root: ${{ github.workspace }} root: ${{ github.workspace }}
version-file: ${{ inputs.version-file }} version-file: ${{ inputs.version-file }}
@@ -119,7 +114,7 @@ runs:
- name: Commit and push release - name: Commit and push release
shell: bash shell: bash
env: env:
TOKEN: ${{ inputs.token }} TOKEN: ${{ github.token }}
GIT_USER_NAME: ${{ inputs.git-user-name }} GIT_USER_NAME: ${{ inputs.git-user-name }}
GIT_USER_EMAIL: ${{ inputs.git-user-email }} GIT_USER_EMAIL: ${{ inputs.git-user-email }}
GIT_ADD_FILES: ${{ inputs.git-add-files }} GIT_ADD_FILES: ${{ inputs.git-add-files }}
@@ -129,11 +124,6 @@ runs:
run: | run: |
set -euo pipefail set -euo pipefail
if [[ -z "${TOKEN:-}" ]]; then
echo "A release PAT is required. Provide inputs.token (for example secrets.RELEASE_PAT)." >&2
exit 1
fi
case "$GITHUB_SERVER_URL" in case "$GITHUB_SERVER_URL" in
https://*) https://*)
authed_remote="https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git" authed_remote="https://oauth2:${TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git"

View File

@@ -7,9 +7,10 @@ description: >
inputs: inputs:
token: token:
description: > description: >
Personal access token used to authenticate release API calls. Token used to authenticate release API calls. Defaults to the
Required to support release updates across workflow boundaries. workflow token.
required: true required: false
default: ''
version: version:
description: > description: >
Semantic version to publish (with or without leading v). When omitted, Semantic version to publish (with or without leading v). When omitted,
@@ -56,7 +57,6 @@ runs:
normalized="${tag#v}" normalized="${tag#v}"
else else
echo "A version input is required when the workflow is not running from a tag push" >&2 echo "A version input is required when the workflow is not running from a tag push" >&2
echo "Provide version via input or ensure HEAD is at a tagged commit." >&2
exit 1 exit 1
fi fi
@@ -65,7 +65,7 @@ runs:
- name: Extract release notes - name: Extract release notes
id: extract-notes id: extract-notes
uses: ./run-vociferate uses: ./../run-vociferate
with: with:
root: ${{ github.workspace }} root: ${{ github.workspace }}
changelog: ${{ inputs.changelog }} changelog: ${{ inputs.changelog }}
@@ -90,66 +90,41 @@ runs:
id: create-release id: create-release
shell: bash shell: bash
env: env:
TOKEN: ${{ inputs.token }} TOKEN: ${{ inputs.token != '' && inputs.token || github.token }}
TAG_NAME: ${{ steps.resolve-version.outputs.tag }} TAG_NAME: ${{ steps.resolve-version.outputs.tag }}
RELEASE_NOTES_FILE: ${{ steps.write-notes.outputs.notes_file }} RELEASE_NOTES_FILE: ${{ steps.write-notes.outputs.notes_file }}
GITHUB_API_URL: ${{ github.api_url }} GITHUB_API_URL: ${{ github.api_url }}
GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_SHA: ${{ github.sha }}
GITHUB_REPOSITORY: ${{ github.repository }} GITHUB_REPOSITORY: ${{ github.repository }}
run: | run: |
set -euo pipefail set -euo pipefail
parse_release_id() {
local json_file="$1"
if command -v python3 >/dev/null 2>&1; then
python3 -c 'import json, sys; payload = json.load(open(sys.argv[1], encoding="utf-8")); value = payload.get("id"); print(value if isinstance(value, int) else "")' "$json_file"
return
fi
# Fallback for environments without python3.
sed -n 's/.*"id"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' "$json_file" | head -n 1
}
raw_token="$(printf '%s' "${TOKEN:-}" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
if [[ "$raw_token" =~ ^%\!t\(string=(.*)\)$ ]]; then
raw_token="${BASH_REMATCH[1]}"
fi
api_token="$(printf '%s' "$raw_token" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
if [[ -z "$api_token" ]]; then
echo "inputs.token is required (set to secrets.RELEASE_PAT)." >&2
exit 1
fi
release_notes="$(cat "$RELEASE_NOTES_FILE")" release_notes="$(cat "$RELEASE_NOTES_FILE")"
escaped_release_notes="$(printf '%s' "$release_notes" | sed 's/\\/\\\\/g; s/"/\\"/g; :a;N;$!ba;s/\n/\\n/g')" escaped_release_notes="$(printf '%s' "$release_notes" | sed 's/\\/\\\\/g; s/"/\\"/g; :a;N;$!ba;s/\n/\\n/g')"
release_api="${GITHUB_API_URL:-${GITHUB_SERVER_URL%/}/api/v1}/repos/${GITHUB_REPOSITORY}/releases" release_api="${GITHUB_API_URL:-${GITHUB_SERVER_URL%/}/api/v1}/repos/${GITHUB_REPOSITORY}/releases"
release_by_tag_api="${release_api}/tags/${TAG_NAME}" release_by_tag_api="${release_api}/tags/${TAG_NAME}"
status_code="$(curl -sS -o release-existing.json -w '%{http_code}' \ status_code="$(curl -sS -o release-existing.json -w '%{http_code}' \
-H "Authorization: token ${api_token}" \ -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${release_by_tag_api}")" "${release_by_tag_api}")"
if [[ "$status_code" == "200" ]]; then if [[ "$status_code" == "200" ]]; then
existing_release_id="$(parse_release_id release-existing.json)" existing_release_id="$(sed -n 's/.*"id"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' release-existing.json | head -n 1)"
if [[ -z "$existing_release_id" ]]; then if [[ -z "$existing_release_id" ]]; then
echo "Failed to parse existing release id for ${TAG_NAME}" >&2 echo "Failed to parse existing release id for ${TAG_NAME}" >&2
cat release-existing.json >&2 cat release-existing.json >&2
exit 1 exit 1
fi fi
if ! curl --fail-with-body \ curl --fail-with-body \
-X PATCH \ -X PATCH \
-H "Authorization: token ${api_token}" \ -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${release_api}/${existing_release_id}" \ "${release_api}/${existing_release_id}" \
--data "{\"tag_name\":\"${TAG_NAME}\",\"name\":\"${TAG_NAME}\",\"body\":\"${escaped_release_notes}\",\"draft\":false,\"prerelease\":false}" \ --data "{\"tag_name\":\"${TAG_NAME}\",\"target\":\"${GITHUB_SHA}\",\"name\":\"${TAG_NAME}\",\"body\":\"${escaped_release_notes}\",\"draft\":false,\"prerelease\":false}" \
--output release.json; then --output release.json
cat release.json >&2 || true
exit 1
fi
echo "id=$existing_release_id" >> "$GITHUB_OUTPUT" echo "id=$existing_release_id" >> "$GITHUB_OUTPUT"
elif [[ "$status_code" != "404" ]]; then elif [[ "$status_code" != "404" ]]; then
@@ -157,18 +132,15 @@ runs:
cat release-existing.json >&2 cat release-existing.json >&2
exit 1 exit 1
else else
if ! curl --fail-with-body \ curl --fail-with-body \
-X POST \ -X POST \
-H "Authorization: token ${api_token}" \ -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${release_api}" \ "${release_api}" \
--data "{\"tag_name\":\"${TAG_NAME}\",\"name\":\"${TAG_NAME}\",\"body\":\"${escaped_release_notes}\",\"draft\":false,\"prerelease\":false}" \ --data "{\"tag_name\":\"${TAG_NAME}\",\"target\":\"${GITHUB_SHA}\",\"name\":\"${TAG_NAME}\",\"body\":\"${escaped_release_notes}\",\"draft\":false,\"prerelease\":false}" \
--output release.json; then --output release.json
cat release.json >&2 || true
exit 1
fi
release_id="$(parse_release_id release.json)" release_id="$(sed -n 's/.*"id"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' release.json | head -n 1)"
if [[ -z "$release_id" ]]; then if [[ -z "$release_id" ]]; then
echo "Failed to parse release id from API response" >&2 echo "Failed to parse release id from API response" >&2
cat release.json >&2 cat release.json >&2

View File

@@ -1 +1 @@
1.1.0 1.0.2

View File

@@ -63,198 +63,35 @@ runs:
printf 'use_binary=false\n' >> "$GITHUB_OUTPUT" printf 'use_binary=false\n' >> "$GITHUB_OUTPUT"
fi fi
- name: Resolve binary metadata
id: resolve-binary
if: steps.resolve-runtime.outputs.use_binary == 'true'
shell: bash
env:
ACTION_REF: ${{ github.action_ref }}
ACTION_REPOSITORY: ${{ github.action_repository }}
CACHE_TOKEN: ${{ env.VOCIFERATE_CACHE_TOKEN }}
SERVER_URL: ${{ github.server_url }}
RUNNER_ARCH: ${{ runner.arch }}
RUNNER_TEMP: ${{ runner.temp }}
run: |
set -euo pipefail
if [[ "$ACTION_REF" != v* ]]; then
echo "run-vociferate binary path requires github.action_ref to be a release tag" >&2
exit 1
fi
case "$RUNNER_ARCH" in
X64)
arch="amd64"
;;
ARM64)
arch="arm64"
;;
*)
echo "Unsupported runner architecture: $RUNNER_ARCH" >&2
exit 1
;;
esac
release_tag="$ACTION_REF"
normalized_version="${release_tag#v}"
asset_name="vociferate_${normalized_version}_linux_${arch}"
cache_dir="${RUNNER_TEMP}/vociferate/${release_tag}/linux-${arch}"
binary_path="${cache_dir}/vociferate"
asset_url="${SERVER_URL}/aether/vociferate/releases/download/${release_tag}/${asset_name}"
provided_cache_token="$(printf '%s' "${CACHE_TOKEN:-}" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
if [[ -n "$provided_cache_token" ]]; then
cache_token="$provided_cache_token"
else
cache_token="${ACTION_REPOSITORY:-aether/vociferate}-${release_tag}"
fi
mkdir -p "$cache_dir"
printf 'cache_token=%s\n' "$cache_token" >> "$GITHUB_OUTPUT"
printf 'cache_dir=%s\n' "$cache_dir" >> "$GITHUB_OUTPUT"
printf 'binary_path=%s\n' "$binary_path" >> "$GITHUB_OUTPUT"
printf 'asset_url=%s\n' "$asset_url" >> "$GITHUB_OUTPUT"
- name: Restore cached binary
id: cache-vociferate
if: steps.resolve-runtime.outputs.use_binary == 'true'
uses: actions/cache@v4
with:
path: ${{ steps.resolve-binary.outputs.cache_dir }}
key: vociferate-${{ steps.resolve-binary.outputs.cache_token }}-linux-${{ runner.arch }}
- name: Download binary
if: steps.resolve-runtime.outputs.use_binary == 'true' && steps.cache-vociferate.outputs.cache-hit != 'true'
shell: bash
env:
TOKEN: ${{ github.token }}
ASSET_URL: ${{ steps.resolve-binary.outputs.asset_url }}
BINARY_PATH: ${{ steps.resolve-binary.outputs.binary_path }}
run: |
set -euo pipefail
curl --fail --location \
-H "Authorization: token ${TOKEN}" \
-o "$BINARY_PATH" \
"$ASSET_URL"
chmod +x "$BINARY_PATH"
- name: Run binary - name: Run binary
id: run-binary id: run-binary
if: steps.resolve-runtime.outputs.use_binary == 'true' if: steps.resolve-runtime.outputs.use_binary == 'true'
shell: bash uses: ./binary
env: with:
VOCIFERATE_BIN: ${{ steps.resolve-binary.outputs.binary_path }} root: ${{ inputs.root }}
ROOT: ${{ inputs.root }} version-file: ${{ inputs.version-file }}
VERSION_FILE: ${{ inputs.version-file }} version-pattern: ${{ inputs.version-pattern }}
VERSION_PATTERN: ${{ inputs.version-pattern }} changelog: ${{ inputs.changelog }}
CHANGELOG: ${{ inputs.changelog }} version: ${{ inputs.version }}
VERSION: ${{ inputs.version }} date: ${{ inputs.date }}
DATE: ${{ inputs.date }} recommend: ${{ inputs.recommend }}
RECOMMEND: ${{ inputs.recommend }} print-unreleased: ${{ inputs.print-unreleased }}
PRINT_UNRELEASED: ${{ inputs.print-unreleased }} print-release-notes: ${{ inputs.print-release-notes }}
PRINT_RELEASE_NOTES: ${{ inputs.print-release-notes }}
VOCIFERATE_REPOSITORY_URL: ${{ vars.VOCIFERATE_REPOSITORY_URL }}
run: |
set -euo pipefail
command=("$VOCIFERATE_BIN" --root "$ROOT")
if [[ -n "$VERSION_FILE" ]]; then
command+=(--version-file "$VERSION_FILE")
fi
if [[ -n "$VERSION_PATTERN" ]]; then
command+=(--version-pattern "$VERSION_PATTERN")
fi
if [[ -n "$CHANGELOG" ]]; then
command+=(--changelog "$CHANGELOG")
fi
if [[ -n "$VERSION" ]]; then
command+=(--version "$VERSION")
fi
if [[ -n "$DATE" ]]; then
command+=(--date "$DATE")
fi
if [[ "$RECOMMEND" == 'true' ]]; then
command+=(--recommend)
fi
if [[ "$PRINT_UNRELEASED" == 'true' ]]; then
command+=(--print-unreleased)
fi
if [[ "$PRINT_RELEASE_NOTES" == 'true' ]]; then
command+=(--print-release-notes)
fi
stdout_file="$(mktemp)"
trap 'rm -f "$stdout_file"' EXIT
"${command[@]}" > "$stdout_file"
delimiter="EOF_STDOUT"
printf 'stdout<<%s\n' "$delimiter" >> "$GITHUB_OUTPUT"
cat "$stdout_file" >> "$GITHUB_OUTPUT"
printf '%s\n' "$delimiter" >> "$GITHUB_OUTPUT"
- name: Run source - name: Run source
id: run-code id: run-code
if: steps.resolve-runtime.outputs.use_binary != 'true' if: steps.resolve-runtime.outputs.use_binary != 'true'
shell: bash uses: ./code
env: with:
ROOT: ${{ inputs.root }} root: ${{ inputs.root }}
VERSION_FILE: ${{ inputs.version-file }} version-file: ${{ inputs.version-file }}
VERSION_PATTERN: ${{ inputs.version-pattern }} version-pattern: ${{ inputs.version-pattern }}
CHANGELOG: ${{ inputs.changelog }} changelog: ${{ inputs.changelog }}
VERSION: ${{ inputs.version }} version: ${{ inputs.version }}
DATE: ${{ inputs.date }} date: ${{ inputs.date }}
RECOMMEND: ${{ inputs.recommend }} recommend: ${{ inputs.recommend }}
PRINT_UNRELEASED: ${{ inputs.print-unreleased }} print-unreleased: ${{ inputs.print-unreleased }}
PRINT_RELEASE_NOTES: ${{ inputs.print-release-notes }} print-release-notes: ${{ inputs.print-release-notes }}
VOCIFERATE_REPOSITORY_URL: ${{ vars.VOCIFERATE_REPOSITORY_URL }}
run: |
set -euo pipefail
source_root="$GITHUB_ACTION_PATH"
while [[ ! -f "$source_root/go.mod" ]] && [[ "$source_root" != "/" ]]; do
source_root="$(realpath "$source_root/..")"
done
if [[ ! -f "$source_root/go.mod" ]]; then
echo "Could not locate Go module root from $GITHUB_ACTION_PATH" >&2
exit 1
fi
command=(go run ./cmd/vociferate --root "$ROOT")
if [[ -n "$VERSION_FILE" ]]; then
command+=(--version-file "$VERSION_FILE")
fi
if [[ -n "$VERSION_PATTERN" ]]; then
command+=(--version-pattern "$VERSION_PATTERN")
fi
if [[ -n "$CHANGELOG" ]]; then
command+=(--changelog "$CHANGELOG")
fi
if [[ -n "$VERSION" ]]; then
command+=(--version "$VERSION")
fi
if [[ -n "$DATE" ]]; then
command+=(--date "$DATE")
fi
if [[ "$RECOMMEND" == 'true' ]]; then
command+=(--recommend)
fi
if [[ "$PRINT_UNRELEASED" == 'true' ]]; then
command+=(--print-unreleased)
fi
if [[ "$PRINT_RELEASE_NOTES" == 'true' ]]; then
command+=(--print-release-notes)
fi
stdout_file="$(mktemp)"
trap 'rm -f "$stdout_file"' EXIT
(cd "$source_root" && "${command[@]}") > "$stdout_file"
delimiter="EOF_STDOUT"
printf 'stdout<<%s\n' "$delimiter" >> "$GITHUB_OUTPUT"
cat "$stdout_file" >> "$GITHUB_OUTPUT"
printf '%s\n' "$delimiter" >> "$GITHUB_OUTPUT"
- name: Finalize stdout - name: Finalize stdout
id: finalize id: finalize

View File

@@ -0,0 +1,176 @@
name: vociferate/run-vociferate-binary
description: Execute vociferate through a released binary.
inputs:
root:
description: Repository root to pass to vociferate.
required: true
version-file:
description: Optional version file path.
required: false
default: ''
version-pattern:
description: Optional version pattern.
required: false
default: ''
changelog:
description: Optional changelog path.
required: false
default: ''
version:
description: Optional version argument.
required: false
default: ''
date:
description: Optional date argument.
required: false
default: ''
recommend:
description: Whether to run vociferate with --recommend.
required: false
default: 'false'
print-unreleased:
description: Whether to print the Unreleased body.
required: false
default: 'false'
print-release-notes:
description: Whether to print the release notes section for version.
required: false
default: 'false'
outputs:
stdout:
description: Captured stdout from the vociferate invocation.
value: ${{ steps.run.outputs.stdout }}
runs:
using: composite
steps:
- name: Resolve binary metadata
id: resolve-binary
shell: bash
env:
ACTION_REF: ${{ github.action_ref }}
ACTION_REPOSITORY: ${{ github.action_repository }}
CACHE_TOKEN: ${{ env.VOCIFERATE_CACHE_TOKEN }}
SERVER_URL: ${{ github.server_url }}
RUNNER_ARCH: ${{ runner.arch }}
RUNNER_TEMP: ${{ runner.temp }}
run: |
set -euo pipefail
if [[ "$ACTION_REF" != v* ]]; then
echo "run-vociferate.binary requires github.action_ref to be a release tag" >&2
exit 1
fi
case "$RUNNER_ARCH" in
X64)
arch="amd64"
;;
ARM64)
arch="arm64"
;;
*)
echo "Unsupported runner architecture: $RUNNER_ARCH" >&2
exit 1
;;
esac
release_tag="$ACTION_REF"
normalized_version="${release_tag#v}"
asset_name="vociferate_${normalized_version}_linux_${arch}"
cache_dir="${RUNNER_TEMP}/vociferate/${release_tag}/linux-${arch}"
binary_path="${cache_dir}/vociferate"
asset_url="${SERVER_URL}/aether/vociferate/releases/download/${release_tag}/${asset_name}"
provided_cache_token="$(printf '%s' "${CACHE_TOKEN:-}" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
if [[ -n "$provided_cache_token" ]]; then
cache_token="$provided_cache_token"
else
cache_token="${ACTION_REPOSITORY:-aether/vociferate}-${release_tag}"
fi
mkdir -p "$cache_dir"
printf 'cache_token=%s\n' "$cache_token" >> "$GITHUB_OUTPUT"
printf 'cache_dir=%s\n' "$cache_dir" >> "$GITHUB_OUTPUT"
printf 'binary_path=%s\n' "$binary_path" >> "$GITHUB_OUTPUT"
printf 'asset_url=%s\n' "$asset_url" >> "$GITHUB_OUTPUT"
- name: Restore cached binary
id: cache-vociferate
uses: actions/cache@v4
with:
path: ${{ steps.resolve-binary.outputs.cache_dir }}
key: vociferate-${{ steps.resolve-binary.outputs.cache_token }}-linux-${{ runner.arch }}
- name: Download binary
if: steps.cache-vociferate.outputs.cache-hit != 'true'
shell: bash
env:
TOKEN: ${{ github.token }}
ASSET_URL: ${{ steps.resolve-binary.outputs.asset_url }}
BINARY_PATH: ${{ steps.resolve-binary.outputs.binary_path }}
run: |
set -euo pipefail
curl --fail --location \
-H "Authorization: token ${TOKEN}" \
-o "$BINARY_PATH" \
"$ASSET_URL"
chmod +x "$BINARY_PATH"
- name: Run binary
id: run
shell: bash
env:
VOCIFERATE_BIN: ${{ steps.resolve-binary.outputs.binary_path }}
ROOT: ${{ inputs.root }}
VERSION_FILE: ${{ inputs.version-file }}
VERSION_PATTERN: ${{ inputs.version-pattern }}
CHANGELOG: ${{ inputs.changelog }}
VERSION: ${{ inputs.version }}
DATE: ${{ inputs.date }}
RECOMMEND: ${{ inputs.recommend }}
PRINT_UNRELEASED: ${{ inputs.print-unreleased }}
PRINT_RELEASE_NOTES: ${{ inputs.print-release-notes }}
VOCIFERATE_REPOSITORY_URL: ${{ vars.VOCIFERATE_REPOSITORY_URL }}
run: |
set -euo pipefail
command=("$VOCIFERATE_BIN" --root "$ROOT")
if [[ -n "$VERSION_FILE" ]]; then
command+=(--version-file "$VERSION_FILE")
fi
if [[ -n "$VERSION_PATTERN" ]]; then
command+=(--version-pattern "$VERSION_PATTERN")
fi
if [[ -n "$CHANGELOG" ]]; then
command+=(--changelog "$CHANGELOG")
fi
if [[ -n "$VERSION" ]]; then
command+=(--version "$VERSION")
fi
if [[ -n "$DATE" ]]; then
command+=(--date "$DATE")
fi
if [[ "$RECOMMEND" == 'true' ]]; then
command+=(--recommend)
fi
if [[ "$PRINT_UNRELEASED" == 'true' ]]; then
command+=(--print-unreleased)
fi
if [[ "$PRINT_RELEASE_NOTES" == 'true' ]]; then
command+=(--print-release-notes)
fi
stdout_file="$(mktemp)"
trap 'rm -f "$stdout_file"' EXIT
"${command[@]}" > "$stdout_file"
delimiter="EOF_STDOUT"
printf 'stdout<<%s\n' "$delimiter" >> "$GITHUB_OUTPUT"
cat "$stdout_file" >> "$GITHUB_OUTPUT"
printf '%s\n' "$delimiter" >> "$GITHUB_OUTPUT"

View File

@@ -0,0 +1,125 @@
name: vociferate/run-vociferate-code
description: Execute vociferate from the checked-out Go source.
inputs:
root:
description: Repository root to pass to vociferate.
required: true
version-file:
description: Optional version file path.
required: false
default: ''
version-pattern:
description: Optional version pattern.
required: false
default: ''
changelog:
description: Optional changelog path.
required: false
default: ''
version:
description: Optional version argument.
required: false
default: ''
date:
description: Optional date argument.
required: false
default: ''
recommend:
description: Whether to run vociferate with --recommend.
required: false
default: 'false'
print-unreleased:
description: Whether to print the Unreleased body.
required: false
default: 'false'
print-release-notes:
description: Whether to print the release notes section for version.
required: false
default: 'false'
outputs:
stdout:
description: Captured stdout from the vociferate invocation.
value: ${{ steps.run.outputs.stdout }}
runs:
using: composite
steps:
- name: Resolve source root
id: resolve-source
shell: bash
run: |
set -euo pipefail
source_root="$GITHUB_ACTION_PATH"
while [[ ! -f "$source_root/go.mod" ]] && [[ "$source_root" != "/" ]]; do
source_root="$(realpath "$source_root/..")"
done
if [[ ! -f "$source_root/go.mod" ]]; then
echo "Could not locate Go module root from $GITHUB_ACTION_PATH" >&2
exit 1
fi
printf 'source_root=%s\n' "$source_root" >> "$GITHUB_OUTPUT"
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.26.1'
cache: true
cache-dependency-path: ${{ steps.resolve-source.outputs.source_root }}/go.sum
- name: Run source
id: run
shell: bash
working-directory: ${{ steps.resolve-source.outputs.source_root }}
env:
ROOT: ${{ inputs.root }}
VERSION_FILE: ${{ inputs.version-file }}
VERSION_PATTERN: ${{ inputs.version-pattern }}
CHANGELOG: ${{ inputs.changelog }}
VERSION: ${{ inputs.version }}
DATE: ${{ inputs.date }}
RECOMMEND: ${{ inputs.recommend }}
PRINT_UNRELEASED: ${{ inputs.print-unreleased }}
PRINT_RELEASE_NOTES: ${{ inputs.print-release-notes }}
VOCIFERATE_REPOSITORY_URL: ${{ vars.VOCIFERATE_REPOSITORY_URL }}
run: |
set -euo pipefail
command=(go run ./cmd/vociferate --root "$ROOT")
if [[ -n "$VERSION_FILE" ]]; then
command+=(--version-file "$VERSION_FILE")
fi
if [[ -n "$VERSION_PATTERN" ]]; then
command+=(--version-pattern "$VERSION_PATTERN")
fi
if [[ -n "$CHANGELOG" ]]; then
command+=(--changelog "$CHANGELOG")
fi
if [[ -n "$VERSION" ]]; then
command+=(--version "$VERSION")
fi
if [[ -n "$DATE" ]]; then
command+=(--date "$DATE")
fi
if [[ "$RECOMMEND" == 'true' ]]; then
command+=(--recommend)
fi
if [[ "$PRINT_UNRELEASED" == 'true' ]]; then
command+=(--print-unreleased)
fi
if [[ "$PRINT_RELEASE_NOTES" == 'true' ]]; then
command+=(--print-release-notes)
fi
stdout_file="$(mktemp)"
trap 'rm -f "$stdout_file"' EXIT
"${command[@]}" > "$stdout_file"
delimiter="EOF_STDOUT"
printf 'stdout<<%s\n' "$delimiter" >> "$GITHUB_OUTPUT"
cat "$stdout_file" >> "$GITHUB_OUTPUT"
printf '%s\n' "$delimiter" >> "$GITHUB_OUTPUT"