diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 95a8915..716f741 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -89,6 +89,61 @@ jobs: bun build export/index.ts --compile --target=${{ matrix.target }} $EXTERNAL_FLAGS --outfile=supertag-export fi + # Apple Silicon (and increasingly x64 on macOS 26+) kills any Mach-O that + # lacks a valid code signature with SIGKILL (exit 137) at exec — no + # Gatekeeper dialog, quarantine removal does nothing. Bun's --compile + # *usually* ad-hoc signs, but some Bun versions (and cross-compiled + # targets) ship UNSIGNED binaries, which is exactly how #84 escaped for a + # month. Re-sign explicitly here so we never depend on Bun's auto-signing. + # --remove-signature first clears any malformed/partial signature that + # would otherwise make `codesign --force` fail with "invalid format". + - name: Codesign macOS binaries (ad-hoc) + run: | + set -euo pipefail + for bin in supertag supertag-lite supertag-mcp supertag-export; do + codesign --remove-signature "$bin" 2>/dev/null || true + codesign --force --sign - --timestamp=none "$bin" + codesign --verify --verbose "$bin" + done + + # Verification gate: actually EXECUTE every binary. This is the check that + # would have caught #84 in CI instead of a month later. An unsigned Mach-O + # is SIGKILLed by the kernel at exec (exit 137) on Apple Silicon, so we + # only need to prove each binary *starts*. supertag/-lite/-export exit + # cleanly on --version; supertag-mcp ignores the flag and runs as a stdio + # server, so we use a background guard (no GNU `timeout` on macOS runners): + # launch, wait 2s, and if it's still alive it execed fine (kill = pass); + # otherwise the only failure we care about is 137 (kernel SIGKILL). + - name: Smoke-test binaries + run: | + set -uo pipefail + RUNNER="" + if [ "${{ matrix.target }}" = "bun-darwin-arm64" ]; then + RUNNER="" # native arm64 on macos-latest + elif arch -x86_64 true 2>/dev/null; then + RUNNER="arch -x86_64" # x64 via Rosetta + else + echo "Rosetta unavailable; skipping x64 execution (signatures verified above)" + exit 0 + fi + smoke() { + local bin="$1" + $RUNNER "./$bin" --version /dev/null 2>&1 & + local pid=$! + sleep 2 + if kill -0 "$pid" 2>/dev/null; then + kill "$pid" 2>/dev/null; wait "$pid" 2>/dev/null || true + echo "ok: $bin started (long-running; killed after 2s)" + else + wait "$pid"; local rc=$? + if [ "$rc" -eq 137 ]; then + echo "FAIL: $bin was SIGKILLed (exit 137) — invalid/missing code signature"; exit 1 + fi + echo "ok: $bin exited $rc" + fi + } + for b in supertag supertag-lite supertag-mcp supertag-export; do smoke "$b"; done + - name: Create distribution package run: | DIST_NAME="supertag-cli-${{ matrix.suffix }}"