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