diff --git a/.gitea/workflows/prepare-release.yml b/.gitea/workflows/release.yml similarity index 95% rename from .gitea/workflows/prepare-release.yml rename to .gitea/workflows/release.yml index e9efd29..f51fe73 100644 --- a/.gitea/workflows/prepare-release.yml +++ b/.gitea/workflows/release.yml @@ -1,4 +1,4 @@ -name: Prepare Release +name: Release on: workflow_dispatch: @@ -24,7 +24,7 @@ jobs: run: shell: bash env: - SUMMARY_FILE: ${{ runner.temp }}/prepare-release-summary.md + SUMMARY_FILE: ${{ runner.temp }}/release-prepare-summary.md steps: - name: Checkout uses: actions/checkout@v4 @@ -39,6 +39,12 @@ jobs: cache: true cache-dependency-path: go.sum + - name: Install release build tools + run: | + set -euo pipefail + apt-get update + apt-get install -y upx-ucl || apt-get install -y upx + - name: Validate formatting run: test -z "$(gofmt -l .)" @@ -242,6 +248,15 @@ jobs: run: | set -euo pipefail + if command -v upx >/dev/null 2>&1; then + upx_cmd=upx + elif command -v upx-ucl >/dev/null 2>&1; then + upx_cmd=upx-ucl + else + echo "UPX is not available on PATH after installation." >&2 + exit 1 + fi + mkdir -p dist for target in linux/amd64 linux/arm64; do @@ -250,6 +265,7 @@ jobs: bin="vociferate_${RELEASE_VERSION}_${os}_${arch}" GOOS="$os" GOARCH="$arch" go build -trimpath -ldflags="-s -w" -o "dist/${bin}" ./cmd/vociferate + "${upx_cmd}" --best --lzma "dist/${bin}" done ( @@ -329,6 +345,7 @@ jobs: 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" + echo "- Release binaries were compressed with UPX before upload." } >> "$SUMMARY_FILE" else { diff --git a/.gitea/workflows/do-release.yml b/.gitea/workflows/update-release.yml similarity index 81% rename from .gitea/workflows/do-release.yml rename to .gitea/workflows/update-release.yml index 9a9c8e4..393a66d 100644 --- a/.gitea/workflows/do-release.yml +++ b/.gitea/workflows/update-release.yml @@ -1,4 +1,4 @@ -name: Do Release +name: Update Release on: push: @@ -29,7 +29,7 @@ jobs: shell: bash env: RELEASE_TOKEN: ${{ secrets.RELEASE_PAT }} - SUMMARY_FILE: ${{ runner.temp }}/do-release-summary.md + SUMMARY_FILE: ${{ runner.temp }}/update-release-summary.md steps: - name: Checkout uses: actions/checkout@v4 @@ -59,7 +59,6 @@ jobs: 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 @@ -79,16 +78,10 @@ jobs: } 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 @@ -107,7 +100,6 @@ jobs: 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 @@ -130,6 +122,12 @@ jobs: cache: true cache-dependency-path: go.sum + - name: Install release build tools + run: | + set -euo pipefail + apt-get update + apt-get install -y upx-ucl || apt-get install -y upx + - name: Preflight release API access env: TAG_NAME: ${{ steps.resolve-version.outputs.tag }} @@ -172,6 +170,15 @@ jobs: run: | set -euo pipefail + if command -v upx >/dev/null 2>&1; then + upx_cmd=upx + elif command -v upx-ucl >/dev/null 2>&1; then + upx_cmd=upx-ucl + else + echo "UPX is not available on PATH after installation." >&2 + exit 1 + fi + mkdir -p dist for target in linux/amd64 linux/arm64; do @@ -180,6 +187,7 @@ jobs: bin="vociferate_${RELEASE_VERSION}_${os}_${arch}" GOOS="$os" GOARCH="$arch" go build -trimpath -ldflags="-s -w" -o "dist/${bin}" ./cmd/vociferate + "${upx_cmd}" --best --lzma "dist/${bin}" done ( @@ -194,7 +202,27 @@ jobs: run: | set -euo pipefail - release_api="${GITHUB_API_URL:-${GITHUB_SERVER_URL%/}/api/v1}/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets" + 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")" @@ -223,25 +251,32 @@ jobs: --data-binary "@${asset}" done - - name: Summarize published release + - 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 - { - 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" - - - name: Summary - if: ${{ always() }} - 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" + echo "- Release binaries were compressed with UPX before upload." + } >> "$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 @@ -268,7 +303,7 @@ jobs: run: shell: bash env: - SUMMARY_FILE: ${{ runner.temp }}/do-release-validate-summary.md + SUMMARY_FILE: ${{ runner.temp }}/update-release-validate-summary.md steps: - name: Checkout tagged revision uses: actions/checkout@v4 diff --git a/AGENTS.md b/AGENTS.md index cfc35b6..6edbd72 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -30,8 +30,8 @@ Apply these checks before invoking actions: - Checkout repository first. - For prepare/publish flows that depend on tags/history, use full history checkout (`fetch-depth: 0`). -- Use `secrets.RELEASE_PAT` for release/tag/update operations (prepare/publish/do-release) so tag pushes trigger downstream workflows reliably. -- `do-release` and `decorate-pr` now run preflight API checks and fail fast when token credentials are missing or insufficient. +- Use `secrets.RELEASE_PAT` for release/tag/update operations (`prepare`, `publish`, `release`, `update-release`) so authenticated release changes can be pushed and published reliably. +- `release`, `update-release`, and `decorate-pr` run preflight API checks and fail fast when token credentials are missing or insufficient. - Set required vars/secrets for coverage uploads: - `vars.ARTEFACT_BUCKET_NAME` - `vars.ARTEFACT_BUCKET_ENDPONT` @@ -83,41 +83,26 @@ Minimal template: ## Minimal Integration Patterns -### 1. Prepare Then Publish - -```yaml -jobs: - prepare: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - id: prepare - uses: https://git.hrafn.xyz/aether/vociferate/prepare@v1.1.0 - - publish: - needs: prepare - uses: https://git.hrafn.xyz/aether/vociferate/.gitea/workflows/do-release.yml@v1.1.0 - with: - tag: ${{ needs.prepare.outputs.version }} - secrets: inherit -``` - -### 2. Publish Existing Tag +### 1. Full Release Workflow ```yaml jobs: release: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - id: publish - uses: https://git.hrafn.xyz/aether/vociferate/publish@v1.1.0 - with: - version: v1.2.3 + uses: https://git.hrafn.xyz/aether/vociferate/.gitea/workflows/release.yml@v1.1.0 + with: + version: ${{ inputs.version }} + secrets: inherit +``` + +### 2. Update Existing Release Tag + +```yaml +jobs: + release: + uses: https://git.hrafn.xyz/aether/vociferate/.gitea/workflows/update-release.yml@v1.1.0 + with: + tag: v1.2.3 + secrets: inherit ``` ### 3. Coverage Badge Publication diff --git a/CHANGELOG.md b/CHANGELOG.md index 897abfe..7295c76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ A `### Breaking` section is used in addition to Keep a Changelog's standard sect - 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. - Release automation now requires `secrets.RELEASE_PAT` for prepare/publish/do-release operations instead of defaulting to `GITHUB_TOKEN`/`GITEA_TOKEN`. +- Renamed the reusable Gitea workflows to `release.yml` and `update-release.yml`, and inlined release publication into the main `release` workflow for clearer per-step job output. +- Release binary builds now compress published linux artifacts with UPX before checksum generation and upload. ### Removed @@ -62,6 +64,7 @@ A `### Breaking` section is used in addition to Keep a Changelog's standard sect - 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. - 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. +- Synced `update-release.yml` with the active release pipeline fixes for Teacup-wrapped outputs, release-id normalization, upload endpoint validation, and accurate success or failure summaries. ## [1.0.2] - 2026-03-21 diff --git a/COMPLIANCE_ANALYSIS.md b/COMPLIANCE_ANALYSIS.md index aaf1b9e..aec97ac 100644 --- a/COMPLIANCE_ANALYSIS.md +++ b/COMPLIANCE_ANALYSIS.md @@ -154,8 +154,8 @@ if err != nil { **Workflows analyzed:** - [push-validation.yml](.gitea/workflows/push-validation.yml) -- [prepare-release.yml](.gitea/workflows/prepare-release.yml) -- [do-release.yml](.gitea/workflows/do-release.yml) +- [release.yml](.gitea/workflows/release.yml) +- [update-release.yml](.gitea/workflows/update-release.yml) #### What's Implemented @@ -171,7 +171,7 @@ if err != nil { - ✅ Coverage badge publication - ✅ Release tag recommendation on `main` branch -**prepare-release.yml:** +**release.yml:** - ✅ Go setup and caching - ✅ Tests run before release preparation @@ -361,7 +361,7 @@ validate **Effort Invested:** -- CI/CD improvements: workflow hardening in `push-validation.yml` and `prepare-release.yml` +- CI/CD improvements: workflow hardening in `push-validation.yml` and `release.yml` - Code organization: injected service boundaries for filesystem, environment, and git access - Local automation: `justfile` validation parity for format, modules, tests, and security - **Primary commits:** 7cb7b05, 383aad4, 5c903c9 diff --git a/README.md b/README.md index f33fbe7..0f98a6a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # 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) -[![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) -[![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) +[![Release](https://git.hrafn.xyz/aether/vociferate/actions/workflows/release.yml/badge.svg?event=workflow_dispatch)](https://git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=release.yml) +[![Update Release](https://git.hrafn.xyz/aether/vociferate/actions/workflows/update-release.yml/badge.svg?event=workflow_dispatch)](https://git.hrafn.xyz/aether/vociferate/actions/runs/latest?workflow=update-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) `vociferate` is an `Æther` release orchestration tool written in Go for repositories that @@ -24,7 +24,7 @@ For agentic coding partners, see [`AGENTS.md`](AGENTS.md) for a direct integrati ### `prepare` — update files, commit, and push tag ```yaml -name: Prepare Release +name: Release on: workflow_dispatch: @@ -47,7 +47,7 @@ jobs: publish: needs: prepare - uses: https://git.hrafn.xyz/aether/vociferate/.gitea/workflows/do-release.yml@v1.1.0 + uses: https://git.hrafn.xyz/aether/vociferate/.gitea/workflows/update-release.yml@v1.1.0 with: tag: ${{ needs.prepare.outputs.version }} secrets: inherit @@ -75,7 +75,7 @@ Pass `token: ${{ secrets.RELEASE_PAT }}` when invoking the action. ### `publish` — create release with changelog notes ```yaml -name: Do Release +name: Update Release on: workflow_dispatch: @@ -86,7 +86,7 @@ on: jobs: release: - uses: https://git.hrafn.xyz/aether/vociferate/.gitea/workflows/do-release.yml@v1.1.0 + uses: https://git.hrafn.xyz/aether/vociferate/.gitea/workflows/update-release.yml@v1.1.0 with: tag: ${{ inputs.tag }} secrets: inherit @@ -96,7 +96,7 @@ Reads the matching section from `CHANGELOG.md` and creates or updates the Gitea/GitHub release with those notes. The `version` input is optional — when omitted it is derived from the current tag ref automatically. -The reusable `Do Release` workflow now runs preflight checks before publish to +The reusable `Update Release` workflow now runs preflight checks before publish to fail fast when the release token is missing or lacks API access. Set `secrets.RELEASE_PAT` and use it for prepare/publish release operations.