Shipping the Mac Component
5 minute read
Maintainers ship the native macOS component inside the ordinary barn_VERSION_darwin_arm64.tar.gz archive. Since 0.9.2, the standard installer and pgsty/infra/barn Homebrew formula install the CLI, its matching hosts helper, and Barn.app together. This page is the maintained release handbook, checked against the release scripts on 2026-11-08. For a development build, use Build from Source.
Keep one release identity
The native payload and the GoReleaser archives must carry the same version and source commit. packaging/mac-release.py exports MACOS.md, MACOS.json, bin/barn, bin/barn-hosts-helper, and Barn.app; its manifest records file modes, digests, and signing state. VM disks, credentials, restore images, and installed services are outside this payload.
The app’s bundle identifier is com.pgsty.barn; its internal executable is barn-mac-runner. The CLI checks the versioned runner protocol and also recognizes the earlier Barn Mac.app bundle name. Replacing the app preserves VM state under $BARN_HOME/mac.
Signing order matters because the CLI embeds the helper’s digest:
- Build and sign
barn-hosts-helper. - Embed that signed helper’s SHA-256 when building
barn, then sign the CLI. - Sign
Barn.appwith hardened runtime and a secure timestamp. - Submit the CLI, helper, and app together to Apple for notarization. After acceptance, staple the app, validate its ticket, and check Gatekeeper.
- Export and re-extract the payload, then recheck signatures and the stapled ticket. During release assembly, replace the Darwin arm64 archive’s CLI/helper with these exact signed bytes and regenerate its SPDX document and checksums.
The payload builder and archive verifiers enforce this pairing. Signing the helper again after building the CLI changes the digest and breaks it.
Build a formal release locally
Use an Apple Silicon Mac with macOS 27+, Xcode 27+, the locked toolchain, a Developer ID Application identity, and a stored notarytool profile. Select the identity with security find-identity -v -p codesigning; create the profile with xcrun notarytool store-credentials. Keep certificate private keys and passwords outside the repository.
Check out a clean source tree tagged v$RELEASE_VERSION, setting RELEASE_VERSION to the intended release version. Choose a new artifact directory outside the source repository:
The formal native build and release-local enforce the make catalog-check gate before building. Synchronize and test any changed official catalog first, following the release gates. A snapshot or uncommitted payload cannot serve as a formal local release. release-local verifies archives, packages, SBOMs, checksums, and CLI/helper pairing, and writes the assembled assets to the specified directory.
For packaging development, BARN_MAC_PAYLOAD=/absolute/native.tar.gz make release-snapshot exercises native payload composition. Match the payload to the snapshot version selected by .goreleaser.yaml—for example, 1.1.1-next after v1.1.0—and use uncommitted for a dirty checkout or the full commit for clean source. Snapshots accept ad-hoc signatures; formal release verification requires a notarized Developer ID payload.
Configure automated signing
The tag workflow uses all six repository secrets below. With none configured, it skips automatic release creation so maintainers can use the local path. A partial set fails immediately; with all six present, a native build, signing, or notarization failure blocks release assembly.
| Secret | Value |
|---|---|
MAC_CERTIFICATE_BASE64 |
Base64-encoded Developer ID Application .p12, including the private key |
MAC_CERTIFICATE_PASSWORD |
Password protecting the .p12 |
MAC_CODESIGN_IDENTITY |
Imported identity name or certificate fingerprint |
MAC_NOTARY_KEY_BASE64 |
Base64-encoded App Store Connect API .p8 key |
MAC_NOTARY_KEY_ID |
API key identifier |
MAC_NOTARY_ISSUER |
API issuer identifier |
The Mac workflow imports signing material into an ephemeral runner keychain and removes it at the end of the job. Pull requests and snapshots use ad-hoc builds. The release workflow creates a draft after verifying the assembled payload.
Verify installation and publish
Run make check, make mac-native-test, and a full snapshot with the native payload. Exercise first installation, repeated installation, and upgrade in an isolated directory. On a supported physical Mac, verify startup, SSH, the desktop window, and normal shutdown using the packaged app. App replacement must preserve instance IDs, machine IDs, SSH keys, and host pins.
The existing tests/mac-live-acceptance.py requires an absolute --barn path, a dedicated disposable --home, a separate new or empty --output evidence directory, and explicit --allow-test-root authorization for that test root. Inspect the sequence with --plan first. --gui adds desktop/clipboard checks; --allow-download adds Apple download when no base is prepared. The harness preserves failed state and evidence. Isolate the process’s home directory as well as BARN_HOME when testing SSH integration, because managed ~/.ssh/config entries live outside the VM state root.
Release assembly generates barn.rb from packaging/homebrew/barn.rb.tmpl. The formula installs barn into bin, and the helper and app into libexec. Keep its install/test logic aligned with the tap. Versions with a suffix need an explicit version line because Homebrew may infer 64 from the architecture suffix; plain versions omit that line. The 0.9.1 public archive predates app delivery and supports Linux guests only.
After checking the local output, push the source and tag, upload the files to a draft GitHub Release, and verify the uploaded bytes before publishing. Mark versions below 1.0.0 and versions with a prerelease suffix as pre-releases; stable releases become latest. Update all four Homebrew platform URLs and checksums after the assets are public, then verify the public archives, installer, and Homebrew upgrade against those released bytes. Record signing, notarization, VM acceptance, and public installation results separately in the validation history.