From 9bafeabee55d101b461290de2893f14926d7d49a Mon Sep 17 00:00:00 2001 From: mervin008 Date: Sat, 10 Oct 2026 19:35:20 +0200 Subject: [PATCH] Publish binaries for every platform with each release Hosts with no Docker and no Go had no way to install the agent. Each release now carries archives for Linux, Windows, macOS and FreeBSD with a SHA256SUMS file. CI builds the same set on every pull request. --- .github/workflows/ci.yml | 14 ++++++++ .github/workflows/release.yml | 38 +++++++++++++++++++++ README.md | 4 +++ scripts/build-release.sh | 62 +++++++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) create mode 100755 scripts/build-release.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0df3f29..5c8d311 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,20 @@ jobs: make tidy git diff --exit-code -- go.mod go.sum + platforms: + name: builds for every platform + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + # The same script the release runs, so a tag is never the first time a + # target is built. + - run: ./scripts/build-release.sh dev dist + - name: the checksums match the files + run: cd dist && sha256sum -c SHA256SUMS + race: name: race detector runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e30b56..c32a94c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -127,3 +127,41 @@ jobs: fi gh release create "$TAG" --verify-tag --title "$TAG" "${notes[@]}" "${flags[@]}" + + binaries: + name: publish binaries + # Archives for every platform, for hosts with no Docker and no Go: Windows + # servers, 32-bit ARM, FreeBSD. After the release page exists, because they + # are attached to it. + needs: release + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Build every platform + env: + TAG: ${{ github.ref_name }} + run: ./scripts/build-release.sh "${TAG#v}" dist + + # A binary that does not report the tag was not stamped with it, and + # would be published as a release it is not. + - name: The Linux build reports this release + env: + TAG: ${{ github.ref_name }} + run: | + tar -xzf "dist/certpilot-agent_${TAG#v}_linux_amd64.tar.gz" -C "$RUNNER_TEMP" + got=$("$RUNNER_TEMP/certpilot-agent_${TAG#v}_linux_amd64/certpilot-agent" version) + [ "$got" = "${TAG#v}" ] || { echo "binary says $got, tag is $TAG"; exit 1; } + + - name: Attach them to the release + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.ref_name }} + # --clobber so re-running a tag's workflow replaces its files. + run: gh release upload "$TAG" dist/* --clobber diff --git a/README.md b/README.md index 86f66e5..e9dfad2 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,10 @@ the key does not, and there is no code path that would let it. go install github.com/certpilot/certpilot-agent/cmd@latest ``` +No Go or Docker on the host? Each [release](https://github.com/certpilot/certpilot-agent/releases) +has a static binary for Linux (amd64, arm64, armv7, 386), Windows (amd64, arm64), +macOS and FreeBSD, with a `SHA256SUMS` file to check it against. + ``` docker run --rm -v certpilot-agent:/var/lib/certpilot-agent \ ghcr.io/certpilot/agent:latest --help diff --git a/scripts/build-release.sh b/scripts/build-release.sh new file mode 100755 index 0000000..8358983 --- /dev/null +++ b/scripts/build-release.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# +# Build the agent for every platform it is released for. +# +# ./scripts/build-release.sh 0.2.4 dist stamp 0.2.4, write archives to dist/ +# ./scripts/build-release.sh dev dist unstamped, as CI builds it +# +# One script for CI and for the release, so a tag is never the first time a +# target is built. Each target is a static binary (CGO off) in an archive with +# the licence and README, and SHA256SUMS lists them all. +# +set -euo pipefail + +cd "$(dirname "$0")/.." + +version="${1:?usage: build-release.sh VERSION OUTDIR}" +out="${2:?usage: build-release.sh VERSION OUTDIR}" + +# os/arch[/goarm] +targets=( + linux/amd64 linux/arm64 linux/arm/7 linux/386 + windows/amd64 windows/arm64 + darwin/amd64 darwin/arm64 + freebsd/amd64 +) + +# An unstamped build keeps the -dev default compiled into the source, rather +# than claiming a release number it is not. +ldflags="-s -w" +if [ "$version" != dev ]; then + ldflags="$ldflags -X github.com/certpilot/certpilot-agent.Version=$version" +fi + +rm -rf "$out" +mkdir -p "$out" +out="$(cd "$out" && pwd)" +work="$(mktemp -d)" +trap 'rm -rf "$work"' EXIT + +for target in "${targets[@]}"; do + IFS=/ read -r goos goarch goarm <<<"$target" + name="certpilot-agent_${version}_${goos}_${goarch}${goarm:+v$goarm}" + bin=certpilot-agent + [ "$goos" = windows ] && bin=certpilot-agent.exe + + dir="$work/$name" + mkdir -p "$dir" + CGO_ENABLED=0 GOOS="$goos" GOARCH="$goarch" GOARM="${goarm:-}" \ + go build -trimpath -ldflags="$ldflags" -o "$dir/$bin" ./cmd/ + cp LICENSE README.md "$dir/" + + if [ "$goos" = windows ]; then + (cd "$work" && zip -qr "$out/$name.zip" "$name") + else + tar -C "$work" -czf "$out/$name.tar.gz" "$name" + fi + echo "built $name" +done + +sum() { if command -v sha256sum >/dev/null; then sha256sum "$@"; else shasum -a 256 "$@"; fi; } +(cd "$out" && sum certpilot-agent_* > SHA256SUMS) +echo "wrote $(ls "$out" | wc -l | tr -d ' ') files to $out"