Skip to content

fix(release): sign macOS DMGs so they install and run#899

Merged
dangershony merged 4 commits into
mainfrom
mac-app
Jun 22, 2026
Merged

fix(release): sign macOS DMGs so they install and run#899
dangershony merged 4 commits into
mainfrom
mac-app

Conversation

@Zazawowow

Copy link
Copy Markdown
Collaborator

Problem

The published macOS .dmg artifacts reported "damaged and can't be opened" on download. Root cause: the DMGs were cross-compiled on the Linux release job (dotnetpackager dmg), so the arm64 Mach-O binaries shipped unsigned — and macOS refuses to run an unsigned arm64 binary, surfacing it as "damaged" (a Gatekeeper/quarantine result, not real corruption).

Fixing the signing alone wasn't enough — local testing surfaced two further blockers that meant the app couldn't run on macOS at all, plus a startup error.

Changes

  • build-macos job (release-avalonia.yml) — new job on macos-latest (matrix x64/arm64) that publishes natively, assembles the .app, ad-hoc signs it with hardened runtime + JIT entitlements, and packs a verified DMG. Cross-compile DMG steps removed from build-linux (artifact linux-macos-packageslinux-packages).
    • JIT entitlements (allow-jit, allow-unsigned-executable-memory, …) are required or .NET dies at launch with Failed to create CoreCLR, HRESULT: 0x80070008.
  • macOS secure key providerCompositionRoot only handled Windows/Linux and threw PlatformNotSupportedException on macOS. Renamed LinuxSecureKeyProviderUnixFileSecureKeyProvider (a chmod-600 file store, nothing Linux-specific) and registered it for Linux || macOS in the app and CLI.
  • Trim fix — rooted Websocket.Client + Serilog so its embedded LibLog reflection survives trimming (was logging an ArgumentNullException at startup).
  • App icon — regenerated AppIcon.icns on the macOS grid (rounded squircle + transparent margin; adds the 512pt/1024px slices the old square icon lacked).
  • README — documents the one-time first-launch bypass (right-click → Open).

Signing approach

Ad-hoc (free, no Apple Developer ID). This removes "damaged"; users get the standard bypassable "unidentified developer" prompt on first launch. The bundle keeps hardened runtime, so it's notarization-ready if a Developer ID is added later.

Testing

Built both arm64 and x64 DMGs locally through the exact CI pipeline (official .NET SDK, ad-hoc sign + entitlements, hdiutil UDZO). The app launches and runs: connects to Nostr relays, loads the wallet and investments, no runtime errors. Release build is 0 errors.

Note: build-time IL2104 ("Websocket.Client produced trim warnings") is an expected warning once an assembly is rooted — it's a warning, not an error.

🤖 Generated with Claude Code

Dorian and others added 4 commits June 22, 2026 19:29
The DMGs were cross-compiled on the Linux job via dotnetpackager, so the
arm64 Mach-O binaries shipped unsigned and macOS reported the app as
"damaged and can't be opened". Add a dedicated build-macos job on
macos-latest (matrix x64/arm64) that publishes natively, assembles the
.app bundle, ad-hoc signs it with hardened-runtime + JIT entitlements
(without which .NET fails to start CoreCLR), and packs a verified DMG.

Removes the cross-compile DMG steps from build-linux (artifact renamed
linux-macos-packages -> linux-packages) and wires build-macos into the
release job. Documents the one-time first-launch bypass in the README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The composition roots only registered an ISecureKeyProvider for Windows
(DPAPI) and Linux, throwing PlatformNotSupportedException on macOS, so the
desktop app crashed at launch on Mac. LinuxSecureKeyProvider is just a
chmod-600 file store with nothing Linux-specific, so rename it to
UnixFileSecureKeyProvider and register it for Linux || macOS in both the
Avalonia app and the CLI. A Keychain-backed provider can replace it later
without changing the contract.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Websocket.Client embeds LibLog, which reflects over Serilog to wire up its
internal logging. Partial trimming removed the reflected members, so the
app logged an ArgumentNullException ("Parameter 'method'") at startup.
Root both assemblies so the reflection targets survive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The .icns was a full-bleed square (no rounded corners, no margin), so it
looked wrong next to native macOS apps. Regenerate it on the macOS icon
grid (1024 canvas, 824 rounded body, 100px transparent margin) with the
full slice set including the 512pt and 1024px sizes the old icon lacked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@dangershony dangershony merged commit b2117ea into main Jun 22, 2026
3 checks passed
@dangershony dangershony deleted the mac-app branch June 22, 2026 21:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants