From ce82fd3e14974e1663c627ad8c01c04628e21fd8 Mon Sep 17 00:00:00 2001 From: Brian Charbonneau Date: Thu, 30 Jul 2026 00:30:40 -0700 Subject: [PATCH] fix(release): package private CA builds locally Signed-off-by: Brian Charbonneau --- .github/workflows/private-ca-release.yml | 66 ++---------------- .../private-ca-desktop-release-lifecycle.md | 28 ++++---- scripts/build-private-ca-macos.sh | 68 +++++++++++++++++++ scripts/test-private-ca-release-contract.sh | 34 +++++++--- 4 files changed, 109 insertions(+), 87 deletions(-) create mode 100755 scripts/build-private-ca-macos.sh diff --git a/.github/workflows/private-ca-release.yml b/.github/workflows/private-ca-release.yml index c25e0ca7d9..1d5582099d 100644 --- a/.github/workflows/private-ca-release.yml +++ b/.github/workflows/private-ca-release.yml @@ -156,70 +156,16 @@ jobs: working-directory: ${{ runner.temp }}/source run: cargo test --manifest-path desktop/src-tauri/Cargo.toml native_websocket::tests::native_websocket_platform_tls_connector_is_available - package: + local-macos-handoff: needs: [approval, validate] - runs-on: macos-15 - timeout-minutes: 75 + runs-on: ubuntu-latest + timeout-minutes: 5 permissions: - contents: write - attestations: write - id-token: write + issues: write steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 - - uses: cashapp/activate-hermit@cea9af7913204a965fd488637a8d1811bba2e616 # v1 - - uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2 - - name: Fetch immutable source and apply patch - run: | - set -euo pipefail - git clone --depth 1 --branch "${{ needs.approval.outputs.tag }}" "https://github.com/${UPSTREAM_REPOSITORY}.git" "$RUNNER_TEMP/source" - [[ "$(git -C "$RUNNER_TEMP/source" rev-parse HEAD)" == "${{ needs.approval.outputs.source_sha }}" ]] - git -C "$RUNNER_TEMP/source" fetch --depth 2 "https://github.com/${GITHUB_REPOSITORY}.git" "${PATCH_COMMIT}" - git -C "$RUNNER_TEMP/source" cherry-pick --no-commit "${PATCH_COMMIT}" - - name: Run macOS platform connector test - working-directory: ${{ runner.temp }}/source - run: cargo test --manifest-path desktop/src-tauri/Cargo.toml native_websocket::tests::native_websocket_platform_tls_connector_is_available - - name: Install desktop dependencies - working-directory: ${{ runner.temp }}/source - run: just desktop-install-ci - - name: Build without updater artifacts or upstream signing - working-directory: ${{ runner.temp }}/source - run: | - set -euo pipefail - cat > desktop/src-tauri/tauri.private-ca.conf.json <<'EOF' - { "bundle": { "macOS": { "minimumSystemVersion": "10.15" }, "createUpdaterArtifacts": false } } - EOF - cd desktop - pnpm tauri build --verbose --no-sign --features mesh-llm --config src-tauri/tauri.private-ca.conf.json - - name: Ad-hoc sign and rebuild the arm64 DMG - working-directory: ${{ runner.temp }}/source - run: | - set -euo pipefail - app_path="desktop/src-tauri/target/release/bundle/macos/Buzz.app" - mkdir -p "$RUNNER_TEMP/release/dmg-root" - codesign --force --deep --sign - "${app_path}" - codesign --verify --deep --strict --verbose=2 "${app_path}" - cp -R "${app_path}" "$RUNNER_TEMP/release/dmg-root/Buzz.app" - hdiutil create -volname Buzz -srcfolder "$RUNNER_TEMP/release/dmg-root" -ov -format UDZO "$RUNNER_TEMP/release/Buzz-${{ needs.approval.outputs.tag }}-private-ca-arm64.dmg" - - name: Create manifest, patch, and checksums - working-directory: ${{ runner.temp }}/source - run: | - set -euo pipefail - cat > "$RUNNER_TEMP/release/manifest.json" < "$RUNNER_TEMP/release/private-ca.patch" - (cd "$RUNNER_TEMP/release" && shasum -a 256 *.dmg manifest.json private-ca.patch > SHA256SUMS) - printf '%s\n' '# Private-CA build test summary' '- upstream tag and SHA verified' '- just ci and focused connector test passed on Ubuntu' '- macOS platform connector test passed' '- ad-hoc signed DMG with updater disabled' > "$RUNNER_TEMP/release/TEST-SUMMARY.md" - - name: Attest the DMG - uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 - with: - subject-path: ${{ runner.temp }}/release/*.dmg - - name: Publish fork prerelease and mark ticket built + - name: Local macOS package handoff env: GH_TOKEN: ${{ github.token }} run: | set -euo pipefail - release_tag="buzz-private-ca-${{ needs.approval.outputs.tag }}-r${GITHUB_RUN_NUMBER}" - gh release create "${release_tag}" "$RUNNER_TEMP/release/Buzz-${{ needs.approval.outputs.tag }}-private-ca-arm64.dmg" "$RUNNER_TEMP/release/SHA256SUMS" "$RUNNER_TEMP/release/manifest.json" "$RUNNER_TEMP/release/private-ca.patch" "$RUNNER_TEMP/release/TEST-SUMMARY.md" --prerelease --title "Buzz private-CA ${release_tag}" --notes "Built from ${{ needs.approval.outputs.tag }} at ${{ needs.approval.outputs.source_sha }} with patch ${PATCH_COMMIT}." - gh issue edit "${{ needs.approval.outputs.issue_number }}" --add-label built - gh issue comment "${{ needs.approval.outputs.issue_number }}" --body "Fork prerelease published: https://github.com/${GITHUB_REPOSITORY}/releases/tag/${release_tag}" + gh issue comment "${{ needs.approval.outputs.issue_number }}" --body "Remote validation passed. Package on the approved local Mac with scripts/build-private-ca-macos.sh ${{ needs.approval.outputs.tag }} ${{ needs.approval.outputs.source_sha }}. GitHub-hosted macOS runners are intentionally not used." diff --git a/docs/operations/private-ca-desktop-release-lifecycle.md b/docs/operations/private-ca-desktop-release-lifecycle.md index f15681c258..b97b86557c 100644 --- a/docs/operations/private-ca-desktop-release-lifecycle.md +++ b/docs/operations/private-ca-desktop-release-lifecycle.md @@ -106,34 +106,30 @@ text never become command input. `skip` closes the ticket without a build. A conflict or failed preflight applies `remediation-required` and publishes no package. -## Hosted validation and package +## Hosted validation and local package -Standard GitHub-hosted runners are used for bounded, reproducible work. They -are currently unmetered for this public repository, but concurrency, queueing, -cache, artifact, and fair-use limits still apply. Larger billable runners are -not used. +Standard GitHub-hosted Ubuntu runners are used for bounded, reproducible source +validation. GitHub-hosted macOS and Windows runners are not used. The approved +local Mac performs the package step after remote validation succeeds. The approved build has three gates: 1. Ubuntu checks out the exact upstream tag, applies the pinned patch without committing it, then runs formatting, Clippy, focused connector tests, and `just ci`. -2. macOS arm64 runs a hermetic platform-trust test. It creates a disposable CA - and localhost WSS server, proves the connection fails before trust is added, - succeeds after adding that CA to a temporary macOS keychain, then removes - all temporary trust material. It covers primary, pairing, and huddle paths. -3. macOS arm64 packages the exact source using existing upstream sidecar and +2. The approved local Apple Silicon Mac runs the platform-trust and private WSS + gates before installation; no identity key is supplied to either gate. +3. The same local Mac runs `scripts/build-private-ca-macos.sh` with the ticket's + immutable tag and SHA. It uses existing upstream sidecar and Tauri conventions. The package has no updater keys, no notarization, no Apple Developer signing identity, and no private infrastructure values. The updater remains disabled. The app is ad-hoc signed and verified before the DMG is rebuilt. -Publish a fork prerelease named `buzz-private-ca-vX.Y.Z-rN`, never the general -latest release. Include the arm64 DMG, SHA256SUMS, applied patch, test summary, -and a manifest containing upstream tag/SHA, patch SHA, result tree SHA, tool -versions, runner architecture, and workflow URL. Add a GitHub build-provenance -attestation. Durable packages belong in the prerelease, not a short-lived -Actions artifact. +The local helper writes the arm64 DMG, SHA256SUMS, applied patch, and a manifest +to `dist/private-ca//`. Publish it as a fork prerelease only after the +private WSS gate and local acceptance are green; it is never the general +latest release. ## Private WSS gate diff --git a/scripts/build-private-ca-macos.sh b/scripts/build-private-ca-macos.sh new file mode 100755 index 0000000000..4bb8f6548b --- /dev/null +++ b/scripts/build-private-ca-macos.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +set -euo pipefail + +readonly upstream_repository="https://github.com/block/buzz.git" +readonly fork_repository="https://github.com/BrianInAz/buzz.git" +readonly patch_commit="6d03a38da5e3402bf97df1b3c46152887eb3778e" + +tag="${1:?usage: $0 [output-directory]}" +source_sha="${2:?usage: $0 [output-directory]}" +output_directory="${3:-$PWD/dist/private-ca/${tag}}" + +[[ "$(uname -s)" == "Darwin" ]] || { + echo "this packaging helper must run on macOS" >&2 + exit 1 +} + +for command in git just pnpm codesign hdiutil shasum; do + command -v "${command}" >/dev/null || { + echo "required command is unavailable: ${command}" >&2 + exit 1 + } +done + +work_directory="$(mktemp -d)" +cleanup() { + rm -rf "${work_directory}" +} +trap cleanup EXIT + +mkdir -p "${output_directory}/dmg-root" +git clone --depth 1 --branch "${tag}" "${upstream_repository}" "${work_directory}/source" +[[ "$(git -C "${work_directory}/source" rev-parse HEAD)" == "${source_sha}" ]] || { + echo "upstream tag did not resolve to the approved SHA" >&2 + exit 1 +} +git -C "${work_directory}/source" fetch --depth 2 "${fork_repository}" "${patch_commit}" +git -C "${work_directory}/source" cherry-pick --no-commit "${patch_commit}" +git -C "${work_directory}/source" diff --check + +( + cd "${work_directory}/source" + just desktop-install-ci + cat > desktop/src-tauri/tauri.private-ca.conf.json <<'EOF' +{ "bundle": { "macOS": { "minimumSystemVersion": "10.15" }, "createUpdaterArtifacts": false } } +EOF + cd desktop + pnpm tauri build --verbose --no-sign --features mesh-llm --config src-tauri/tauri.private-ca.conf.json +) + +app_path="${work_directory}/source/desktop/src-tauri/target/release/bundle/macos/Buzz.app" +dmg_path="${output_directory}/Buzz-${tag}-private-ca-arm64.dmg" +codesign --force --deep --sign - "${app_path}" +codesign --verify --deep --strict --verbose=2 "${app_path}" +cp -R "${app_path}" "${output_directory}/dmg-root/Buzz.app" +hdiutil create -volname Buzz -srcfolder "${output_directory}/dmg-root" -ov -format UDZO "${dmg_path}" + +cat > "${output_directory}/manifest.json" < "${output_directory}/private-ca.patch" +( + cd "${output_directory}" + shasum -a 256 "$(basename "${dmg_path}")" manifest.json private-ca.patch > SHA256SUMS +) + +rm -rf "${output_directory}/dmg-root" +echo "private-CA package written to ${output_directory}" diff --git a/scripts/test-private-ca-release-contract.sh b/scripts/test-private-ca-release-contract.sh index 4b76379aeb..e029fe015a 100755 --- a/scripts/test-private-ca-release-contract.sh +++ b/scripts/test-private-ca-release-contract.sh @@ -3,6 +3,7 @@ set -euo pipefail workflow=".github/workflows/private-ca-release.yml" +local_builder="scripts/build-private-ca-macos.sh" if [[ ! -f "${workflow}" ]]; then echo "missing ${workflow}" >&2 @@ -31,8 +32,6 @@ require 'labeled' require 'workflow_dispatch:' require 'contents: read' require 'issues: write' -require 'attestations: write' -require 'id-token: write' require "github.actor == 'BrianInAz'" require "github.event.label.name == 'build-approved'" require "github.event.label.name == 'skip'" @@ -40,16 +39,9 @@ require 'refs/tags/' require '6d03a38da5e3402bf97df1b3c46152887eb3778e' require 'cherry-pick --no-commit' require 'just ci' -require 'macos-15' -require 'codesign --verify --deep --strict' -require 'hdiutil create' -require 'SHA256SUMS' -require 'gh release create' require 'gh issue close' -require '--prerelease' -require 'actions/attest-build-provenance@' -require 'createUpdaterArtifacts": false' -require 'buzz-private-ca-' +require 'Local macOS package handoff' +require 'scripts/build-private-ca-macos.sh' forbid 'BUZZ_TEST_WSS_URL' forbid 'buzz.bjzy.me' @@ -58,5 +50,25 @@ forbid 'tailscale' forbid 'VAULT_' forbid 'APPLE_CERTIFICATE' forbid 'TAURI_SIGNING_PRIVATE_KEY' +forbid 'runs-on: macos' +forbid 'actions/attest-build-provenance@' +forbid 'gh release create' + +if [[ ! -x "${local_builder}" ]]; then + echo "missing executable ${local_builder}" >&2 + exit 1 +fi + +for expected in 'set -euo pipefail' 'createUpdaterArtifacts": false' 'codesign --verify --deep --strict' 'hdiutil create'; do + if ! rg --fixed-strings --quiet -- "${expected}" "${local_builder}"; then + echo "${local_builder} must contain: ${expected}" >&2 + exit 1 + fi +done + +if rg --ignore-case --fixed-strings --quiet -- 'insecure_skip_verify' "${local_builder}"; then + echo "${local_builder} must not bypass TLS verification" >&2 + exit 1 +fi echo "private CA release workflow contract passed"