diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 2b870e5..99feb1f 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -100,7 +100,10 @@ jobs: # npmjs.com pointing at this repo + workflow (release-please.yml). publish-npm: needs: [release-please, build-and-upload] - if: ${{ needs.release-please.outputs.release_created }} + # always(): publish whatever targets reached the release even if one build + # leg failed. The generator is partial-matrix-tolerant (only present targets + # are pinned in optionalDependencies) and fails loudly if nothing built. + if: ${{ always() && needs.release-please.outputs.release_created }} runs-on: ubuntu-latest permissions: contents: read @@ -149,7 +152,9 @@ jobs: update-homebrew-formula: needs: [release-please, build-and-upload] - if: ${{ needs.release-please.outputs.release_created }} + # always(): the formula only needs the 4 darwin/linux-gnu assets, so a musl + # build failure shouldn't block it. Fails on its own if a core asset is missing. + if: ${{ always() && needs.release-please.outputs.release_created }} runs-on: ubuntu-latest steps: @@ -180,14 +185,17 @@ jobs: VERSION=${{ needs.release-please.outputs.tag_name }} VERSION_NO_V=${VERSION#v} - # Download checksums - curl -L -o darwin-x64.sha256 \ + # Download checksums. -f makes curl fail (non-zero) on a 404 instead of + # writing an HTML error page into the .sha256 file — so when a core + # target is missing under always(), this job hard-fails rather than + # baking a corrupt checksum into the formula. + curl -fLsS -o darwin-x64.sha256 \ "https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-darwin-x64.sha256" - curl -L -o darwin-arm64.sha256 \ + curl -fLsS -o darwin-arm64.sha256 \ "https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-darwin-arm64.sha256" - curl -L -o linux-x64.sha256 \ + curl -fLsS -o linux-x64.sha256 \ "https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-linux-x64.sha256" - curl -L -o linux-arm64.sha256 \ + curl -fLsS -o linux-arm64.sha256 \ "https://github.com/${{ github.repository }}/releases/download/${VERSION}/csp-linux-arm64.sha256" # Extract checksums diff --git a/.github/workflows/release-rust.yml b/.github/workflows/release-rust.yml index 48e6a65..c78e960 100644 --- a/.github/workflows/release-rust.yml +++ b/.github/workflows/release-rust.yml @@ -74,12 +74,36 @@ jobs: - name: Add target run: rustup target add ${{ matrix.target }} - - name: Install musl tools + # The musl target pulls C/C++ deps (esaxx-rs C++ via tokenizers, onig/zstd + # C) but musl-tools only ships musl-gcc, not musl-g++, so esaxx-rs fails to + # build. cargo-zigbuild uses zig as a full C/C++ cross toolchain (clang + + # musl + libc++), which builds the whole native set. zig is installed from + # the official tarball, so no extra pinned third-party action is needed. + - name: Set up cargo-zigbuild (musl) if: ${{ endsWith(matrix.target, '-musl') }} - run: sudo apt-get update && sudo apt-get install -y musl-tools + run: | + ZIG_VERSION=0.13.0 + # Zig signs releases with minisign (not SHA checksums). Verify the + # tarball against the official ziglang.org public key before extracting + # and executing it, so a compromised mirror/MITM can't inject a binary. + ZIG_PUBKEY="RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U" + TARBALL="zig-linux-x86_64-${ZIG_VERSION}.tar.xz" + sudo apt-get update && sudo apt-get install -y minisign + curl -fsSLO "https://ziglang.org/download/${ZIG_VERSION}/${TARBALL}" + curl -fsSLO "https://ziglang.org/download/${ZIG_VERSION}/${TARBALL}.minisig" + minisign -Vm "${TARBALL}" -P "${ZIG_PUBKEY}" + tar -xJf "${TARBALL}" -C "$RUNNER_TEMP" + echo "$RUNNER_TEMP/zig-linux-x86_64-${ZIG_VERSION}" >> "$GITHUB_PATH" + cargo install --locked cargo-zigbuild --version '^0.19' - name: Build release binary - run: cargo build --release --locked -p csp-cli --target ${{ matrix.target }} + shell: bash + run: | + if [[ "${{ matrix.target }}" == *-musl ]]; then + cargo zigbuild --release --locked -p csp-cli --target "${{ matrix.target }}" + else + cargo build --release --locked -p csp-cli --target "${{ matrix.target }}" + fi - name: Stage asset (unix) if: ${{ !startsWith(matrix.os, 'windows') }} @@ -104,9 +128,11 @@ jobs: ${{ matrix.asset }} ${{ matrix.asset }}.sha256 + # Resilient by design: runs even if some build legs failed (always()), so the + # targets that did build still reach the release. Fails only if NOTHING built. upload-release-assets: needs: build - if: ${{ inputs.tag != '' }} + if: ${{ always() && inputs.tag != '' }} runs-on: ubuntu-latest permissions: contents: write @@ -119,8 +145,14 @@ jobs: - name: Prepare release assets run: | mkdir -p release - find artifacts -type f -exec cp {} release/ \; - ls -lh release/ + find artifacts -type f -exec cp {} release/ \; 2>/dev/null || true + count=$(find release -type f | wc -l | tr -d ' ') + echo "staged $count asset file(s):" + ls -lh release/ || true + if [ "$count" -eq 0 ]; then + echo "::error::no built assets to upload — every build leg failed" >&2 + exit 1 + fi - name: Upload to release env: