Official releases are built on a trusted maintainer Mac through Fastlane. Pull requests and contributor builds never receive Apple or GitHub credentials.
The release lane runs the static gate, all native tests, immutable dependency verification, a Developer ID build, bundle verification, Apple notarization, ticket stapling, Gatekeeper assessment, archive validation, and SHA-256 generation. It then extracts the final ZIP and repeats signature, notarization, Gatekeeper, architecture, and bundle checks against the exact app users will download. It also creates, notarizes, and staples a drag-to-Applications disk image. It produces:
artifacts/Audio-Monster-v<VERSION>-macOS-arm64.zip
artifacts/Audio-Monster-v<VERSION>-macOS-arm64.zip.sha256
artifacts/Audio-Monster-v<VERSION>-macOS-arm64.dmg
artifacts/Audio-Monster-v<VERSION>-macOS-arm64.dmg.sha256
The version comes from CFBundleShortVersionString in the source Info.plist.
The app must not be running from dist/ during a release.
For a polished local installer, build the app and create a disk image:
make build-app
make package-dmg
open "dist/Audio Monster.dmg"The image contains Audio Monster and an Applications shortcut, with the Finder window arranged for the familiar drag-the-app-to-Applications workflow. This step does not modify an existing installed copy.
bundle installGemfile.lock pins the complete Ruby dependency graph. Homebrew Fastlane remains
convenient for development, but official release commands should use
bundle exec fastlane.
Keep the Developer ID identity in the login keychain and the provisioning profile outside the repository:
export AUDIO_MONSTER_SIGNING_IDENTITY='Developer ID Application: …'
export AUDIO_MONSTER_PROVISIONING_PROFILE='/path/to/Audio_Monster.provisionprofile'The profile authorizes the iCloud Documents entitlement. The build derives the Apple Team ID from the profile; no personal Team ID belongs in source control.
An App Store Connect API key is preferred. Download its .p8 file once and
store it securely outside the repository:
export AUDIO_MONSTER_ASC_KEY_ID='KEY_ID'
export AUDIO_MONSTER_ASC_ISSUER_ID='ISSUER_UUID'
export AUDIO_MONSTER_ASC_KEY_PATH='/secure/path/AuthKey_KEY_ID.p8'The fallback is an Apple ID app-specific password:
export AUDIO_MONSTER_NOTARY_APPLE_ID='developer@example.com'
export AUDIO_MONSTER_NOTARY_TEAM_ID='APPLE_TEAM_ID'
export FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD='app-specific-password'Do not put any of these values in .env, shell history, Fastfiles, commits, or
issue logs. Prefer a password manager or an ephemeral shell session.
Quit Audio Monster, then run:
make releaseSuccess requires all of the following:
Package.swift,Package.resolved, and every materialized checkout agree on the pinned MLX Audio Swift and SwiftReadability revisions and SwiftSoup 2.13.6.codesign --verify --deep --strictaccepts the app.- Every Swift compatibility runtime required by the executable is present,
ARM64-only, and independently signed inside
Contents/lib. stapler validatefinds the attached notarization ticket.- Gatekeeper reports
source=Notarized Developer ID. - The app contains the exact license/notice tree for every resolved Swift package, the reproducible monster app icon, and no legacy JavaScript, Python, or Node runtime payload.
- The final ZIP passes an integrity test and its re-extracted app passes every release check before a matching checksum is written.
The temporary ZIP uploaded by Fastlane's notarization action is deleted. The final ZIP is created only after stapling so the ticket is included.
Publishing is intentionally separate and requires a clean, committed repository
with a matching v<VERSION> tag:
export AUDIO_MONSTER_GITHUB_REPOSITORY='owner/audio-monster'
export FL_GITHUB_RELEASE_API_TOKEN='github-token'
bundle exec fastlane mac publishThe lane requires the local tag, HEAD, and the peeled tag on origin to be the
same commit both before and after the release build. It then creates a draft
GitHub release with generated notes, the ZIP, its checksum, the drag-to-
Applications DMG, and the DMG checksum. A zero major version is not
automatically marked as a prerelease. A maintainer must review and publish that
draft in GitHub; a genuinely prerelease-only build can be marked as such while
reviewing the draft. The lane will not create an implicit tag, accept an
unpushed tag, or publish a dirty working tree.
- Update
CFBundleShortVersionString,CFBundleVersion, README, and notices. - Commit the complete change, create the matching version tag, and push both
the commit and tag to
origin. - Quit the running app.
- Run the publish lane, which rebuilds, tests, signs, notarizes, verifies, and uploads the release as a draft.
- Review the draft release and verify its checksum.
- Download and launch that exact draft asset through Finder on another Mac.
- Publish the GitHub release only after the downloaded asset passes.