From 8657effa57a11834e1134c7ac016f54b07f5b251 Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Tue, 28 Oct 2025 12:54:58 +0100 Subject: [PATCH] WIP: Notarize Mac builds in parallel --- .github/workflows/ci.yml | 53 +++++-- script/build-mac | 155 +++++++++++++++++++ script/bundle-mac | 318 ++------------------------------------- script/notarize-mac | 255 +++++++++++++++++++++++++++++++ 4 files changed, 459 insertions(+), 322 deletions(-) create mode 100755 script/build-mac create mode 100755 script/notarize-mac diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e594cdcfff4e5ba2383cee4d2b4551ea86d9e8d8..f67a447cbab9f0536fd86474632b2fb24bde65a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -511,27 +511,16 @@ jobs: fi exit $RET_CODE - bundle-mac: + release-build-mac: timeout-minutes: 120 - name: Create a macOS bundle + name: Build optimized macOS artifacts runs-on: - self-mini-macos if: | ( startsWith(github.ref, 'refs/tags/v') || contains(github.event.pull_request.labels.*.name, 'run-bundling') ) needs: [macos_tests] - env: - MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} - MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} - APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }} - APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }} - APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }} steps: - - name: Install Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 - with: - node-version: "18" - - name: Setup Sentry CLI uses: matbour/setup-sentry-cli@3e938c54b3018bdd019973689ef984e033b0454b #v2 with: @@ -568,7 +557,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Create macOS app bundle - run: script/bundle-mac + run: script/build-mac - name: Rename binaries if: ${{ github.ref == 'refs/heads/main' }} || contains(github.event.pull_request.labels.*.name, 'run-bundling') }} @@ -590,6 +579,42 @@ jobs: name: Zed_${{ github.event.pull_request.head.sha || github.sha }}-x86_64.dmg path: target/x86_64-apple-darwin/release/Zed-x86_64.dmg + notarize-mac: + name: Notarize macOS binaries + timeout-minutes: 120 + runs-on: + - self-mini-macos + if: | + ( startsWith(github.ref, 'refs/tags/v') + || contains(github.event.pull_request.labels.*.name, 'run-bundling') ) + needs: [release-build-mac] + strategy: + matrix: + target: [aarch64-apple-darwin, x86_64-apple-darwin] + env: + MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} + MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} + APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }} + APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }} + APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }} + + steps: + - name: Install Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: "18" + - name: Notarize ${{ matrix.target }} + run: ./script/notarize-mac ${{ matrix.target }} + + bundle-mac: + name: Create macOS release + timeout-minutes: 60 + needs: [notarize-mac] + runs-on: self-mini-macos + if: | + ( startsWith(github.ref, 'refs/tags/v') + || contains(github.event.pull_request.labels.*.name, 'run-bundling') ) + steps: - uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v1 name: Upload app bundle to release if: ${{ env.RELEASE_CHANNEL == 'preview' || env.RELEASE_CHANNEL == 'stable' }} diff --git a/script/build-mac b/script/build-mac new file mode 100755 index 0000000000000000000000000000000000000000..0a7d0c1414269746bc2ac99a3b7a9b612b2a9e00 --- /dev/null +++ b/script/build-mac @@ -0,0 +1,155 @@ +#!/usr/bin/env bash + +set -euo pipefail +source script/lib/blob-store.sh + +build_flag="--release" +target_dir="release" +local_arch=false +local_only=false +local_install=false +bundle_name="" + +# This must match the team in the provisioning profile. +IDENTITY="Zed Industries, Inc." +APPLE_NOTARIZATION_TEAM="MQ55VZLNZQ" + +# Function for displaying help info +help_info() { + echo " +Usage: ${0##*/} [options] [bundle_name] +Build the application bundle for macOS. + +Options: + -d Compile in debug mode + -l Compile for local architecture only. + -i Install the resulting DMG into /Applications in local mode. Noop without -l. + -h Display this help and exit. + " +} + +function prepare_binaries() { + local architecture=$1 + local app_path=$2 + + cp target/${architecture}/${target_dir}/zed "${app_path}/Contents/MacOS/zed" + cp target/${architecture}/${target_dir}/cli "${app_path}/Contents/MacOS/cli" +} + +while getopts 'dlih' flag +do + case "${flag}" in + 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 + local_arch=true + local_only=true + ;; + i) local_install=true;; + h) + help_info + exit 0 + ;; + esac +done + +shift $((OPTIND-1)) + +if [[ $# -gt 0 ]]; then + if [ "$1" ]; then + bundle_name=$1 + fi +fi + +# Get release channel +pushd crates/zed +channel=$(&1 | head -n 1 || echo "") +if [ "$cargo_bundle_version" != "cargo-bundle v0.6.1-zed" ]; then + cargo install cargo-bundle --git https://github.com/zed-industries/cargo-bundle.git --branch zed-deploy +fi + +# 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#*: } + +# Generate the licenses first, so they can be baked into the binaries +script/generate-licenses + +if [ "$local_arch" = true ]; then + echo "Building for local target only." + cargo build ${build_flag} --package zed --package cli --package remote_server +else + rustup target add aarch64-apple-darwin + rustup target add x86_64-apple-darwin + + echo "Compiling zed binaries" + cargo build ${build_flag} --package zed --package cli --target aarch64-apple-darwin --target x86_64-apple-darwin + # Build remote_server in separate invocation to prevent feature unification from other crates + # from influencing dynamic libraries required by it. + cargo build ${build_flag} --package remote_server --target aarch64-apple-darwin --target x86_64-apple-darwin +fi + +echo "Creating application bundle" +pushd crates/zed +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_x64=$(cargo bundle ${build_flag} --target x86_64-apple-darwin --select-workspace-root | xargs) + app_path_aarch64=$(cargo bundle ${build_flag} --target aarch64-apple-darwin --select-workspace-root | xargs) + app_path=$app_path_x64 +fi + +if [ "$local_arch" = true ]; then + cp -R target/${target_dir}/cli "${app_path}/Contents/MacOS/" +else + prepare_binaries "aarch64-apple-darwin" "$app_path_aarch64" + prepare_binaries "x86_64-apple-darwin" "$app_path_x64" +fi + +mv Cargo.toml.backup Cargo.toml +popd +echo "Built ${app_path}" + +function upload_debug_info() { + architecture=$1 + if [[ -n "${SENTRY_AUTH_TOKEN:-}" ]]; then + echo "Uploading zed debug symbols to sentry..." + # note: this uploads the unstripped binary which is needed because it contains + # .eh_frame data for stack unwinding. see https://github.com/getsentry/symbolic/issues/783 + sentry-cli debug-files upload --include-sources --wait -p zed -o zed-dev \ + "target/${architecture}/${target_dir}/zed" \ + "target/${architecture}/${target_dir}/remote_server" \ + "target/${architecture}/${target_dir}/zed.dwarf" + else + echo "missing SENTRY_AUTH_TOKEN. skipping sentry upload." + fi +} + +if command -v sentry-cli >/dev/null 2>&1; then + upload_debug_info "aarch64-apple-darwin" + upload_debug_info "x86_64-apple-darwin" +else + echo "sentry-cli not found. skipping sentry upload." + echo "install with: 'curl -sL https://sentry.io/get-cli | bash'" +fi diff --git a/script/bundle-mac b/script/bundle-mac index 8f13b347b84fcc652227ea98633995387684a992..e005c300acdfbdda17ca23b38474e2f971373554 100755 --- a/script/bundle-mac +++ b/script/bundle-mac @@ -7,14 +7,9 @@ build_flag="--release" target_dir="release" open_result=false local_arch=false -local_only=false local_install=false bundle_name="" -can_code_sign=false -# This must match the team in the provisioning profile. -IDENTITY="Zed Industries, Inc." -APPLE_NOTARIZATION_TEAM="MQ55VZLNZQ" # Function for displaying help info help_info() { @@ -31,21 +26,26 @@ Options: " } +build_args="" while getopts 'dloih' flag do case "${flag}" in - o) open_result=true;; + o) open_result=true + build_args="${build_args} -o" + ;; d) export CARGO_INCREMENTAL=true export CARGO_BUNDLE_SKIP_BUILD=true build_flag=""; target_dir="debug" + build_args="${build_args} -d" ;; l) export CARGO_INCREMENTAL=true export CARGO_BUNDLE_SKIP_BUILD=true local_arch=true local_only=true + build_args="${build_args} -l" ;; i) local_install=true;; h) @@ -63,307 +63,9 @@ if [[ $# -gt 0 ]]; then fi fi -# Get release channel -pushd crates/zed -channel=$(&1 | head -n 1 || echo "") -if [ "$cargo_bundle_version" != "cargo-bundle v0.6.1-zed" ]; then - cargo install cargo-bundle --git https://github.com/zed-industries/cargo-bundle.git --branch zed-deploy -fi - -# 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#*: } - -# Generate the licenses first, so they can be baked into the binaries -script/generate-licenses - -if [ "$local_arch" = true ]; then - echo "Building for local target only." - cargo build ${build_flag} --package zed --package cli --package remote_server -else - rustup target add aarch64-apple-darwin - rustup target add x86_64-apple-darwin - - echo "Compiling zed binaries" - cargo build ${build_flag} --package zed --package cli --target aarch64-apple-darwin --target x86_64-apple-darwin - # Build remote_server in separate invocation to prevent feature unification from other crates - # from influencing dynamic libraries required by it. - cargo build ${build_flag} --package remote_server --target aarch64-apple-darwin --target x86_64-apple-darwin -fi - -echo "Creating application bundle" -pushd crates/zed -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_x64=$(cargo bundle ${build_flag} --target x86_64-apple-darwin --select-workspace-root | xargs) - app_path_aarch64=$(cargo bundle ${build_flag} --target aarch64-apple-darwin --select-workspace-root | xargs) - app_path=$app_path_x64 -fi - -mv Cargo.toml.backup Cargo.toml -popd -echo "Bundled ${app_path}" - -if [[ -n "${MACOS_CERTIFICATE:-}" && -n "${MACOS_CERTIFICATE_PASSWORD:-}" && -n "${APPLE_NOTARIZATION_KEY:-}" && -n "${APPLE_NOTARIZATION_KEY_ID:-}" && -n "${APPLE_NOTARIZATION_ISSUER_ID:-}" ]]; then - can_code_sign=true - - echo "Setting up keychain for code signing..." - 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 - - function cleanup() { - echo "Cleaning up keychain" - security default-keychain -s login.keychain - security delete-keychain zed.keychain - } - - trap cleanup EXIT -fi - -GIT_VERSION="v2.43.3" -GIT_VERSION_SHA="fa29823" - -function download_and_unpack() { - local url=$1 - local path_to_unpack=$2 - local target_path=$3 - - temp_dir=$(mktemp -d) - - if ! command -v curl &> /dev/null; then - echo "curl is not installed. Please install curl to continue." - exit 1 - fi - - curl --silent --fail --location "$url" | tar -xvz -C "$temp_dir" -f - $path_to_unpack - - mv "$temp_dir/$path_to_unpack" "$target_path" - - rm -rf "$temp_dir" -} - -function download_git() { - local architecture=$1 - local target_binary=$2 - - tmp_dir=$(mktemp -d) - pushd "$tmp_dir" - - case "$architecture" in - aarch64-apple-darwin) - download_and_unpack "https://github.com/desktop/dugite-native/releases/download/${GIT_VERSION}/dugite-native-${GIT_VERSION}-${GIT_VERSION_SHA}-macOS-arm64.tar.gz" bin/git ./git - ;; - x86_64-apple-darwin) - download_and_unpack "https://github.com/desktop/dugite-native/releases/download/${GIT_VERSION}/dugite-native-${GIT_VERSION}-${GIT_VERSION_SHA}-macOS-x64.tar.gz" bin/git ./git - ;; - *) - echo "Unsupported architecture: $architecture" - exit 1 - ;; - esac - - popd - - mv "${tmp_dir}/git" "${target_binary}" - rm -rf "$tmp_dir" -} - -function prepare_binaries() { - local architecture=$1 - local app_path=$2 - - cp target/${architecture}/${target_dir}/zed "${app_path}/Contents/MacOS/zed" - cp target/${architecture}/${target_dir}/cli "${app_path}/Contents/MacOS/cli" -} - -function sign_app_binaries() { - local app_path=$1 - local architecture=$2 - local architecture_dir=$3 - rm -rf "${app_path}/Contents/Frameworks" - mkdir -p "${app_path}/Contents/Frameworks" - if [ "$local_arch" = true ]; then - cp -R target/${target_dir}/cli "${app_path}/Contents/MacOS/" - fi - - echo "Downloading git binary" - download_git "${architecture}" "${app_path}/Contents/MacOS/git" - - # Note: The app identifier for our development builds is the same as the app identifier for nightly. - cp crates/zed/contents/$channel/embedded.provisionprofile "${app_path}/Contents/" - - if [[ $can_code_sign = true ]]; then - echo "Code signing binaries" - # sequence of codesign commands modeled after this example: https://developer.apple.com/forums/thread/701514 - /usr/bin/codesign --deep --force --timestamp --options runtime --sign "$IDENTITY" "${app_path}/Contents/MacOS/cli" -v - /usr/bin/codesign --deep --force --timestamp --options runtime --sign "$IDENTITY" "${app_path}/Contents/MacOS/git" -v - /usr/bin/codesign --deep --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "$IDENTITY" "${app_path}/Contents/MacOS/zed" -v - /usr/bin/codesign --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "$IDENTITY" "${app_path}" -v - else - echo "One or more of the following variables are missing: MACOS_CERTIFICATE, MACOS_CERTIFICATE_PASSWORD, APPLE_NOTARIZATION_KEY, APPLE_NOTARIZATION_KEY_ID, APPLE_NOTARIZATION_ISSUER_ID" - 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-bundling` 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/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 [ "$local_install" = true ]; then - rm -rf "/Applications/$bundle_name" - mv "$app_path" "/Applications/$bundle_name" - echo "Installed application bundle: /Applications/$bundle_name" - if [ "$open_result" = true ]; then - echo "Opening /Applications/$bundle_name" - open "/Applications/$bundle_name" - fi - else - if [ "$open_result" = true ]; then - echo "Opening $app_path" - open "$app_path" - fi - fi - else - dmg_target_directory="target/${architecture_dir}/${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}" - notarization_key_file=$(mktemp) - - 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/terms/terms.json "${dmg_file_path}" - - if [[ $can_code_sign = true ]]; then - echo "Notarizing DMG with Apple" - /usr/bin/codesign --deep --force --timestamp --options runtime --sign "$IDENTITY" "$(pwd)/${dmg_file_path}" -v - echo "$APPLE_NOTARIZATION_KEY" > "$notarization_key_file" - "${xcode_bin_dir_path}/notarytool" submit --wait --key "$notarization_key_file" --key-id "$APPLE_NOTARIZATION_KEY_ID" --issuer "$APPLE_NOTARIZATION_ISSUER_ID" "${dmg_file_path}" - rm "$notarization_key_file" - "${xcode_bin_dir_path}/stapler" staple "${dmg_file_path}" - fi - - if [ "$open_result" = true ]; then - open $dmg_target_directory - fi - fi -} - -function sign_binary() { - local binary_path=$1 - - if [[ $can_code_sign = true ]]; then - echo "Code signing executable $binary_path" - /usr/bin/codesign --deep --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "$IDENTITY" "${binary_path}" -v - fi -} - -if [ "$local_arch" = true ]; then - sign_app_binaries "$app_path" "$local_target_triple" "$local_target_triple" - - sign_binary "target/release/remote_server" -else - # Create universal binary - prepare_binaries "aarch64-apple-darwin" "$app_path_aarch64" - prepare_binaries "x86_64-apple-darwin" "$app_path_x64" - - - sign_app_binaries "$app_path_x64" "x86_64-apple-darwin" "x86_64-apple-darwin" - sign_app_binaries "$app_path_aarch64" "aarch64-apple-darwin" "aarch64-apple-darwin" - - sign_binary "target/x86_64-apple-darwin/release/remote_server" - sign_binary "target/aarch64-apple-darwin/release/remote_server" - gzip -f --stdout --best target/x86_64-apple-darwin/release/remote_server > target/zed-remote-server-macos-x86_64.gz - gzip -f --stdout --best target/aarch64-apple-darwin/release/remote_server > target/zed-remote-server-macos-aarch64.gz -fi - -function upload_debug_info() { - architecture=$1 - if [[ -n "${SENTRY_AUTH_TOKEN:-}" ]]; then - echo "Uploading zed debug symbols to sentry..." - # note: this uploads the unstripped binary which is needed because it contains - # .eh_frame data for stack unwinding. see https://github.com/getsentry/symbolic/issues/783 - sentry-cli debug-files upload --include-sources --wait -p zed -o zed-dev \ - "target/${architecture}/${target_dir}/zed" \ - "target/${architecture}/${target_dir}/remote_server" \ - "target/${architecture}/${target_dir}/zed.dwarf" - else - echo "missing SENTRY_AUTH_TOKEN. skipping sentry upload." - fi -} - -if command -v sentry-cli >/dev/null 2>&1; then - upload_debug_info "aarch64-apple-darwin" - upload_debug_info "x86_64-apple-darwin" +script/build-mac $build_args +if [ "$local_only" = true ]; then + script/notarize-mac $bundle_name else - echo "sentry-cli not found. skipping sentry upload." - echo "install with: 'curl -sL https://sentry.io/get-cli | bash'" + script/notarize-mac $bundle_name fi diff --git a/script/notarize-mac b/script/notarize-mac new file mode 100755 index 0000000000000000000000000000000000000000..f92463cbd3da325e429e1d532c67f2cd9488b525 --- /dev/null +++ b/script/notarize-mac @@ -0,0 +1,255 @@ +# This must match the team in the provisioning profile. +IDENTITY="Zed Industries, Inc." +APPLE_NOTARIZATION_TEAM="MQ55VZLNZQ" + +local_arch=false +local_only=false +local_install=false +can_code_sign=false +app_path="" +app_target="" +# Function for displaying help info +help_info() { + echo " +Usage: ${0##*/} [options] [bundle_name] +Notarize the application bundle for macOS. + +Options: + -l Notarize local architecture only. + -o Open dir with the resulting DMG or launch the app itself in local mode. + -i Install the resulting DMG into /Applications in local mode. Noop without -l. + -h Display this help and exit. + " +} + +while getopts 'loih' flag +do + case "${flag}" in + o) open_result=true + ;; + l) + local_arch=true + local_only=true + ;; + i) local_install=true;; + h) + help_info + exit 0 + ;; + esac +done + +shift +if [[ $# -gt 0 ]]; then + if [ "$1" ]; then + app_path=$1 + fi + if [ "$2" ]; then + app_target=$2 + fi +fi + +pushd crates/zed +channel=$( /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 + + function cleanup() { + echo "Cleaning up keychain" + security default-keychain -s login.keychain + security delete-keychain zed.keychain + } + + trap cleanup EXIT +fi + + +GIT_VERSION="v2.43.3" +GIT_VERSION_SHA="fa29823" + +function download_and_unpack() { + local url=$1 + local path_to_unpack=$2 + local target_path=$3 + + temp_dir=$(mktemp -d) + + if ! command -v curl &> /dev/null; then + echo "curl is not installed. Please install curl to continue." + exit 1 + fi + + curl --silent --fail --location "$url" | tar -xvz -C "$temp_dir" -f - $path_to_unpack + + mv "$temp_dir/$path_to_unpack" "$target_path" + + rm -rf "$temp_dir" +} + +function download_git() { + local architecture=$1 + local target_binary=$2 + + tmp_dir=$(mktemp -d) + pushd "$tmp_dir" + + case "$architecture" in + aarch64-apple-darwin) + download_and_unpack "https://github.com/desktop/dugite-native/releases/download/${GIT_VERSION}/dugite-native-${GIT_VERSION}-${GIT_VERSION_SHA}-macOS-arm64.tar.gz" bin/git ./git + ;; + x86_64-apple-darwin) + download_and_unpack "https://github.com/desktop/dugite-native/releases/download/${GIT_VERSION}/dugite-native-${GIT_VERSION}-${GIT_VERSION_SHA}-macOS-x64.tar.gz" bin/git ./git + ;; + *) + echo "Unsupported architecture: $architecture" + exit 1 + ;; + esac + + popd + + mv "${tmp_dir}/git" "${target_binary}" + rm -rf "$tmp_dir" +} + + +function sign_app_binaries() { + local app_path=$1 + local architecture=$2 + local architecture_dir=$architecture + rm -rf "${app_path}/Contents/Frameworks" + mkdir -p "${app_path}/Contents/Frameworks" + + echo "Downloading git binary" + download_git "${architecture}" "${app_path}/Contents/MacOS/git" + + # Note: The app identifier for our development builds is the same as the app identifier for nightly. + cp crates/zed/contents/$channel/embedded.provisionprofile "${app_path}/Contents/" + + if [[ $can_code_sign = true ]]; then + echo "Code signing binaries" + # sequence of codesign commands modeled after this example: https://developer.apple.com/forums/thread/701514 + /usr/bin/codesign --deep --force --timestamp --options runtime --sign "$IDENTITY" "${app_path}/Contents/MacOS/cli" -v + /usr/bin/codesign --deep --force --timestamp --options runtime --sign "$IDENTITY" "${app_path}/Contents/MacOS/git" -v + /usr/bin/codesign --deep --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "$IDENTITY" "${app_path}/Contents/MacOS/zed" -v + /usr/bin/codesign --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "$IDENTITY" "${app_path}" -v + else + echo "One or more of the following variables are missing: MACOS_CERTIFICATE, MACOS_CERTIFICATE_PASSWORD, APPLE_NOTARIZATION_KEY, APPLE_NOTARIZATION_KEY_ID, APPLE_NOTARIZATION_ISSUER_ID" + 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-bundling` 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/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 + bundle_name=$(basename "$app_path") + + # If bundle_name doesn't end in .app, append it + if [[ "$bundle_name" != *.app ]]; then + bundle_name="$bundle_name.app" + fi + + if [ "$local_only" = true ]; then + if [ "$local_install" = true ]; then + rm -rf "/Applications/$bundle_name" + mv "$app_path" "/Applications/$bundle_name" + echo "Installed application bundle: /Applications/$bundle_name" + if [ "$open_result" = true ]; then + echo "Opening /Applications/$bundle_name" + open "/Applications/$bundle_name" + fi + else + if [ "$open_result" = true ]; then + echo "Opening $app_path" + open "$app_path" + fi + fi + else + dmg_target_directory="target/${architecture_dir}/${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}" + notarization_key_file=$(mktemp) + + 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/terms/terms.json "${dmg_file_path}" + + if [[ $can_code_sign = true ]]; then + echo "Notarizing DMG with Apple" + /usr/bin/codesign --deep --force --timestamp --options runtime --sign "$IDENTITY" "$(pwd)/${dmg_file_path}" -v + echo "$APPLE_NOTARIZATION_KEY" > "$notarization_key_file" + "${xcode_bin_dir_path}/notarytool" submit --wait --key "$notarization_key_file" --key-id "$APPLE_NOTARIZATION_KEY_ID" --issuer "$APPLE_NOTARIZATION_ISSUER_ID" "${dmg_file_path}" + rm "$notarization_key_file" + "${xcode_bin_dir_path}/stapler" staple "${dmg_file_path}" + fi + + if [ "$open_result" = true ]; then + open $dmg_target_directory + fi + fi +} + +function sign_binary() { + local binary_path=$1 + + if [[ $can_code_sign = true ]]; then + echo "Code signing executable $binary_path" + /usr/bin/codesign --deep --force --timestamp --options runtime --entitlements crates/zed/resources/zed.entitlements --sign "$IDENTITY" "${binary_path}" -v + fi +} + +sign_app_binaries "$app_path" "$app_target" +sign_binary "target/x86_64-apple-darwin/release/remote_server" + +if [ "$local_arch" = false ]; then + gzip -f --stdout --best target/x86_64-apple-darwin/release/remote_server > target/zed-remote-server-macos-x86_64.gz +fi