CI (.github/workflows/ci.yml, job homebrew) builds, signs, publishes and
updates the Homebrew tap on every push to main and on every version tag.
Nothing ships unless format, lint, guardrails, build, test, inspector and the
plugin gate all pass.
main publishes| Condition | Release | Cask |
|---|---|---|
Resources/Info.plist carries a version with no tag yet |
That version: a release and tag 1.1.0 at this commit, with generated notes |
flash |
| The version is already tagged | A nightly-YYYYMMDD-SHA pre-release for that commit |
Rolling flash@nightly |
A *.*.* tag pushed by hand |
That version | flash |
So a release is one commit: bump CFBundleShortVersionString in
Resources/Info.plist, merge to main, and CI cuts the tag, the release and
the cask. The tag is created by the workflow token, which does not start a
second run. CFBundleVersion is the workflow run number. GitHub releases are
immutable after publication, so CI creates each release with its archive
attached before publishing it. A rerun accepts an existing release only when
the expected archive is already present.
Release builds are ad-hoc signed until the secrets below exist. With them,
CI signs with your Developer ID, hardens the runtime
(Resources/Flash.entitlements), submits the zip to Apple’s notary service,
staples the ticket and zips again. Users then skip “Open Anyway”, and the
Accessibility grant survives updates because the signature is stable.
Everything is read from GitHub Actions secrets of the repository (Settings › Secrets and variables › Actions › Repository secrets). Missing secrets only skip the step with a warning in the job log.
| Secret | Contents |
|---|---|
MACOS_CERTIFICATE_P12 |
The Developer ID Application certificate and its private key, as a .p12, base64-encoded in one line |
MACOS_CERTIFICATE_PASSWORD |
The password you set when exporting the .p12 |
NOTARY_KEY_ID |
The App Store Connect API key ID, ten characters |
NOTARY_ISSUER_ID |
The issuer ID of that key, a UUID |
NOTARY_KEY_P8 |
The .p8 private key file, base64-encoded in one line |
You need an Apple Developer Program membership.
.cer.).p12, and
set a password. That password is MACOS_CERTIFICATE_PASSWORD.Encode it:
base64 -i DeveloperID.p12 | tr -d '\n' | pbcopy
Paste the clipboard as MACOS_CERTIFICATE_P12, then delete the .p12.
The certificate is valid for five years. CI finds the identity by name in the imported certificate, so nothing else to configure.
flash-ci.NOTARY_ISSUER_ID) and the
key’s Key ID (NOTARY_KEY_ID).Download the .p8 file. Apple lets you download it once. Encode it:
base64 -i AuthKey_XXXXXXXXXX.p8 | tr -d '\n' | pbcopy
Paste the clipboard as NOTARY_KEY_P8, then delete the file.
With the GitHub CLI, from the repository:
gh secret set MACOS_CERTIFICATE_P12 < <(base64 -i DeveloperID.p12 | tr -d '\n')
gh secret set MACOS_CERTIFICATE_PASSWORD
gh secret set NOTARY_KEY_ID
gh secret set NOTARY_ISSUER_ID
gh secret set NOTARY_KEY_P8 < <(base64 -i AuthKey_XXXXXXXXXX.p8 | tr -d '\n')
Each command without a redirect prompts for the value. HOMEBREW_TAP_TOKEN,
already present, lets the job push the cask to aymericbeaumet/homebrew-tap.
Run the workflow (push to main, or Actions › CI › Run workflow is not
enough: the job only deploys on a push). In the job log, “Signing as:
Developer ID Application: …” and “==> Notarizing” should appear, then
“==> Stapling the notarization ticket”. On a Mac that never saw this build:
curl -LO https://github.com/aymericbeaumet/flash/releases/latest/download/Flash-1.1.0.zip
ditto -x -k Flash-1.1.0.zip .
spctl -a -vv -t exec Flash.app # source=Notarized Developer ID
xcrun stapler validate Flash.app # The validate action worked!
codesign -dvv Flash.app 2>&1 | grep -E 'Authority|Timestamp|flags'
The build script honours the same variables, so a release can be signed and notarized locally:
export FLASH_SIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)"
export FLASH_HARDENED_RUNTIME=1
export FLASH_NOTARIZE=1
export FLASH_NOTARY_KEY_PATH="$HOME/.private_keys/AuthKey_XXXXXXXXXX.p8"
export FLASH_NOTARY_KEY_ID=XXXXXXXXXX
export FLASH_NOTARY_ISSUER_ID=00000000-0000-0000-0000-000000000000
./Scripts/build.sh --release # build/Flash.zip, stapled
FLASH_NOTARIZE=1 refuses to run without a real identity and the hardened
runtime: Apple rejects ad-hoc or unhardened binaries. When notarization is
refused, the script prints Apple’s log, which names the offending file and
reason.
sign_app in Scripts/_common.sh signs every bundled plugin executable and
then the app, with the hardened runtime, a secure timestamp and the
entitlements in Resources/Flash.entitlements (AppleEvents only; Flash
cannot be sandboxed, so the Mac App Store is out of reach by design). The
dev build keeps its self-signed “Flash Dev” identity so Accessibility and
Automation grants survive rebuilds.