OpenClicky is distributed outside the Mac App Store using a Developer ID signed DMG, notarized by Apple and delivered to users through Sparkle OTA updates. The project ships with a set of release scripts underDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/jasonkneen/openclicky/llms.txt
Use this file to discover all available pages before exploring further.
scripts/ that automate the most error-prone steps — version bumping, archiving, packaging, notarizing, stapling, and appcast generation — while leaving the final GitHub upload and git push as intentional manual steps so you review the artifact before it reaches users.
Prerequisites
Before your first release, complete these one-time machine setup steps:-
Developer ID certificate — install your
Developer ID Application: Jason Kneen (SW75ZJJ5R6)certificate into the login keychain. Verify it is present with: -
App-specific password — generate one at appleid.apple.com under Sign-In and Security → App-Specific Passwords using the Apple ID associated with your developer account (
jason.knen@bouncingfish.com). Label it something recognizable, for exampleOpenClicky Notary. -
Notary keychain profile — store the credentials once so the release script can submit without prompting:
The profile name
OpenClickyNotarymatches theNOTARY_PROFILEconstant inscripts/release.sh. If you choose a different name, update both. -
Sparkle private key — the
sign_updatetool reads the EdDSA private key from theopenclickyaccount in your Keychain. Confirm the matching public key is set asSUPublicEDKeyincursor-buddy/Info.plistbefore your first signed release.
Full Release Flow
Update
MARKETING_VERSION and CURRENT_PROJECT_VERSION in the Xcode project. Use the helper script to do this from the terminal — it updates every occurrence of MARKETING_VERSION in cursor-buddy.xcodeproj/project.pbxproj and delegates build-number management to agvtool:# Bump marketing version to 1.1.0 and auto-increment the build number
scripts/bump-version.sh 1.1.0
# Or supply both explicitly
scripts/bump-version.sh 1.1.0 7
# Check current values without changing anything
scripts/bump-version.sh --show
You can also change the values directly in Xcode’s Build Settings for the
cursor-buddy target if you prefer a GUI.In Xcode, select the
cursor-buddy scheme and a Generic macOS Device destination, then choose Product → Archive. The archive will appear in the Xcode Organizer.If you use
scripts/release.sh (see Automated Pipeline below), xcodebuild handles the archive step for you:xcodebuild \
-project cursor-buddy.xcodeproj \
-scheme cursor-buddy \
-configuration Release \
-destination "generic/platform=macOS" \
-archivePath build/OpenClicky.xcarchive \
MARKETING_VERSION="1.1.0" \
CURRENT_PROJECT_VERSION="7" \
archive
From the Xcode Organizer, click Distribute App, choose Direct Distribution, and follow the export wizard. The resulting
.app is signed with your Developer ID Application certificate.When driven by script,
xcodebuild -exportArchive reads scripts/ExportOptions.plist, which specifies method=developer-id. The script verifies this value and exits if it has been changed:xcodebuild \
-exportArchive \
-archivePath build/OpenClicky.xcarchive \
-exportPath build/Export \
-exportOptionsPlist scripts/ExportOptions.plist
codesign --verify --deep --strict --verbose=2 build/Export/OpenClicky.app
spctl --assess --type execute --verbose=2 build/Export/OpenClicky.app
mkdir -p build/dmg-stage
cp -R build/Export/OpenClicky.app build/dmg-stage/
ln -s /Applications build/dmg-stage/Applications
hdiutil create \
-volname "OpenClicky 1.1.0" \
-srcfolder build/dmg-stage \
-ov \
-format UDZO \
dist/OpenClicky-1.1.0-7.dmg
codesign --sign "Developer ID Application: Jason Kneen (SW75ZJJ5R6)" \
--timestamp \
dist/OpenClicky-1.1.0-7.dmg
Submit the signed DMG to Apple’s notary service using the keychain profile created during setup. The
--wait flag blocks until Apple returns a result:xcrun notarytool submit dist/OpenClicky-1.1.0-7.dmg \
--keychain-profile "OpenClickyNotary" \
--wait
xcrun stapler staple dist/OpenClicky-1.1.0-7.dmg
xcrun stapler validate dist/OpenClicky-1.1.0-7.dmg
Sign the DMG with Sparkle’s
sign_update only after stapling. The EdDSA signature must cover the final stapled bytes. Signing an earlier, unstapled copy and then stapling will invalidate the Sparkle signature.Sparkle verifies every update DMG against the EdDSA signature in
appcast.xml. Generate the signature using Sparkle’s sign_update binary, which reads the private key from the openclicky Keychain account:sign_update prints an <enclosure> snippet containing sparkle:edSignature and length. Keep both values — you will need them in the next step.If
sign_update is not on your PATH, the generate-appcast.sh script searches for it automatically inside Xcode’s DerivedData for the cursor-buddy project. Build the project in Xcode at least once to populate DerivedData, or install via:gh release create v1.1.0 dist/OpenClicky-1.1.0-7.dmg \
--title "OpenClicky 1.1.0" \
--notes "Release notes here"
Add a new
<item> block to appcast.xml in the repository root. Replace all placeholder values with the real version, build number, publication date, download URL, byte length, and Sparkle signature:<item>
<title>OpenClicky 1.1.0</title>
<pubDate>Fri, 24 Apr 2026 16:10:08 +0000</pubDate>
<sparkle:version>7</sparkle:version>
<sparkle:shortVersionString>1.1.0</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>14.2</sparkle:minimumSystemVersion>
<enclosure
url="https://github.com/jasonkneen/openclicky/releases/download/v1.1.0/OpenClicky-1.1.0-7.dmg"
length="12345678"
type="application/octet-stream"
sparkle:edSignature="SIGNATURE_FROM_SPARKLE_SIGN_UPDATE"/>
</item>
scripts/generate-appcast.sh automates this step (see Automated Pipeline below). It locates sign_update, generates the signature, reads version and minimum OS from the project file, and rewrites appcast.xml in one pass.Sparkle reads the feed directly from the
main branch on GitHub. Pushing appcast.xml is what makes the update visible to installed builds:Automated Pipeline
The scripts inscripts/ can run the entire flow with a single command.
scripts/ship.sh — end-to-end release
ship.sh chains bump-version.sh → release.sh → generate-appcast.sh and prints the remaining manual steps (GitHub upload and git push) at the end:
ship.sh completes, the output lists the exact gh release create and git commands to run.
scripts/release.sh — archive, sign, notarize
Run release.sh alone if you have already bumped the version and only need the build/notarize step:
dist/OpenClicky-<version>-<build>.dmg.
scripts/generate-appcast.sh — Sparkle signing and appcast
Run generate-appcast.sh after you have a notarized and stapled DMG:
MARKETING_VERSION, CURRENT_PROJECT_VERSION, and MACOSX_DEPLOYMENT_TARGET from the Xcode project file and writes a complete appcast.xml.
Post-Release Verification Checklist
Before declaring a release complete, confirm:- The DMG opens without Gatekeeper warnings on a clean Mac.
spctlaccepts the DMG:- The appcast URL is reachable over HTTPS.
- The new appcast item has a higher
sparkle:version(build number) than the currently installed build. - The
urlin the appcast<enclosure>exactly matches the uploaded GitHub Release asset URL. sparkle:edSignaturewas generated from the final stapled DMG, not an earlier copy.- A clean installed build checks the feed and offers or stages the update without requiring the user to visit GitHub.