YAML 131 lines
# Release: on `v*` tag push, cross-build the six-platform matrix,
# generate the package-manager manifests, sign the checksums (keyless),
# attest build provenance, and publish everything to the GitHub Release
# for that tag. Operator tags + pushes; CI never tags or pushes on its
# own. Keyless signing uses the workflow's OIDC identity — no secrets.
name: release
on:
push:
tags: ['v*']
concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false
# attest-build-provenance@v2 still bundles Node-20 sub-actions; opt them
# into Node-24 now (forced runner default from 2026-06-02 regardless).
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
permissions:
contents: write # create the release + upload assets
id-token: write # keyless cosign + provenance OIDC
attestations: write # build-provenance attestation
jobs:
publish:
name: build + sign + publish
runs-on: ubuntu-latest
steps:
- name: checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: setup-go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true
- name: derive build metadata
id: meta
run: |
TAG="${GITHUB_REF_NAME}"
BUILD_DATE="$(git log -1 --format=%cI "refs/tags/${TAG}")"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "build_date=${BUILD_DATE}" >> "$GITHUB_OUTPUT"
case "${TAG}" in
*-*) echo "prerelease=true" >> "$GITHUB_OUTPUT" ;;
*) echo "prerelease=false" >> "$GITHUB_OUTPUT" ;;
esac
- name: cross-build matrix
env:
VERSION: ${{ steps.meta.outputs.tag }}
BUILD_DATE: ${{ steps.meta.outputs.build_date }}
run: make release
- name: generate package manifests
env:
VERSION: ${{ steps.meta.outputs.tag }}
run: make packaging
- name: install cosign
uses: sigstore/cosign-installer@v3
- name: sign checksums (keyless)
run: |
cosign sign-blob --yes \
--output-signature dist/SHA256SUMS.sig \
--output-certificate dist/SHA256SUMS.pem \
dist/SHA256SUMS
- name: attest build provenance
uses: actions/attest-build-provenance@v2
with:
subject-path: 'dist/eeco_*.tar.gz, dist/eeco_*.zip'
- name: write release notes
run: |
cat > release-notes.md <<EOF
eeco ${{ steps.meta.outputs.tag }}
Pre-built binaries for darwin/linux/windows (amd64 + arm64),
SHA256SUMS, a keyless cosign signature, and the package
manifests (eeco.rb, eeco.json).
Verify the checksums signature (no key needed):
cosign verify-blob \\
--certificate dist/SHA256SUMS.pem \\
--certificate-identity-regexp \\
'^https://github.com/ajhahnde/eeco/\.github/workflows/release\.yml@refs/tags/v' \\
--certificate-oidc-issuer https://token.actions.githubusercontent.com \\
--signature dist/SHA256SUMS.sig \\
SHA256SUMS
Build provenance is attested; verify a downloaded archive with
'gh attestation verify <archive> --repo ajhahnde/eeco'.
Install routes and checksum verification: docs/USAGE.md §1.
EOF
- name: publish
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG="${{ steps.meta.outputs.tag }}"
ASSETS="dist/eeco_*.tar.gz dist/eeco_*.zip dist/SHA256SUMS \
dist/SHA256SUMS.sig dist/SHA256SUMS.pem dist/eeco.rb dist/eeco.json \
dist/eeco.1"
PRE_FLAG=""
if [ "${{ steps.meta.outputs.prerelease }}" = "true" ]; then
PRE_FLAG="--prerelease"
fi
if gh release view "${TAG}" >/dev/null 2>&1; then
gh release upload "${TAG}" ${ASSETS} --clobber
else
gh release create "${TAG}" ${ASSETS} \
--title "${TAG}" \
--notes-file release-notes.md \
${PRE_FLAG}
fi
- name: sync package-manager taps
# Stable releases only; taps never carry a prerelease. Skips
# cleanly when no PAT is configured, so the release still
# succeeds before the tap repos / token exist (Phase 1 setup).
if: steps.meta.outputs.prerelease == 'false'
env:
VERSION: ${{ steps.meta.outputs.tag }}
TAP_PUSH_TOKEN: ${{ secrets.TAP_PUSH_TOKEN }}
run: |
if [ -z "${TAP_PUSH_TOKEN}" ]; then
echo "TAP_PUSH_TOKEN unset; skipping tap sync" >&2
exit 0
fi
./scripts/sync-taps.sh