2121# ./scripts/make-dmg.sh
2222#
2323# Or by hand:
24- # CODESIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)" scripts/notarize.sh sign VSCode-darwin-arm64/LevelCode.app
25- # NOTARY_PROFILE="levelcode-notary" scripts/notarize.sh submit LevelCode-arm64.dmg
24+ # CODESIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)" scripts/notarize.sh sign VSCode-darwin-arm64/LevelCode.app
25+ # NOTARY_PROFILE="levelcode-notary" scripts/notarize.sh notarize-app VSCode-darwin-arm64/LevelCode.app
26+ # NOTARY_PROFILE="levelcode-notary" scripts/notarize.sh submit LevelCode-arm64.dmg
2627#
2728# ── Config (env) ────────────────────────────────────────────────────────────────────────
2829# CODESIGN_IDENTITY (sign) the "Developer ID Application: …" identity string
@@ -89,18 +90,48 @@ sign_app() {
8990 echo " [notarize] signature OK"
9091}
9192
92- submit_dmg () {
93- local DMG=" $1 "
94- [ -f " $DMG " ] || die " dmg not found: $DMG "
93+ # Submit a container (zip / dmg / pkg) to Apple's notary service and block until done.
94+ # notarytool can't ingest a bare .app — it must be wrapped — hence the zip in notarize_app.
95+ _notary_submit () {
96+ local FILE=" $1 "
9597 command -v xcrun > /dev/null || die " xcrun not found (install Xcode command line tools)"
96- echo " [notarize] Submitting $DMG to Apple's notary service (a few minutes) …"
9798 if [ -n " ${NOTARY_PROFILE:- } " ]; then
98- xcrun notarytool submit " $DMG " --keychain-profile " $NOTARY_PROFILE " --wait
99+ xcrun notarytool submit " $FILE " --keychain-profile " $NOTARY_PROFILE " --wait
99100 elif [ -n " ${APPLE_ID:- } " ] && [ -n " ${TEAM_ID:- } " ] && [ -n " ${APP_SPECIFIC_PASSWORD:- } " ]; then
100- xcrun notarytool submit " $DMG " --apple-id " $APPLE_ID " --team-id " $TEAM_ID " --password " $APP_SPECIFIC_PASSWORD " --wait
101+ xcrun notarytool submit " $FILE " --apple-id " $APPLE_ID " --team-id " $TEAM_ID " --password " $APP_SPECIFIC_PASSWORD " --wait
101102 else
102103 die " set NOTARY_PROFILE (from 'xcrun notarytool store-credentials'), or APPLE_ID + TEAM_ID + APP_SPECIFIC_PASSWORD"
103104 fi
105+ }
106+
107+ # Notarize a .app and STAPLE the ticket onto the bundle itself. This is what lets the app
108+ # launch on first run WITHOUT a network round-trip after a user drags it out of the dmg to
109+ # /Applications — the dmg's own stapled ticket does not travel with the extracted app, so an
110+ # offline first launch of an un-stapled app fails Gatekeeper ("cannot be checked for malicious
111+ # software"). Stapling is signature-safe (the ticket is stored outside the sealed contents).
112+ notarize_app () {
113+ local APP=" $1 "
114+ [ -d " $APP " ] || die " app not found: $APP "
115+ command -v xcrun > /dev/null || die " xcrun not found (install Xcode command line tools)"
116+ command -v ditto > /dev/null || die " ditto not found"
117+ local ZIP=" ${TMPDIR:-/ tmp} /$( basename " ${APP% .app} " ) .notarize.$$ .zip"
118+ echo " [notarize] Zipping $APP for notarization …"
119+ rm -f " $ZIP "
120+ ditto -c -k --sequesterRsrc --keepParent " $APP " " $ZIP "
121+ echo " [notarize] Submitting the app to Apple's notary service (a few minutes) …"
122+ _notary_submit " $ZIP "
123+ rm -f " $ZIP "
124+ echo " [notarize] Stapling the ticket onto $APP …"
125+ xcrun stapler staple " $APP "
126+ xcrun stapler validate " $APP "
127+ echo " [notarize] ✅ $APP is notarized and stapled (launches offline on first run)."
128+ }
129+
130+ submit_dmg () {
131+ local DMG=" $1 "
132+ [ -f " $DMG " ] || die " dmg not found: $DMG "
133+ echo " [notarize] Submitting $DMG to Apple's notary service (a few minutes) …"
134+ _notary_submit " $DMG "
104135 echo " [notarize] Stapling the ticket onto $DMG …"
105136 xcrun stapler staple " $DMG "
106137 xcrun stapler validate " $DMG "
@@ -109,7 +140,8 @@ submit_dmg() {
109140
110141cmd=" ${1:- } " ; [ $# -gt 0 ] && shift || true
111142case " $cmd " in
112- sign) [ $# -ge 1 ] || die " usage: notarize.sh sign <LevelCode.app>" ; sign_app " $1 " ;;
113- submit) [ $# -ge 1 ] || die " usage: notarize.sh submit <LevelCode.dmg>" ; submit_dmg " $1 " ;;
114- * ) echo " usage: $( basename " $0 " ) {sign <LevelCode.app> | submit <LevelCode.dmg>}" ; exit 2 ;;
143+ sign) [ $# -ge 1 ] || die " usage: notarize.sh sign <LevelCode.app>" ; sign_app " $1 " ;;
144+ notarize-app) [ $# -ge 1 ] || die " usage: notarize.sh notarize-app <LevelCode.app>" ; notarize_app " $1 " ;;
145+ submit) [ $# -ge 1 ] || die " usage: notarize.sh submit <LevelCode.dmg>" ; submit_dmg " $1 " ;;
146+ * ) echo " usage: $( basename " $0 " ) {sign <LevelCode.app> | notarize-app <LevelCode.app> | submit <LevelCode.dmg>}" ; exit 2 ;;
115147esac
0 commit comments