Skip to content

build(release): add macOS notarization and Windows code signing #242

Description

@inureyes

Problem

  • macOS: The release pipeline already codesigns the binary with Developer ID Application (lines 137-149 of .github/workflows/release.yml), but it is never notarized. As a result, even an Apple-signed binary will trigger Gatekeeper warnings on first launch. The AC_API_ISSUER_ID, AC_API_KEY_ID, and AC_API_PRIVATE_KEY_P8 secrets are already wired into the packaging environment but never used — no xcrun notarytool step exists.
  • Windows: all-smi.exe is shipped completely unsigned, so end users see SmartScreen warnings and corporate antivirus products may flag the binary. There is no signing infrastructure in the workflow.
  • Reference: The sister project backend.ai-go already implements both flows end-to-end in .github/workflows/packaging.yml, so the patterns are known-good and reusable here.

Proposed Solution

All changes are confined to .github/workflows/release.yml. No source code changes are expected.

macOS — extend the existing Code sign macOS binary step

  1. After the existing codesign --options runtime --timestamp invocation, ditto the signed binary into a temporary .zip (notarytool only accepts .zip, .dmg, or .pkg — it cannot take a raw Mach-O).
  2. Write the AC_API_PRIVATE_KEY_P8 secret to a chmod 600 temp .p8 file inside $RUNNER_TEMP.
  3. Submit with:
    xcrun notarytool submit \"$NOTARIZE_ZIP\" \\
      --key \"$KEY_FILE\" \\
      --key-id \"$AC_API_KEY_ID\" \\
      --issuer \"$AC_API_ISSUER_ID\" \\
      --wait --timeout 30m
    
  4. On status: Invalid or status: Rejected, fetch xcrun notarytool log <submission-id> for diagnostics, then fail the step.
  5. Verify with spctl --assess --type execute --verbose <binary>. Treat first-call propagation delay as a warning, not a failure — once Apple has issued the notarization ticket, Gatekeeper verifies it online at first launch regardless of local spctl cache state.
  6. Always clean up the temp .p8 file (use trap or an if: always() cleanup step).
  7. Do not attempt xcrun stapler staple on the raw binary — stapling only works on .app, .dmg, and .pkg bundles. Add an explanatory comment in the workflow stating this is intentional.

Windows — switch runner and add signing

  1. Change the matrix os for x86_64-pc-windows-msvc from windows-latest to windows-on-macmini02-x64 (the same self-hosted runner used by backend.ai-go, which already has the signing certificate installed in the Windows Certificate Store).
  2. Add a Setup persistent cache paths (Windows self-hosted) step (mirror lines 118-130 of backend.ai-go/.github/workflows/packaging.yml) to point CARGO_HOME / RUSTUP_HOME to persistent paths outside the workspace, so the self-hosted runner does not balloon its workspace cache between runs.
  3. After cargo build --release and before the packaging step, add a Sign Windows binary with signtool step:
    $SIGNTOOL = \"C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.26100.0\\x64\\signtool.exe\"
    $TIMESTAMP_URL = \"http://ts.ssl.com/\"
    & $SIGNTOOL sign /tr $TIMESTAMP_URL /td sha256 /fd sha256 /a target/x86_64-pc-windows-msvc/release/all-smi.exe
    Fail the step on non-zero exit. Then verify with & $SIGNTOOL verify /pa <path> (warn-only on verification failure, mirroring backend.ai-go's behavior).
  4. Signing must happen before the Compress-Archive packaging step so the signed .exe ends up inside the released zip.
  5. The existing checksum step then naturally covers the signed artifact.

Acceptance Criteria

  • .github/workflows/release.yml macOS matrix entry has codesign → notarize → spctl verify, with AC_API_* secrets sourced from the packaging environment (already declared at the job level).
  • Notarization step writes the .p8 key to a temp file, runs xcrun notarytool submit ... --wait, fetches notarytool log on failure, and removes the key file unconditionally.
  • The workflow contains an explanatory comment stating that stapling is intentionally skipped because it does not apply to bare Mach-O binaries.
  • Windows matrix entry runs on windows-on-macmini02-x64.
  • Windows job has a persistent-cache-path setup step before cargo build.
  • Windows signing step uses signtool sign /tr http://ts.ssl.com/ /td sha256 /fd sha256 /a and verifies with signtool verify /pa.
  • Signing happens before the zip packaging step so the released archive contains the signed binary.
  • No secrets are echoed in logs (key files chmod 600, certificate password not printed, etc.).
  • No source code changes outside the workflow file.

Non-Goals

  • Building .pkg or .dmg installers for macOS (stays single-binary).
  • Adding MSI or NSIS installers for Windows.
  • Changing the existing Linux, Homebrew, or Debian PPA pipelines.
  • Changing CI workflow (ci.yml) — it continues to run on windows-latest.
  • Adding Tauri minisign-style updater signatures (all-smi has no auto-updater).

Technical Considerations / Reference

  • Pattern source: ../backend.ai-go/.github/workflows/packaging.yml — Windows signing at lines 102-368, macOS notarize at lines 1584-1629.
  • Self-hosted runner label windows-on-macmini02-x64 is already provisioned with the signing certificate in the Windows Certificate Store; it is the same runner used for backend.ai-go releases, so no new infrastructure is required.
  • Secrets already configured in the packaging environment: DEV_ID_CERT_P12, DEV_ID_CERT_PASSWORD, AC_API_ISSUER_ID, AC_API_KEY_ID, AC_API_PRIVATE_KEY_P8, and the BUNDLE_ID variable (com.lablup.all-smi).
  • backend.ai-go uses APPLE_ID / APPLE_TEAM_ID / APPLE_PASSWORD for notarytool; all-smi will instead use the App Store Connect API key alternative (--key / --key-id / --issuer) since those secrets are already wired.

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority:highHigh priority issuestatus:readyReady to be worked ontype:choreMaintenance tasks (build, CI, etc)

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions