
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>
291 lines
11 KiB
Bash
Executable file
291 lines
11 KiB
Bash
Executable file
#!/bin/bash
|
|
|
|
set -e
|
|
|
|
build_flag="--release"
|
|
target_dir="release"
|
|
open_result=false
|
|
local_arch=false
|
|
local_only=false
|
|
overwrite_local_app=false
|
|
bundle_name=""
|
|
zed_crate="zed"
|
|
binary_name="Zed"
|
|
|
|
# This must match the team in the provisioning profile.
|
|
APPLE_NOTORIZATION_TEAM="MQ55VZLNZQ"
|
|
|
|
# Function for displaying help info
|
|
help_info() {
|
|
echo "
|
|
Usage: ${0##*/} [options] [bundle_name]
|
|
Build the application bundle.
|
|
|
|
Options:
|
|
-d Compile in debug mode
|
|
-l Compile for local architecture and copy bundle to /Applications, implies -d.
|
|
-o Open the resulting DMG or the app itself in local mode.
|
|
-f Overwrite the local app bundle if it exists.
|
|
-h Display this help and exit.
|
|
"
|
|
}
|
|
|
|
function uploadDsym
|
|
{
|
|
SPACE="zed-debug-symbols"
|
|
REGION="nyc3"
|
|
file_to_upload="$1"
|
|
file_name="$2"
|
|
date=$(date +"%a, %d %b %Y %T %z")
|
|
acl="x-amz-acl:public-read"
|
|
content_type="application/octet-stream"
|
|
storage_type="x-amz-storage-class:STANDARD"
|
|
string="PUT\n\n${content_type}\n${date}\n${acl}\n${storage_type}\n/${SPACE}/${space_path}/${file_name}"
|
|
signature=$(echo -en "${string}" | openssl sha1 -hmac "${DIGITALOCEAN_SPACES_SECRET_KEY}" -binary | base64)
|
|
|
|
curl --fail -vv -s -X PUT -T "$file_to_upload" \
|
|
-H "Host: ${SPACE}.${REGION}.digitaloceanspaces.com" \
|
|
-H "Date: $date" \
|
|
-H "Content-Type: $content_type" \
|
|
-H "$storage_type" \
|
|
-H "$acl" \
|
|
-H "Authorization: AWS ${DIGITALOCEAN_SPACES_ACCESS_KEY}:$signature" \
|
|
"https://${SPACE}.${REGION}.digitaloceanspaces.com/${file_name}"
|
|
}
|
|
|
|
while getopts 'dlfoh' flag
|
|
do
|
|
case "${flag}" in
|
|
o) open_result=true;;
|
|
d)
|
|
export CARGO_INCREMENTAL=true
|
|
export CARGO_BUNDLE_SKIP_BUILD=true
|
|
build_flag="";
|
|
target_dir="debug"
|
|
;;
|
|
l)
|
|
export CARGO_INCREMENTAL=true
|
|
export CARGO_BUNDLE_SKIP_BUILD=true
|
|
build_flag=""
|
|
local_arch=true
|
|
local_only=true
|
|
target_dir="debug"
|
|
;;
|
|
f) overwrite_local_app=true;;
|
|
h)
|
|
help_info
|
|
exit 0
|
|
;;
|
|
esac
|
|
done
|
|
|
|
shift $((OPTIND-1))
|
|
|
|
if [ "$1" ]; then
|
|
bundle_name=$1
|
|
fi
|
|
|
|
export ZED_BUNDLE=true
|
|
export MACOSX_DEPLOYMENT_TARGET=10.15.7
|
|
|
|
cargo_bundle_version=$(cargo -q bundle --help 2>&1 | head -n 1 || echo "")
|
|
if [ "$cargo_bundle_version" != "cargo-bundle v0.6.0-zed" ]; then
|
|
cargo install cargo-bundle --git https://github.com/zed-industries/cargo-bundle.git --branch zed-deploy
|
|
fi
|
|
|
|
rustup target add wasm32-wasi
|
|
|
|
# Deal with versions of macOS that don't include libstdc++ headers
|
|
export CXXFLAGS="-stdlib=libc++"
|
|
|
|
version_info=$(rustc --version --verbose)
|
|
host_line=$(echo "$version_info" | grep host)
|
|
local_target_triple=${host_line#*: }
|
|
|
|
if [ "$local_arch" = true ]; then
|
|
echo "Building for local target only."
|
|
cargo build ${build_flag} --package ${zed_crate}
|
|
cargo build ${build_flag} --package cli
|
|
else
|
|
echo "Compiling zed binaries"
|
|
cargo build ${build_flag} --package ${zed_crate} --package cli --target aarch64-apple-darwin --target x86_64-apple-darwin
|
|
fi
|
|
|
|
echo "Creating application bundle"
|
|
pushd crates/zed
|
|
channel=$(<RELEASE_CHANNEL)
|
|
popd
|
|
|
|
pushd crates/${zed_crate}
|
|
cp Cargo.toml Cargo.toml.backup
|
|
sed \
|
|
-i .backup \
|
|
"s/package.metadata.bundle-${channel}/package.metadata.bundle/" \
|
|
Cargo.toml
|
|
|
|
if [ "$local_arch" = true ]; then
|
|
app_path=$(cargo bundle ${build_flag} --select-workspace-root | xargs)
|
|
else
|
|
app_path=$(cargo bundle ${build_flag} --target x86_64-apple-darwin --select-workspace-root | xargs)
|
|
fi
|
|
|
|
mv Cargo.toml.backup Cargo.toml
|
|
popd
|
|
echo "Bundled ${app_path}"
|
|
|
|
if [ "$local_arch" = false ]; then
|
|
echo "Uploading dSYMs"
|
|
dsymutil --flat target/aarch64-apple-darwin/release/Zed
|
|
dsymutil --flat target/x86_64-apple-darwin/release/Zed
|
|
version="$(cargo metadata --no-deps --manifest-path crates/zed/Cargo.toml --offline --format-version=1 | jq -r '.packages | map(select(.name == "zed"))[0].version')"
|
|
if [ "$channel" == "nightly" ]; then
|
|
version="$version-$(git rev-parse --short HEAD)"
|
|
fi
|
|
gzip target/aarch64-apple-darwin/release/Zed.dwarf
|
|
gzip target/x86_64-apple-darwin/release/Zed.dwarf
|
|
uploadDsym target/aarch64-apple-darwin/release/Zed.dwarf.gz "$channel/Zed-$version-aarch64-apple-darwin.dwarf.gz"
|
|
uploadDsym target/x86_64-apple-darwin/release/Zed.dwarf.gz "$channel/Zed-$version-x86_64-apple-darwin.dwarf.gz"
|
|
|
|
echo "Creating fat binaries"
|
|
lipo \
|
|
-create \
|
|
target/{x86_64-apple-darwin,aarch64-apple-darwin}/${target_dir}/${binary_name} \
|
|
-output \
|
|
"${app_path}/Contents/MacOS/${zed_crate}"
|
|
lipo \
|
|
-create \
|
|
target/{x86_64-apple-darwin,aarch64-apple-darwin}/${target_dir}/cli \
|
|
-output \
|
|
"${app_path}/Contents/MacOS/cli"
|
|
fi
|
|
|
|
echo "Copying WebRTC.framework into the frameworks folder"
|
|
mkdir "${app_path}/Contents/Frameworks"
|
|
if [ "$local_arch" = false ]; then
|
|
cp -R target/${local_target_triple}/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/"
|
|
else
|
|
cp -R target/${target_dir}/WebRTC.framework "${app_path}/Contents/Frameworks/"
|
|
fi
|
|
|
|
# Note: The app identifier for our development builds is the same as the app identifier for nightly.
|
|
cp crates/${zed_crate}/contents/$channel/embedded.provisionprofile "${app_path}/Contents/"
|
|
|
|
if [[ -n $MACOS_CERTIFICATE && -n $MACOS_CERTIFICATE_PASSWORD && -n $APPLE_NOTARIZATION_USERNAME && -n $APPLE_NOTARIZATION_PASSWORD ]]; then
|
|
echo "Signing bundle with Apple-issued certificate"
|
|
security create-keychain -p "$MACOS_CERTIFICATE_PASSWORD" zed.keychain || echo ""
|
|
security default-keychain -s zed.keychain
|
|
security unlock-keychain -p "$MACOS_CERTIFICATE_PASSWORD" zed.keychain
|
|
echo "$MACOS_CERTIFICATE" | base64 --decode > /tmp/zed-certificate.p12
|
|
security import /tmp/zed-certificate.p12 -k zed.keychain -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
|
|
rm /tmp/zed-certificate.p12
|
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CERTIFICATE_PASSWORD" zed.keychain
|
|
|
|
# sequence of codesign commands modeled after this example: https://developer.apple.com/forums/thread/701514
|
|
/usr/bin/codesign --deep --force --timestamp --sign "Zed Industries, Inc." "${app_path}/Contents/Frameworks/WebRTC.framework" -v
|
|
/usr/bin/codesign --deep --force --timestamp --options runtime --sign "Zed Industries, Inc." "${app_path}/Contents/MacOS/cli" -v
|
|
/usr/bin/codesign --deep --force --timestamp --options runtime --entitlements crates/${zed_crate}/resources/zed.entitlements --sign "Zed Industries, Inc." "${app_path}/Contents/MacOS/${zed_crate}" -v
|
|
/usr/bin/codesign --force --timestamp --options runtime --entitlements crates/${zed_crate}/resources/zed.entitlements --sign "Zed Industries, Inc." "${app_path}" -v
|
|
|
|
security default-keychain -s login.keychain
|
|
else
|
|
echo "One or more of the following variables are missing: MACOS_CERTIFICATE, MACOS_CERTIFICATE_PASSWORD, APPLE_NOTARIZATION_USERNAME, APPLE_NOTARIZATION_PASSWORD"
|
|
if [[ "$local_only" = false ]]; then
|
|
echo "To create a self-signed local build use ./scripts/build.sh -ldf"
|
|
exit 1
|
|
fi
|
|
|
|
echo "====== WARNING ======"
|
|
echo "This bundle is being signed without all entitlements, some features (e.g. universal links) will not work"
|
|
echo "====== WARNING ======"
|
|
|
|
# NOTE: if you need to test universal links you have a few paths forward:
|
|
# - create a PR and tag it with the `run-build-dmg` label, and download the .dmg file from there.
|
|
# - get a signing key for the MQ55VZLNZQ team from Nathan.
|
|
# - create your own signing key, and update references to MQ55VZLNZQ to your own team ID
|
|
# then comment out this line.
|
|
cat crates/${zed_crate}/resources/zed.entitlements | sed '/com.apple.developer.associated-domains/,+1d' > "${app_path}/Contents/Resources/zed.entitlements"
|
|
|
|
codesign --force --deep --entitlements "${app_path}/Contents/Resources/zed.entitlements" --sign ${MACOS_SIGNING_KEY:- -} "${app_path}" -v
|
|
fi
|
|
|
|
if [[ "$target_dir" = "debug" && "$local_only" = false ]]; then
|
|
if [ "$open_result" = true ]; then
|
|
open "$app_path"
|
|
else
|
|
echo "Created application bundle:"
|
|
echo "$app_path"
|
|
fi
|
|
exit 0
|
|
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
|
|
mv "$app_path" "/Applications/$bundle_name"
|
|
|
|
if [ "$open_result" = true ]; then
|
|
open "/Applications/$bundle_name"
|
|
else
|
|
echo "Installed application bundle:"
|
|
echo "/Applications/$bundle_name"
|
|
fi
|
|
else
|
|
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"
|
|
npm install --global dmg-license minimist
|
|
dmg-license script/eula/eula.json "${dmg_file_path}"
|
|
|
|
if [[ -n $MACOS_CERTIFICATE && -n $MACOS_CERTIFICATE_PASSWORD && -n $APPLE_NOTARIZATION_USERNAME && -n $APPLE_NOTARIZATION_PASSWORD ]]; then
|
|
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}"
|
|
"${xcode_bin_dir_path}/stapler" staple "${dmg_file_path}"
|
|
fi
|
|
|
|
if [ "$open_result" = true ]; then
|
|
open $dmg_target_directory
|
|
fi
|
|
fi
|