Staple notarization ticket to .dmg and .app bundle (#7775)

Antonio Scandurra , Thorsten , bennetbo , Martin Palma , and evrsen created

This should eliminate a pretty significant (multiple seconds) slowdown
that new users (or users after restarting their OS) have been
experiencing.

Previously, we would just notarize the application, which meant that
every user of the application had to perform an integrity check against
Apple's servers to ensure the app wasn't malicious.

With this commit, we are now using `xcrun stapler staple`, which
attaches the notarization ticket to both the app bundle as well as the
DMG. This should prevent users from needing to reach out to Apple's
notarization service in order to verify the app's integrity.

You can confirm the quarantine status of the application by running `ls
-l@` in `Terminal.app`:

    ls -l@ /Applications/Zed.app/Contents/MacOS/zed

Release Notes:

- Improved startup time when opening Zed for the first time or after
restarting the operating system.

Co-authored-by: Thorsten <thorsten@zed.dev>
Co-authored-by: bennetbo <bennetbo@gmx.de>
Co-authored-by: Martin Palma <m@palma.bz>
Co-authored-by: evrsen <146845123+evrsen@users.noreply.github.com>

Change summary

script/bundle | 42 +++++++++++++++++++++++++++++++-----------
1 file changed, 31 insertions(+), 11 deletions(-)

Detailed changes

script/bundle 🔗

@@ -218,17 +218,17 @@ if [[ "$target_dir" = "debug" && "$local_only" = false ]]; then
     exit 0
 fi
 
-if [ "$local_only" = true ]; then
-    # If bundle_name is not set or empty, use the basename of $app_path
-    if [ -z "$bundle_name" ]; then
-        bundle_name=$(basename "$app_path")
-    else
-        # If bundle_name doesn't end in .app, append it
-        if [[ "$bundle_name" != *.app ]]; then
-            bundle_name="$bundle_name.app"
-        fi
+# If bundle_name is not set or empty, use the basename of $app_path
+if [ -z "$bundle_name" ]; then
+    bundle_name=$(basename "$app_path")
+else
+    # If bundle_name doesn't end in .app, append it
+    if [[ "$bundle_name" != *.app ]]; then
+        bundle_name="$bundle_name.app"
     fi
+fi
 
+if [ "$local_only" = true ]; then
     if [ "$overwrite_local_app" = true ]; then
         rm -rf "/Applications/$bundle_name"
     fi
@@ -241,19 +241,38 @@ if [ "$local_only" = true ]; then
         echo "/Applications/$bundle_name"
     fi
 else
-    echo "Creating DMG"
     dmg_target_directory="target/${target_dir}"
     dmg_source_directory="${dmg_target_directory}/dmg"
     dmg_file_path="${dmg_target_directory}/Zed.dmg"
+    xcode_bin_dir_path="$(xcode-select -p)/usr/bin"
 
     rm -rf ${dmg_source_directory}
     mkdir -p ${dmg_source_directory}
     mv "${app_path}" "${dmg_source_directory}"
 
+    if [[ -n $MACOS_CERTIFICATE && -n $MACOS_CERTIFICATE_PASSWORD && -n $APPLE_NOTARIZATION_USERNAME && -n $APPLE_NOTARIZATION_PASSWORD ]]; then
+        echo "Creating temporary DMG at ${dmg_file_path} using ${dmg_source_directory} to notarize app bundle"
+        hdiutil create -volname Zed -srcfolder "${dmg_source_directory}" -ov -format UDZO "${dmg_file_path}"
+
+        echo "Notarizing DMG with Apple"
+        "${xcode_bin_dir_path}/notarytool" submit --wait --apple-id "$APPLE_NOTARIZATION_USERNAME" --password "$APPLE_NOTARIZATION_PASSWORD" --team-id "$APPLE_NOTORIZATION_TEAM" "${dmg_file_path}"
+
+        echo "Removing temporary DMG (used only for notarization)"
+        rm "${dmg_file_path}"
+
+        echo "Stapling notarization ticket to ${dmg_source_directory}/${bundle_name}"
+        "${xcode_bin_dir_path}/stapler" staple "${dmg_source_directory}/${bundle_name}"
+    fi
+
+    echo "Adding symlink to /Applications to ${dmg_source_directory}"
     ln -s /Applications ${dmg_source_directory}
+
+    echo "Creating final DMG at ${dmg_file_path} using ${dmg_source_directory}"
     hdiutil create -volname Zed -srcfolder "${dmg_source_directory}" -ov -format UDZO "${dmg_file_path}"
+
     # If someone runs this bundle script locally, a symlink will be placed in `dmg_source_directory`.
     # This symlink causes CPU issues with Zed if the Zed codebase is the project being worked on, so we simply remove it for now.
+    echo "Removing symlink to /Applications from ${dmg_source_directory}"
     rm ${dmg_source_directory}/Applications
 
     echo "Adding license agreement to DMG"
@@ -262,7 +281,8 @@ else
 
     if [[ -n $MACOS_CERTIFICATE && -n $MACOS_CERTIFICATE_PASSWORD && -n $APPLE_NOTARIZATION_USERNAME && -n $APPLE_NOTARIZATION_PASSWORD ]]; then
         echo "Notarizing DMG with Apple"
-        "$(xcode-select -p)/usr/bin/notarytool" submit --wait --apple-id "$APPLE_NOTARIZATION_USERNAME" --password "$APPLE_NOTARIZATION_PASSWORD" --team-id "$APPLE_NOTORIZATION_TEAM" "${dmg_file_path}"
+        "${xcode_bin_dir_path}/notarytool" submit --wait --apple-id "$APPLE_NOTARIZATION_USERNAME" --password "$APPLE_NOTARIZATION_PASSWORD" --team-id "$APPLE_NOTORIZATION_TEAM" "${dmg_file_path}"
+        "${xcode_bin_dir_path}/stapler" staple "${dmg_file_path}"
     fi
 
     if [ "$open_result" = true ]; then