Skip to content

Ship STEPSS to macOS through a Homebrew tap, once the app is notarized #8

Description

@apetros

What

Give macOS users what #6 gives Debian and Ubuntu users: install and upgrade STEPSS through a package manager rather than downloading a .dmg and dragging it once per release. The mechanics mirror the apt work closely. A repository named SPS-L/homebrew-stepss holding a single Casks/stepss.rb that points at the release .dmg and its SHA-256, updated by the release workflow, and the user runs:

brew tap SPS-L/stepss
brew install --cask stepss

The tap is the easy half. The hard half is that Apple, unlike Debian and unlike Microsoft, will not let an unsigned application install cleanly, and fixing that is a procurement question rather than a technical one. This issue is worth opening now and finishing later, in that order.

Source for the survey behind this: Short answer.pdf in the umbrella repo. Its macOS half raises two 2026 Homebrew policy changes that are load-bearing for the install instructions and that I could not verify; they are flagged as such below.

What we actually ship today, which is narrower than the note assumes

The survey asks whether the DMG is a universal or arm64 build, and worries about Apple Silicon users getting a Rosetta-dependent app. Inspecting v3.74.10 answers it, and the answer runs the other way:

  • STEPSS.app/Contents/MacOS/STEPSS is Mach-O 64-bit arm64 executable, thin, not universal. Apple Silicon only. Intel Macs get nothing at all.
  • The bundle is ad-hoc signed. Contents/_CodeSignature exists, which is jpackage doing the minimum arm64 requires to execute locally. It is not a Developer ID signature and it is not notarized, so Gatekeeper still quarantines it on download.
  • CFBundleIdentifier is my.stepss.
  • LSMinimumSystemVersion is 10.11, which is jpackage's default and is nonsense for a thin arm64 binary.

A universal build is not something we can choose. versions.properties pins ramses-macos-arm64, stepss-helios-macos-arm64, dyngraph-macos-arm64 and codegen-macos-arm64. There is no Intel payload for any of the four engines, so an x86_64 or universal STEPSS cannot be produced from this repository until those four upstreams publish one. This is the same structural finding as the Linux arm64 note in #6, and it has the same shape: it looks like a build matrix question and is not.

Two consequences for the cask, both of which are one line each and both of which are wrong by default:

  • depends_on arch: :arm64, so an Intel user gets a clear refusal instead of an app that will not launch.
  • depends_on macos: ">= :big_sur" or similar, rather than inheriting the fictional 10.11 floor. Fixing LSMinimumSystemVersion in the bundle itself is the better version of this.

CFBundleIdentifier: my.stepss should change while the app is still small. It is derived from the main class package rather than chosen, and it is not a namespace anyone owns. It is what Gatekeeper, macOS preferences, and a cask's uninstall/zap stanzas key on, so it wants to be reverse-DNS of a domain we control: org.sps-lab.stepss. This is --mac-package-identifier in build.xml, and it is much cheaper to change before a cask ships uninstall rules referencing it than after. Note the same reasoning does not apply to the Java preferences node my.ramses.RamsesUI, which is deliberately frozen because renaming it would forget every existing user's settings; the bundle identifier has no such installed base yet.

Notarization, which is the gate

An unsigned, un-notarized app downloaded from the internet is quarantined by Gatekeeper. The user gets a refusal on first launch and has to either right-click and open, or run xattr -rd com.apple.quarantine by hand. For a tool aimed at MSc students and project partners, that is a support burden and it also undermines the point of the exercise: a package manager that ends in "now run this incantation to convince macOS to trust it" has not really packaged anything.

Doing it properly means:

  1. An Apple Developer Program membership and a Developer ID Application certificate. This is the blocker, and it is procurement, not engineering. Check whether CUT holds an institutional Apple Developer account before assuming a new one is needed.
  2. Signing the .app with that identity, running jpackage over the signed bundle to produce the DMG, then xcrun notarytool submit and xcrun stapler staple. All of it runs on the existing macos-latest runner, with the certificate and an app-specific password in secrets.
  3. Only then writing the install instructions, so they never have to mention xattr.

Until that lands, a tap is still worth having for people who already know the right-click trick, but the instructions have to be honest about it.

Two Homebrew policy claims that need checking before instructions are written

The survey states, and I could not verify either:

  • Tap trust, from Homebrew 6.0.0 on 11 June 2026. Non-official taps must be explicitly trusted before Homebrew will evaluate their Ruby, publishers cannot pre-trust their own tap, and the install line therefore becomes brew tap --trust SPS-L/stepss followed by brew install --cask stepss. It also claims brew doctor now exits non-zero when untrusted taps are present, which would matter for any lab machine provisioning script.
  • Notarization is no longer optional, with unsigned casks deprecated since Homebrew 5.0.0, the cask audit now checking codesigning and notarization, failing casks removed from the official tap by September 2026, and --no-quarantine deprecated as an escape hatch.

Both change what users type and what the tap can get away with, so confirm them against docs.brew.sh and the Homebrew release notes before publishing anything. If the second one is accurate, it moves notarization from "should" to "must" and makes the procurement step the critical path for this whole issue rather than a nice-to-have.

The repository name is forced, unlike the other two

brew tap SPS-L/stepss resolves to a repository literally named homebrew-stepss. Homebrew's naming rule is not negotiable, so this is the one channel that cannot follow the stepss-* prefix that #6 argued for and that the Scoop bucket keeps. Add it to the umbrella as a submodule anyway, under whatever directory name reads sensibly, so its workflow stays inside the action-version and secret audits.

Worth noting alongside: the survey observes that tap trust binds to the remote URL, so a mirror is a separate trust decision. That lines up with the licensing constraint in #6, where the RAMSES grant is explicitly non-transferable and mirroring is not something we can authorise anyway.

How it gets updated

The same pattern as #6 and #7. The bundles matrix is fail-fast: false, so a job hanging off needs: [bundles] is skipped whenever any single platform fails, and v3.74.8 is the proof: it shipped a .deb and a .dmg but no .msi. Put the dispatch in the dmg leg, gated on matrix.type == 'dmg', right after the asset upload, sending needs.release.outputs.version through env: and never interpolated into the script text. STEPSS_TOKEN, verified against the new repository before it is relied on, since a failed dispatch is silent on the receiving side.

The cask itself needs only the version and the SHA-256 rewritten per release, which the receiving repository can do from the release assets. Unlike winget, nothing here is moderated, so it tolerates our cadence: ten releases went out on 2026-08-15, six inside one hour.

Licensing carries over unchanged

As in #6 and #7: STEPSS as a whole is not Apache 2.0. The bundled RAMSES is proprietary, free for non-commercial use only, capped at 1000 buses and two cores; Helios and CODEGEN are under Academic Public Licenses; only the two user interfaces are Apache 2.0. A cask has no licence field to get wrong, but the tap's README is the page a user reads before typing brew install, so it should carry the same summary and point at getting-started/license.md rather than restating it.

Done when

  • build.xml sets --mac-package-identifier org.sps-lab.stepss and a truthful LSMinimumSystemVersion.
  • The Apple Developer question is answered one way or the other, and recorded here even if the answer is "not this year".
  • The DMG is signed with a Developer ID identity, notarized and stapled by the release workflow.
  • SPS-L/homebrew-stepss exists, is a submodule of the umbrella, and its README carries the licence summary.
  • brew install --cask stepss on a clean Apple Silicon machine installs a STEPSS that launches with no Gatekeeper prompt and no xattr, and brew upgrade moves it to the next release.
  • An Intel Mac gets a clear refusal from depends_on arch: :arm64 rather than a broken install.
  • The cask is updated by the release workflow from the dmg leg, and a release whose Linux or Windows leg fails still updates it.
  • The two Homebrew policy claims above have been checked against current documentation, and the install instructions match whatever is actually true.

Companion issues: #6 for Debian and Ubuntu, and #7 for Windows.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions