The released MCCOutletCLI linked libusb by absolute Homebrew path. Once
the CLI is Developer-ID signed with the hardened runtime it is subject to
library validation and may only load dylibs signed by Apple or the same
Team ID. Homebrew's libusb bottle is signed by a different team, so dyld
refused it unconditionally and the shipped CLI was unrunnable on every
machine (including ones with `brew install libusb`).
CI missed this because the smoke test runs in the build job, before the
sign-macos job adds the hardened runtime, on a runner that has brew libusb.
Fix, in the sign-macos job before the CLI is signed: treat libusb exactly
like lsl.framework. Copy the dylib into the CLI's Frameworks/ dir
(preferring the macdeployqt'd app copy, falling back to the CLI's current
reference so it works on both /opt/homebrew and /usr/local runners),
normalize its install name to @rpath, repoint the CLI, and sign the dylib
with the Developer ID before the CLI (dependency before dependent). The
dylib ships automatically since Frameworks is already in the tarball.
Also add a guardrail: after signing, fail the build if the CLI still
links any absolute brew dylib.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem
The released MCCOutletCLI from the signed macOS tarball is dead on arrival on every machine — even ones with brew install libusb:
The GUI app is fine; only the CLI is broken.
Root cause
Two facts combine:
Why CI didn't catch it
The MCCOutletCLI --help smoke tests run in the build job, before the sign-macos job adds the hardened runtime, on a runner that has brew libusb. Unsigned/non-hardened binaries can load cross-team dylibs, so the pre-signing test passes, then signing silently breaks the shipped artifact.
Fix
In the sign-macos job, before sign_and_notarize.sh runs on the CLI, treat libusb exactly like lsl.framework:
The dylib ships automatically — Frameworks is already in the release tarball.
Verification
Manually validated the equivalent steps against the v3.2.0 asset on both a dev Mac and a deployed study computer: after the repoint + re-sign the CLI runs and enumerates an attached USB-1608FS-Plus. Once this ships, the downstream brnbci-deploy workaround that repoints + re-signs the binary post-download becomes a no-op.
🤖 Generated with Claude Code