Repository navigation
changelog: condense the v3.3.0 section #10
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| # Tag-triggered. The signing key + S3 creds are only exposed here (tags on the | |
| # base repo), never on PRs from forks. | |
| on: | |
| push: | |
| tags: ["v*"] | |
| permissions: | |
| contents: write # create the GitHub Release + upload assets | |
| # The apt repo is stateful (index rebuilt from the full pool); never publish two | |
| # releases concurrently. | |
| concurrency: | |
| group: apt-publish | |
| cancel-in-progress: false | |
| jobs: | |
| release: | |
| runs-on: ubuntu-latest | |
| # Backstop, not the primary guard (apt-publish deadlines each download). A hung | |
| # job holds the shared apt-publish concurrency group, so it would also block the | |
| # reconcile that heals a half-finished publish. | |
| timeout-minutes: 30 | |
| # Gate secrets behind a protected Environment (add required reviewers in repo settings). | |
| environment: release | |
| env: | |
| APT_S3_ENDPOINT: ${{ vars.APT_S3_ENDPOINT }} | |
| APT_S3_REGION: ${{ vars.APT_S3_REGION }} | |
| APT_S3_BUCKET: ${{ vars.APT_S3_BUCKET }} | |
| APT_S3_PREFIX: ${{ vars.APT_S3_PREFIX }} | |
| AWS_ACCESS_KEY_ID: ${{ secrets.APT_S3_ACCESS_KEY }} | |
| AWS_SECRET_ACCESS_KEY: ${{ secrets.APT_S3_SECRET_KEY }} | |
| steps: | |
| - uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 # full history + tags for versioning | |
| - uses: actions/setup-go@v6 | |
| with: | |
| go-version: "1.24" | |
| # apt-ftparchive ships in apt-utils, which the runner image already carries ("apt-utils | |
| # is already the newest version" in every successful run), so the install is a fallback | |
| # for an image that ever drops it and the apt-get update behind it does not run at all | |
| # in the normal case. It was not a free no-op: on 2026-08-19 the Ubuntu mirror hung | |
| # mid-fetch with no timeout of its own and the step sat silent for 29 minutes, until the | |
| # job timeout killed a reconcile that had not yet reached the pool. A stall here is | |
| # expensive out of proportion to the step, because the job holds the shared apt-publish | |
| # concurrency group while it burns: releases and every later reconcile queue behind it. | |
| # The step timeout bounds the fallback so a bad mirror fails fast and names itself. | |
| - name: Ensure apt-ftparchive is available | |
| timeout-minutes: 3 | |
| run: | | |
| if ! command -v apt-ftparchive >/dev/null; then | |
| sudo apt-get update | |
| sudo apt-get install -y apt-utils | |
| fi | |
| - name: Import GPG signing key | |
| id: gpg | |
| uses: crazy-max/ghaction-import-gpg@v7 | |
| with: | |
| gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} | |
| passphrase: ${{ secrets.GPG_PASSPHRASE }} | |
| - name: Extract release notes from CHANGELOG.md | |
| run: ./scripts/changelog-extract.sh "${GITHUB_REF_NAME}" | tee /tmp/notes.md | |
| - name: Build, package .deb, and cut the GitHub Release | |
| uses: goreleaser/goreleaser-action@v7 | |
| with: | |
| version: "~> v2" | |
| args: release --clean --release-notes=/tmp/notes.md | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # GoReleaser's --release-notes did not reliably populate the release body (v2.0.0-rc1 | |
| # shipped empty), so set it explicitly from the extracted CHANGELOG section. gh is | |
| # preinstalled on ubuntu-latest and GITHUB_TOKEN has contents:write. | |
| - name: Set GitHub Release notes from CHANGELOG | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: gh release edit "${GITHUB_REF_NAME}" --repo "${{ github.repository }}" --notes-file /tmp/notes.md | |
| # A semver prerelease tag (e.g. v2.0.0-rc1) MUST become a deb version with '~' | |
| # (2.0.0~rc1) so it sorts BELOW the final 2.0.0. A '2.0.0-rc1' deb sorts ABOVE | |
| # 2.0.0 (deb revision ordering), so the final would never supersede the rc. | |
| # nfpm's default semver schema does this; this step fails loudly if it ever stops. | |
| - name: Guard — prerelease debs must use '~' | |
| run: | | |
| if [[ "${GITHUB_REF_NAME}" == *-* ]]; then | |
| for deb in dist/*.deb; do | |
| ver="$(dpkg-deb -f "$deb" Version)" | |
| echo "$deb -> $ver" | |
| case "$ver" in | |
| *'~'*) ;; | |
| *) echo "::error::prerelease tag ${GITHUB_REF_NAME} produced deb version '$ver' without '~' (would sort above the final release)"; exit 1 ;; | |
| esac | |
| done | |
| fi | |
| # --- shared apt repo: Approach A (append-only pool + index rebuilt from the full pool) --- | |
| # cs-agent is the SOLE publisher of the shared index today, so this is race-free. When a | |
| # 2nd package's CI ships, move index rebuild+sign into a single index-builder (see | |
| # packaging/README.md "Scaling to multiple packages"); reconcile-apt-repo.yml already | |
| # rebuilds the shared index from the pool on a timer, on this same concurrency group. | |
| - name: Pull existing pool from S3 | |
| run: | | |
| mkdir -p aptrepo | |
| go run ./cmd/apt-publish pull aptrepo | |
| - name: Add new .debs to the pool | |
| run: | | |
| mkdir -p aptrepo/pool/main/c/cs-agent | |
| cp dist/*.deb aptrepo/pool/main/c/cs-agent/ | |
| - name: Rebuild + sign the apt index | |
| env: | |
| APT_GPG_KEY_ID: ${{ steps.gpg.outputs.fingerprint }} | |
| run: ./scripts/build-apt-repo.sh aptrepo | |
| - name: Publish apt repo to S3 (Release files last) | |
| run: go run ./cmd/apt-publish push aptrepo |