The release body is the tag's own annotation, but the runner's checkout only had the commit — no tag object — so `git cat-file -t` failed and every release silently fell back to "fix some bugs" (v1.8.0 included). The publish job now fetches tags, and the notes step fetches the tag ref explicitly before reading it, so the annotation always makes it into the release body. Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
129 lines
3.9 KiB
YAML
129 lines
3.9 KiB
YAML
name: release
|
|
|
|
on:
|
|
push:
|
|
tags:
|
|
- 'v*'
|
|
|
|
permissions:
|
|
contents: write
|
|
id-token: write
|
|
attestations: write
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
arch: [amd64, arm64]
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version-file: src/go.mod
|
|
cache: true
|
|
|
|
- name: Build (${{ matrix.arch }})
|
|
run: |
|
|
VERSION="${GITHUB_REF_NAME#v}"
|
|
cd src
|
|
CGO_ENABLED=0 GOOS=linux GOARCH=${{ matrix.arch }} go build \
|
|
-trimpath -buildvcs=false \
|
|
-ldflags="-s -w -X vpsmgr/internal/ver.Version=${VERSION}" \
|
|
-o "../vps-${{ matrix.arch }}" .
|
|
|
|
- name: Verify artifact
|
|
run: |
|
|
file vps-${{ matrix.arch }}
|
|
if [ "${{ matrix.arch }}" = "amd64" ]; then
|
|
./vps-${{ matrix.arch }} version
|
|
fi
|
|
|
|
# The project is pure Go (no cgo); keep tests C-free too so they never
|
|
# depend on a working C toolchain on the runner.
|
|
- name: Vet + test (CGO_ENABLED=0, amd64)
|
|
if: matrix.arch == 'amd64'
|
|
run: |
|
|
cd src
|
|
CGO_ENABLED=0 go vet ./...
|
|
CGO_ENABLED=0 go test ./...
|
|
|
|
- name: Attest build provenance
|
|
uses: actions/attest@v4
|
|
with:
|
|
subject-path: vps-${{ matrix.arch }}
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: vps-${{ matrix.arch }}
|
|
path: vps-${{ matrix.arch }}
|
|
if-no-files-found: error
|
|
|
|
publish:
|
|
needs: build
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
# The release notes come from the tag OBJECT, so its annotation has to be
|
|
# local: `fetch-tags` brings it, and the notes step fetches the tag ref
|
|
# explicitly as well (a checkout that only fetched the commit leaves no
|
|
# tag behind, which is how a tag message silently becomes the fallback).
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
with:
|
|
fetch-depth: 0
|
|
fetch-tags: true
|
|
|
|
- name: Download artifacts
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
path: dist
|
|
merge-multiple: true
|
|
|
|
- name: Generate checksums
|
|
run: |
|
|
cd dist
|
|
sha256sum vps-amd64 vps-arm64 | tee SHA256SUMS
|
|
|
|
# The release body is the tag's own annotation: write the notes at tag
|
|
# time with
|
|
# git tag -a v1.2.3 -m "new feature X
|
|
#
|
|
# - bullet"
|
|
# A lightweight tag (`git tag v1.2.3`) carries no message, so the release
|
|
# falls back to "fix some bugs". GitHub appends the auto-generated
|
|
# "Full changelog" compare link to whatever body we pass.
|
|
- name: Build release notes
|
|
run: |
|
|
TAG="${GITHUB_REF_NAME}"
|
|
# The tag's annotation IS the release body, so make sure the tag object
|
|
# (not just the commit) is local; without this the notes silently fall
|
|
# back to "fix some bugs".
|
|
git fetch --force --no-tags origin "+refs/tags/$TAG:refs/tags/$TAG" || true
|
|
notes=""
|
|
if [ "$(git cat-file -t "$TAG" 2>/dev/null)" = "tag" ]; then
|
|
notes="$(git for-each-ref "refs/tags/$TAG" --format='%(contents)')"
|
|
# A signed tag's message carries a PGP block; drop it.
|
|
notes="${notes%%-----BEGIN PGP SIGNATURE-----*}"
|
|
fi
|
|
if [ -z "$(printf '%s' "$notes" | tr -d '[:space:]')" ]; then
|
|
notes="fix some bugs"
|
|
fi
|
|
printf '%s\n' "$notes" > release-notes.md
|
|
echo "----- release notes -----"
|
|
cat release-notes.md
|
|
|
|
- name: Release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
body_path: release-notes.md
|
|
files: |
|
|
dist/vps-amd64
|
|
dist/vps-arm64
|
|
dist/SHA256SUMS
|
|
generate_release_notes: true
|