You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
macos: version-stamp the .dmg, ship the CLI in it, and sign with rcodesign
Three gaps in the macOS packaging, all visible next to the Windows side.
The image was always named Mercury.dmg, with nothing to distinguish 1.9.10
from 1.9.11 in a downloads folder or on a release page -- while Windows has
shipped mercury-$(MERCURY_VERSION)-w64-<hash>.zip and
Mercury_$(MERCURY_VERSION)_Setup.exe all along. MERCURY_VERSION was already
parsed from common/mercury_version.h for exactly that purpose; the .dmg simply
never used it. Now Mercury-1.9.11-universal.dmg.
The image also held only Mercury.app. There is a universal CLI target, but
nothing copied its output in, so a Mac operator wanting a headless station --
a TNC for Winlink/BPQ32, or uucp -- had nothing to install. The GUI is built
-tags mercury_embedded with the modem linked in, so the CLI is a genuinely
separate artifact. It goes in a "Command Line" folder beside the .app with
mercury.ini.example and a README, so a drag-install still copies exactly one
thing and nothing extra lands inside the bundle to complicate signing.
And there was no signing. Added opt-in signing via rcodesign
(github.com/indygreg/apple-platform-rs): the CLI, then the .app bundle, then
the image -- in that order, because signing a disk image does NOT sign what is
inside it. Notarization is a separate target (macos-notarize-dmg) since it
needs the network and Apple's verdict. Missing credentials warn instead of
failing, so developer builds are unchanged.
rcodesign rather than Apple's codesign or anchore/quill. Quill was tried
first and rejected: it signs Mach-O binaries only, and cannot sign bundles or
disk images (anchore/quill#815, #550 both open, no CodeResources support in
its tree) -- which is everything Gatekeeper actually judges. rcodesign covers
Mach-O, bundles, .dmg and .pkg, plus notarize and staple, in pure Rust with no
Mac, no Xcode and no keychain, so the signing certificate never has to reach a
macOS runner. Note this does not remove macOS from the release entirely:
hdiutil builds the image and is Apple-only. What moves off the Mac is signing
and notarization.
Two pre-existing bugs surfaced while testing this:
- macos-universal never invoked internal_deps, so after its own `make clean`
the link failed on a missing modem/freedv/libfreedvdata.a. That target
could not work on a clean tree; nothing had called it from the dmg path
before, so it went unnoticed.
- the two universal targets both run `make clean`, which removes `mercury`
AND Mercury.app, so they cannot be ordered as prerequisites -- whichever
ran second deleted the other's output. The CLI is now built first and
parked under a name clean does not match (and deliberately NOT added to
clean, which would defeat it), cleared at the start of the recipe so a
leftover from a failed run can never be staged as if fresh.
Tested on macOS 15.7.7 (x86_64):
- image mounts and contains Mercury.app, Applications, and
Command Line/{mercury,mercury.ini.example,README.txt}
- both binaries report `lipo -archs` = x86_64 arm64
- the CLI runs: "Mercury Version 1.9.11 (git 6dec3b0)"
- a Mercury.app signed by rcodesign 0.28.0 ON LINUX passes Apple's own
`codesign --verify --deep --strict`: "valid on disk", "satisfies its
Designated Requirement", with CodeResources written and the universal
Mach-O sealed
- the .dmg signed on Linux likewise verifies and still mounts
Signed with a self-signed test certificate, so spctl still rejects it -- that
is Gatekeeper judging the credential, not the signature. A real Developer ID
plus notarization is the remaining gate, and is a credentials matter rather
than a code one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
0 commit comments