From 32ff16073a1613ce063ca4164242b19a363fc6d6 Mon Sep 17 00:00:00 2001 From: Phil Bjorge Date: Tue, 18 Aug 2026 13:35:26 -0700 Subject: [PATCH 1/3] fix(release): keep healthy targets when one runner fails The build matrix runs with the default fail-fast, so the first leg that fails cancels every other leg. In the v0.3.26 release run, the Windows and x86_64 Linux legs failed before executing a single step, the three remaining legs were cancelled, `upload` was skipped through `needs`, and the release published with no assets at all. Set fail-fast: false so an unavailable runner costs one target instead of all five, and let `upload` run whenever the matrix is not cancelled so a partial asset set still reaches the release. Guard the upload with an explicit check that at least one artifact arrived, so a fully failed matrix fails loudly here rather than silently publishing nothing. --- .github/workflows/release.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7bc5096..f568aed 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,6 +22,11 @@ jobs: permissions: contents: read strategy: + # One unavailable runner must not cancel the legs that are healthy. + # In the v0.3.26 run, two legs failed to acquire a runner before any + # step executed, fail-fast cancelled the three healthy legs, and the + # release shipped with no assets at all. + fail-fast: false matrix: include: - target: x86_64-pc-windows-msvc @@ -87,7 +92,9 @@ jobs: name: Upload Release Assets needs: build runs-on: ubuntu-latest - if: vars.CI_BUDGET_MODE != 'off' + # Ship whatever built. A partial asset set beats the empty asset set that + # `needs: build` produced whenever any single target failed. + if: ${{ !cancelled() && vars.CI_BUDGET_MODE != 'off' }} permissions: contents: write @@ -95,6 +102,13 @@ jobs: - name: Download all artifacts uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + - name: Verify at least one asset was built + run: | + count=$(find . -type f -name 'linear-cli-*' | wc -l | tr -d ' ') + echo "found $count asset(s)" + test "$count" -gt 0 + find . -type f -name 'linear-cli-*' -exec ls -l {} + + - name: Upload to release uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 with: From 9ca3067c1272cc7391c9c95bf89a80a72a0cb971 Mon Sep 17 00:00:00 2001 From: Phil Bjorge Date: Tue, 18 Aug 2026 13:35:41 -0700 Subject: [PATCH 2/3] fix(release): build and publish the tag the operator dispatched The workflow takes no input, so every job builds whatever ref the dispatch happened to use and `softprops/action-gh-release` falls back to github.ref for the target release. Dispatching from a branch therefore builds the branch and attaches assets to a ref that is not a release tag, and the crates.io publish step packages the branch rather than the tagged source. Add a required `tag` input, check it out in both the build matrix and the publish job, and pass it to the release action explicitly. --- .github/workflows/release.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f568aed..8d40c1d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,11 @@ name: Release on: workflow_dispatch: + inputs: + tag: + description: "Release tag to build and attach assets to (e.g. v0.3.28)" + required: true + type: string env: CARGO_TERM_COLOR: always @@ -48,6 +53,8 @@ jobs: steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ inputs.tag }} - name: Install Linux keyring build deps if: runner.os == 'Linux' && !matrix.use_cross @@ -112,6 +119,9 @@ jobs: - name: Upload to release uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 with: + # Without an explicit tag the action falls back to github.ref, which + # is a branch ref whenever the workflow is dispatched from a branch. + tag_name: ${{ inputs.tag }} files: | linear-cli-*/linear-cli-* @@ -125,6 +135,8 @@ jobs: steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + ref: ${{ inputs.tag }} - name: Install Rust uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable From fc74f157626b8edfb536b70c4dd3625585cf2710 Mon Sep 17 00:00:00 2001 From: Phil Bjorge Date: Tue, 18 Aug 2026 13:35:51 -0700 Subject: [PATCH 3/3] fix(release): run the release matrix on GitHub-hosted runners MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Windows and Linux legs request Blacksmith runners. In the v0.3.26 run neither could be acquired and both jobs failed without executing a step, which is what took the release down. PR Check uses the same Linux label successfully, so the label is live; the release matrix asks for three Blacksmith runners at once and does not get them. Move the five release targets to GitHub-hosted runners. This repository is public, so those minutes are free, and a dispatch-only release that runs a few times a month then draws nothing from the shared Blacksmith pool. That serves ADR 0001's goal — protect the shared pool — more completely than routing release builds through it. PR Check stays on Blacksmith, unchanged. --- .github/workflows/release.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8d40c1d..061badf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,11 @@ name: Release # Blacksmith free tier is not consumed by surprise release builds. The # workflow stays local-by-default: an operator dispatches it after a manual # `cargo publish`. See docs/adr/0001-pr-check-blacksmith-local-release.md. +# +# The release matrix runs on GitHub-hosted runners. This repository is public, +# so those minutes are free, and a rare dispatch-only release then costs the +# shared Blacksmith pool nothing at all — which is what ADR 0001 set out to +# protect. PR Check stays on Blacksmith, unchanged. on: workflow_dispatch: @@ -35,7 +40,7 @@ jobs: matrix: include: - target: x86_64-pc-windows-msvc - runner: blacksmith-4vcpu-windows-2025 + runner: windows-latest ext: .exe - target: x86_64-apple-darwin runner: macos-latest @@ -44,10 +49,10 @@ jobs: runner: macos-latest ext: "" - target: x86_64-unknown-linux-gnu - runner: blacksmith-4vcpu-ubuntu-2404 + runner: ubuntu-latest ext: "" - target: aarch64-unknown-linux-gnu - runner: blacksmith-4vcpu-ubuntu-2404 + runner: ubuntu-latest ext: "" use_cross: true