All checks were successful
Push Validation / validate (push) Successful in 54s
- Remove publish steps (release creation, binary build/upload) from the Prepare Release workflow; it now stops after committing and pushing the tag. - Add Do Release workflow triggered on v*.*.* tag pushes; reads release notes from the tagged changelog section, creates or updates the release, builds linux/amd64 and linux/arm64 binaries, uploads assets, then smoke-tests both binaries in a follow-on validate job. - Remove the standalone Action Validation workflow; binary validation now runs as a second job in Do Release after the release job succeeds, using the exact tag and version just published. - Update README to document the two-workflow release model and add split prepare/publish usage examples for both the composite action and the reusable workflows. - Update changelog unreleased section to reflect the new pipeline split and corrected artifact scope (linux/amd64 and linux/arm64 only).
302 lines
10 KiB
YAML
302 lines
10 KiB
YAML
name: Do Release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*.*.*'
|
|
workflow_dispatch:
|
|
inputs:
|
|
tag:
|
|
description: Semantic version tag to publish, with or without leading v. Defaults to the current tag ref when dispatched from a tag.
|
|
required: false
|
|
workflow_call:
|
|
inputs:
|
|
tag:
|
|
description: Semantic version tag to publish, with or without leading v. When omitted, the current tag ref is used.
|
|
required: false
|
|
default: ''
|
|
type: string
|
|
|
|
jobs:
|
|
release:
|
|
runs-on: ubuntu-latest
|
|
container: docker.io/catthehacker/ubuntu:act-latest
|
|
outputs:
|
|
tag: ${{ steps.resolve-tag.outputs.tag }}
|
|
version: ${{ steps.resolve-tag.outputs.version }}
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
env:
|
|
RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
steps:
|
|
- name: Checkout tagged revision
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
ref: ${{ github.ref }}
|
|
|
|
- name: Resolve release tag
|
|
id: resolve-tag
|
|
env:
|
|
INPUT_TAG: ${{ inputs.tag }}
|
|
GITHUB_REF_VALUE: ${{ github.ref }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
provided_tag="$(printf '%s' "${INPUT_TAG:-}" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
|
|
if [[ -n "$provided_tag" ]]; then
|
|
normalized_version="${provided_tag#v}"
|
|
tag="v${normalized_version}"
|
|
elif [[ "${GITHUB_REF_VALUE}" == refs/tags/* ]]; then
|
|
tag="${GITHUB_REF_VALUE#refs/tags/}"
|
|
normalized_version="${tag#v}"
|
|
else
|
|
echo "A tag input is required when the workflow is not running from a tag push" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "tag=$tag" >> "$GITHUB_OUTPUT"
|
|
echo "version=$normalized_version" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Checkout requested tag
|
|
if: ${{ inputs.tag != '' }}
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
ref: refs/tags/${{ steps.resolve-tag.outputs.tag }}
|
|
|
|
- name: Setup Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: '1.26.1'
|
|
check-latest: true
|
|
cache: true
|
|
cache-dependency-path: go.sum
|
|
|
|
- name: Extract release notes from changelog
|
|
id: release-notes
|
|
env:
|
|
RELEASE_VERSION: ${{ steps.resolve-tag.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
release_notes="$(awk -v version="$RELEASE_VERSION" '
|
|
$0 ~ "^## \\[" version "\\] - " {capture=1}
|
|
capture {
|
|
if ($0 ~ "^## \\[" && $0 !~ "^## \\[" version "\\] - ") exit
|
|
print
|
|
}
|
|
' changelog.md)"
|
|
|
|
if [[ -z "${release_notes//[[:space:]]/}" ]]; then
|
|
echo "Release notes section for ${RELEASE_VERSION} was not found in changelog.md" >&2
|
|
exit 1
|
|
fi
|
|
|
|
notes_file="$RUNNER_TEMP/release-notes.md"
|
|
printf '%s\n' "$release_notes" > "$notes_file"
|
|
|
|
echo "notes_file=$notes_file" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Create or update release
|
|
id: release
|
|
env:
|
|
TAG_NAME: ${{ steps.resolve-tag.outputs.tag }}
|
|
RELEASE_NOTES_FILE: ${{ steps.release-notes.outputs.notes_file }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
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')"
|
|
release_api="${GITHUB_API_URL:-${GITHUB_SERVER_URL%/}/api/v1}/repos/${GITHUB_REPOSITORY}/releases"
|
|
release_by_tag_api="${release_api}/tags/${TAG_NAME}"
|
|
|
|
status_code="$(curl -sS -o release-existing.json -w '%{http_code}' \
|
|
-H "Authorization: token ${RELEASE_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
"${release_by_tag_api}")"
|
|
|
|
if [[ "$status_code" == "200" ]]; then
|
|
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
|
|
echo "Failed to parse existing release id for ${TAG_NAME}" >&2
|
|
cat release-existing.json >&2
|
|
exit 1
|
|
fi
|
|
|
|
curl --fail-with-body \
|
|
-X PATCH \
|
|
-H "Authorization: token ${RELEASE_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
"${release_api}/${existing_release_id}" \
|
|
--data "{\"tag_name\":\"${TAG_NAME}\",\"target\":\"${GITHUB_SHA}\",\"name\":\"${TAG_NAME}\",\"body\":\"${escaped_release_notes}\",\"draft\":false,\"prerelease\":false}" \
|
|
--output release.json
|
|
elif [[ "$status_code" != "404" ]]; then
|
|
echo "Unexpected response while checking release ${TAG_NAME}: HTTP ${status_code}" >&2
|
|
cat release-existing.json >&2
|
|
exit 1
|
|
else
|
|
curl --fail-with-body \
|
|
-X POST \
|
|
-H "Authorization: token ${RELEASE_TOKEN}" \
|
|
-H "Content-Type: application/json" \
|
|
"${release_api}" \
|
|
--data "{\"tag_name\":\"${TAG_NAME}\",\"target\":\"${GITHUB_SHA}\",\"name\":\"${TAG_NAME}\",\"body\":\"${escaped_release_notes}\",\"draft\":false,\"prerelease\":false}" \
|
|
--output release.json
|
|
fi
|
|
|
|
release_id="$(sed -n 's/.*"id"[[:space:]]*:[[:space:]]*\([0-9][0-9]*\).*/\1/p' release.json | head -n 1)"
|
|
if [[ -z "$release_id" ]]; then
|
|
echo "Failed to parse release id from API response" >&2
|
|
cat release.json >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "id=$release_id" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Build release binaries
|
|
env:
|
|
RELEASE_VERSION: ${{ steps.resolve-tag.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.release.outputs.id }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
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: Summarize published release
|
|
env:
|
|
TAG_NAME: ${{ steps.resolve-tag.outputs.tag }}
|
|
RELEASE_VERSION: ${{ steps.resolve-tag.outputs.version }}
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
{
|
|
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"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|
|
|
|
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
|
|
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: ${{ github.token }}
|
|
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
|
|
|
|
recommended_tag="$(${RUN_COMMAND} --recommend --root .)"
|
|
case "$recommended_tag" in
|
|
v*.*.*)
|
|
;;
|
|
*)
|
|
echo "Unexpected recommended tag: $recommended_tag" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
{
|
|
echo "## Released Binary Validation (${{ matrix.asset_arch }})"
|
|
echo
|
|
echo "- Release tag: ${TAG_NAME}"
|
|
echo "- Asset: ${asset_name}"
|
|
echo "- Recommended tag: ${recommended_tag}"
|
|
} >> "$GITHUB_STEP_SUMMARY"
|