feat: add prepare and publish composite actions
Add two focused subdirectory composite actions: - prepare/action.yml: downloads the vociferate binary, runs it to update changelog and release-version, then commits, tags, and pushes — replacing the boilerplate git steps consumers previously had to write inline. - publish/action.yml: extracts the matching changelog section and creates or updates the Gitea/GitHub release. Outputs release-id, tag, and version so consumers can upload their own assets after it runs. Simplify the vociferate workflows to use ./prepare and ./publish directly, validating both actions in the self-release pipeline. Update README to show the clean two-action usage pattern.
This commit is contained in:
@@ -22,8 +22,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
container: docker.io/catthehacker/ubuntu:act-latest
|
||||
outputs:
|
||||
tag: ${{ steps.resolve-tag.outputs.tag }}
|
||||
version: ${{ steps.resolve-tag.outputs.version }}
|
||||
tag: ${{ steps.publish.outputs.tag }}
|
||||
version: ${{ steps.publish.outputs.version }}
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
@@ -36,35 +36,12 @@ jobs:
|
||||
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 }}
|
||||
ref: ${{ startsWith(inputs.tag, 'v') && format('refs/tags/{0}', inputs.tag) || format('refs/tags/v{0}', inputs.tag) }}
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
@@ -74,90 +51,16 @@ jobs:
|
||||
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"
|
||||
id: publish
|
||||
uses: ./publish
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: ${{ inputs.tag }}
|
||||
|
||||
- name: Build release binaries
|
||||
env:
|
||||
RELEASE_VERSION: ${{ steps.resolve-tag.outputs.version }}
|
||||
RELEASE_VERSION: ${{ steps.publish.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -178,7 +81,8 @@ jobs:
|
||||
|
||||
- name: Upload release binaries
|
||||
env:
|
||||
RELEASE_ID: ${{ steps.release.outputs.id }}
|
||||
RELEASE_ID: ${{ steps.publish.outputs.release-id }}
|
||||
RELEASE_VERSION: ${{ steps.publish.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
@@ -213,8 +117,8 @@ jobs:
|
||||
|
||||
- name: Summarize published release
|
||||
env:
|
||||
TAG_NAME: ${{ steps.resolve-tag.outputs.tag }}
|
||||
RELEASE_VERSION: ${{ steps.resolve-tag.outputs.version }}
|
||||
TAG_NAME: ${{ steps.publish.outputs.tag }}
|
||||
RELEASE_VERSION: ${{ steps.publish.outputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
@@ -21,8 +21,6 @@ jobs:
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -37,91 +35,20 @@ jobs:
|
||||
cache: true
|
||||
cache-dependency-path: go.sum
|
||||
|
||||
- name: Resolve release version
|
||||
id: resolve-version
|
||||
env:
|
||||
INPUT_VERSION: ${{ inputs.version }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
provided_version="$(printf '%s' "${INPUT_VERSION:-}" | sed 's/^[[:space:]]\+//; s/[[:space:]]\+$//')"
|
||||
|
||||
if [[ -n "$provided_version" ]]; then
|
||||
release_version="$provided_version"
|
||||
else
|
||||
if ! release_version="$(go run ./cmd/vociferate --recommend --root . 2>release-recommendation.err)"; then
|
||||
recommendation_error="$(tr '\n' ' ' < release-recommendation.err | sed 's/[[:space:]]\+/ /g' | sed 's/^ //; s/ $//')"
|
||||
echo "Resolve release version: ${recommendation_error}" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "RELEASE_VERSION=$release_version" >> "$GITHUB_ENV"
|
||||
echo "version=$release_version" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Prepare release files
|
||||
run: |
|
||||
set -euo pipefail
|
||||
go run ./cmd/vociferate \
|
||||
--root . \
|
||||
--version "$RELEASE_VERSION" \
|
||||
--date "$(date -u +%F)" \
|
||||
--changelog changelog.md
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
set -euo pipefail
|
||||
go test ./...
|
||||
run: go test ./...
|
||||
|
||||
- name: Configure git author
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git config user.name "gitea-actions[bot]"
|
||||
git config user.email "gitea-actions[bot]@users.noreply.local"
|
||||
|
||||
- name: Commit release changes and push tag
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
normalized_version="${RELEASE_VERSION#v}"
|
||||
tag="v${normalized_version}"
|
||||
|
||||
if git rev-parse "$tag" >/dev/null 2>&1; then
|
||||
echo "Tag ${tag} already exists" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$GITHUB_SERVER_URL" in
|
||||
https://*)
|
||||
authed_remote="https://oauth2:${RELEASE_TOKEN}@${GITHUB_SERVER_URL#https://}/${GITHUB_REPOSITORY}.git"
|
||||
;;
|
||||
http://*)
|
||||
authed_remote="http://oauth2:${RELEASE_TOKEN}@${GITHUB_SERVER_URL#http://}/${GITHUB_REPOSITORY}.git"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported GITHUB_SERVER_URL: ${GITHUB_SERVER_URL}" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
git remote set-url origin "$authed_remote"
|
||||
git add changelog.md release-version
|
||||
git commit -m "release: prepare ${tag}"
|
||||
git tag "$tag"
|
||||
git push origin HEAD
|
||||
git push origin "$tag"
|
||||
- name: Prepare and tag release
|
||||
id: prepare
|
||||
uses: ./prepare
|
||||
with:
|
||||
version: ${{ inputs.version }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Summarize prepared release
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
normalized_version="${RELEASE_VERSION#v}"
|
||||
tag="v${normalized_version}"
|
||||
|
||||
tag="${{ steps.prepare.outputs.version }}"
|
||||
{
|
||||
echo "## Release Prepared"
|
||||
echo
|
||||
echo "- Updated files were committed to main."
|
||||
echo "- Tag pushed: ${tag}"
|
||||
echo "- The tag-triggered Do Release workflow will create or update the release and publish binaries."
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
Reference in New Issue
Block a user