From b1016a8204213ed73d0b5ec51b0e6889d93fba92 Mon Sep 17 00:00:00 2001 From: Nathan Nutter Date: Mon, 2 Nov 2026 16:13:08 -0600 Subject: [PATCH 1/4] Trigger the tap update from the version tag A release that GITHUB_TOKEN creates does not start a new workflow run, so the `release: published` trigger alone would never fire for a release the repository makes for itself. The tag is the event that always exists: a maintainer pushes it, and any other producer of the release pushes it too. Resolve the tag from the most specific event data first, then fall back to the pushed ref, so the release, manual, and tag triggers all keep working. Change-Id: I65cb73662f3c7849d320a7c9885dbfff8ef79774 --- .github/workflows/update-tap.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-tap.yaml b/.github/workflows/update-tap.yaml index 6e27ec9..1c3320f 100644 --- a/.github/workflows/update-tap.yaml +++ b/.github/workflows/update-tap.yaml @@ -1,5 +1,7 @@ name: Update Tap on: + push: + tags: ["v*"] release: types: [published] workflow_dispatch: @@ -26,8 +28,18 @@ jobs: env: EVENT_TAG: ${{ github.event.release.tag_name }} INPUT_TAG: ${{ inputs.tag }} + REF_TAG: ${{ github.ref_name }} + REF_TYPE: ${{ github.ref_type }} run: | - tag="${EVENT_TAG:-$INPUT_TAG}" + if [[ -n "$EVENT_TAG" ]]; then + tag="$EVENT_TAG" + elif [[ -n "$INPUT_TAG" ]]; then + tag="$INPUT_TAG" + elif [[ "$REF_TYPE" == "tag" ]]; then + tag="$REF_TAG" + else + tag="" + fi if [[ -z "$tag" ]]; then echo "A release tag is required." >&2 exit 1 From 773ae509b051c3932ca03af488c7ff3140bda8dc Mon Sep 17 00:00:00 2001 From: Nathan Nutter Date: Mon, 2 Nov 2026 16:13:31 -0600 Subject: [PATCH 2/4] Release on a version tag with generated notes A release now makes itself: a maintainer pushes an annotated or lightweight version tag, and the workflow creates the GitHub release with generated notes. Before this, the release was a manual step in the GitHub UI, so the tag and its release could drift apart. Create the release only when it does not exist yet, so a re-run of the workflow does not fail on the tag that already has a release. Change-Id: Id820c7ca6ea029036a14dda06e258ad9470bd980 --- .github/workflows/release.yaml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..9c016d1 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,29 @@ +name: Release +on: + push: + tags: ["v*"] +permissions: + contents: write +concurrency: + group: release + cancel-in-progress: false +jobs: + release: + runs-on: ubuntu-26.04 + timeout-minutes: 10 + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.ref_name }} + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + - name: Create the release + run: | + if gh release view "$TAG" >/dev/null 2>&1; then + echo "The release $TAG already exists." + exit 0 + fi + gh release create "$TAG" --verify-tag --generate-notes --title "$TAG" From 996997802a83c241a667a20dd0783409e0695801 Mon Sep 17 00:00:00 2001 From: Nathan Nutter Date: Mon, 2 Nov 2026 16:15:31 -0600 Subject: [PATCH 3/4] Attach binaries for four platforms to the release A release with no binaries is not a release that a maintainer can hand to a user who does not build from source. Build Linux and macOS binaries for arm64 and amd64, then attach them with checksums. The assets go to a draft first, so a release that still lacks an asset does not appear in the public list while the upload runs. The publish job has no checkout, so it sets GH_REPO: gh reads the repository from the environment, and a job without a work tree has nothing else to read. A re-run must repair a release that a failed upload left behind, so the upload step has no condition and overwrites its assets. The create and the publish steps read the release state first, so a re-run does not publish twice and does not touch a version that is already public. Change-Id: I81af424ff328bbe5a33ab29fa976485cdf40671a --- .github/workflows/release.yaml | 72 ++++++++++++++++++++++++++++++---- 1 file changed, 64 insertions(+), 8 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9c016d1..3be49ca 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,11 +8,23 @@ concurrency: group: release cancel-in-progress: false jobs: - release: + build: runs-on: ubuntu-26.04 - timeout-minutes: 10 + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + platform: + - os: linux + arch: arm64 + - os: linux + arch: amd64 + - os: darwin + arch: arm64 + - os: darwin + arch: amd64 env: - GH_TOKEN: ${{ github.token }} + BINARY: constable TAG: ${{ github.ref_name }} steps: - name: Check out repository @@ -20,10 +32,54 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - name: Create the release + - name: Install mise + uses: jdx/mise-action@6612f0ef07ed2a736cd1e00a94a065522dd41195 # v5.2.5 + with: + version: 2026.10.25 + - name: Build + env: + GOOS: ${{ matrix.platform.os }} + GOARCH: ${{ matrix.platform.arch }} + run: | + name="${BINARY}_${TAG}_${GOOS}_${GOARCH}" + CGO_ENABLED=0 go build -trimpath -ldflags "-s -w -X main.version=${TAG}" -o "$name" ./cmd/constable + tar -czf "${name}.tar.gz" "$name" + - name: Upload + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 + with: + name: ${{ matrix.platform.os }}-${{ matrix.platform.arch }} + path: ${{ env.BINARY }}_${{ env.TAG }}_${{ matrix.platform.os }}_${{ matrix.platform.arch }}.tar.gz + if-no-files-found: error + publish: + needs: build + runs-on: ubuntu-26.04 + timeout-minutes: 10 + env: + GH_TOKEN: ${{ github.token }} + GH_REPO: ${{ github.repository }} + TAG: ${{ github.ref_name }} + steps: + - name: Download the artifacts + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + path: dist + merge-multiple: true + - name: Make the checksums + run: cd dist && sha256sum -- *.tar.gz > checksums.txt + - name: Read the release state + id: state run: | - if gh release view "$TAG" >/dev/null 2>&1; then - echo "The release $TAG already exists." - exit 0 + if state="$(gh release view "$TAG" --json isDraft --jq '.isDraft' 2>/dev/null)"; then + if [[ "$state" == "true" ]]; then state=draft; else state=published; fi + else + state=none fi - gh release create "$TAG" --verify-tag --generate-notes --title "$TAG" + echo "state=$state" >> "$GITHUB_OUTPUT" + - name: Create a draft release + if: steps.state.outputs.state == 'none' + run: gh release create "$TAG" --verify-tag --draft --generate-notes --title "$TAG" + - name: Upload the assets + run: gh release upload "$TAG" dist/* --clobber + - name: Publish the release + if: steps.state.outputs.state != 'published' + run: gh release edit "$TAG" --draft=false --latest From 8cee5728d93e70d11be8254abf607f0aaced3d1d Mon Sep 17 00:00:00 2001 From: Nathan Nutter Date: Mon, 2 Nov 2026 17:35:40 -0600 Subject: [PATCH 4/4] Upgrade the locked timber tool to 0.22.0 The worktree tooling asks for `latest`, but `mise.lock` still held 0.21.0, so every run used the older release. The lock now records the current release. Change-Id: If0ce9f775764c099ed435bfbbadca0c15b9ea301 --- mise.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mise.lock b/mise.lock index 486d357..15a8490 100644 --- a/mise.lock +++ b/mise.lock @@ -145,7 +145,7 @@ backend = "go:github.com/nnutter/testifylint" specifiers = ["latest"] [[tools."go:github.com/nnutter/timber"]] -version = "0.21.0" +version = "0.22.0" backend = "go:github.com/nnutter/timber" specifiers = ["latest"]