From 955a0f7a3399d089f967a0be3f08b3e62c1c796f Mon Sep 17 00:00:00 2001 From: Kevin Song <4605384+chipbuster@users.noreply.github.com> Date: Sun, 27 Feb 2022 15:58:28 -0600 Subject: [PATCH] ci: Notarize MacOS Binaries and Add Flat Package Installers (#3571) * ci: Notarize MacOS * Modify logging and documentation to be better * Make a copy of certain parts of the deploy workflow * Delete testing workflow --- .github/workflows/deploy.yml | 94 +++++++ .gitignore | 3 + install/macos_packages/aarch64.plist | 10 + install/macos_packages/build_and_notarize.sh | 115 ++++++++ .../macos_packages/build_component_package.sh | 90 ++++++ .../build_distribution_package.sh | 67 +++++ install/macos_packages/common.sh | 22 ++ .../English.lproj/conclusion.html | 28 ++ .../pkg_resources/English.lproj/license.html | 26 ++ .../pkg_resources/English.lproj/welcome.html | 18 ++ install/macos_packages/pkg_resources/icon.png | Bin 0 -> 58035 bytes install/macos_packages/readme.md | 263 ++++++++++++++++++ install/macos_packages/x86_64.plist | 10 + 13 files changed, 746 insertions(+) create mode 100644 install/macos_packages/aarch64.plist create mode 100644 install/macos_packages/build_and_notarize.sh create mode 100644 install/macos_packages/build_component_package.sh create mode 100644 install/macos_packages/build_distribution_package.sh create mode 100644 install/macos_packages/common.sh create mode 100644 install/macos_packages/pkg_resources/English.lproj/conclusion.html create mode 100644 install/macos_packages/pkg_resources/English.lproj/license.html create mode 100644 install/macos_packages/pkg_resources/English.lproj/welcome.html create mode 100644 install/macos_packages/pkg_resources/icon.png create mode 100644 install/macos_packages/readme.md create mode 100644 install/macos_packages/x86_64.plist diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 27899e30..4e3bd095 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -105,6 +105,100 @@ jobs: name: ${{ matrix.name }} path: ${{ matrix.name }} + # Notarize starship binaries for MacOS and build notarized pkg installers + notarize_and_pkgbuild: + runs-on: macos-latest + needs: github_build + strategy: + fail-fast: false + matrix: + include: + - target: x86_64-apple-darwin + arch: x86_64 + name: starship-x86_64-apple-darwin.tar.gz + pkgname: starship-x86_64-apple-darwin.pkg + + - target: aarch64-apple-darwin + arch: aarch64 + name: starship-aarch64-apple-darwin.tar.gz + pkgname: starship-aarch64-apple-darwin.pkg + + env: + KEYCHAIN_FILENAME: app-signing.keychain-db + KEYCHAIN_ENTRY: AC_PASSWORD + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Notarize | Set up secrets + env: + APP_CERTIFICATE_BASE64: ${{ secrets.APPLEDEV_APPSIGNKEY_BASE64 }} + INSTALL_CERTIFICATE_BASE64: ${{ secrets.APPLEDEV_INSTALLERSIGNKEY_BASE64 }} + P12_PASSWORD: ${{ secrets.APPLEDEV_SIGNKEY_PASS }} + KEYCHAIN_PASSWORD: ${{ secrets.APPLEDEV_SIGNKEY_PASS }} + APPLEID_USERNAME: ${{ secrets.APPLEDEV_ID_NAME }} + APPLEID_TEAMID: ${{ secrets.APPLEDEV_TEAM_ID }} + APPLEID_PASSWORD: ${{ secrets.APPLEDEV_PASSWORD }} + run: | + APP_CERTIFICATE_PATH="$RUNNER_TEMP/app_certificate.p12" + INSTALL_CERTIFICATE_PATH="$RUNNER_TEMP/install_certificate.p12" + KEYCHAIN_PATH="$RUNNER_TEMP/$KEYCHAIN_FILENAME" + + # import certificates from secrets + echo -n "$APP_CERTIFICATE_BASE64" | base64 --decode --output $APP_CERTIFICATE_PATH + echo -n "$INSTALL_CERTIFICATE_BASE64" | base64 --decode --output $INSTALL_CERTIFICATE_PATH + + # create temporary keychain + security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" + security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + + # import certificates to keychain + security import $APP_CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security import $INSTALL_CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH + security list-keychain -d user -s $KEYCHAIN_PATH + + # Add Apple Developer ID credentials to keychain + xcrun notarytool store-credentials "$KEYCHAIN_ENTRY" --team-id "$APPLEID_TEAMID" --apple-id "$APPLEID_USERNAME" --password "$APPLEID_PASSWORD" --keychain "$KEYCHAIN_PATH" + + - name: Notarize | Build docs + run: | + cd docs + npm install + npm run build + + - name: Notarize | Download artifacts + uses: actions/download-artifact@v2 + with: + name: ${{ matrix.name }} + path: artifacts + + - name: Notarize | Unpack Binaries + run: tar xf artifacts/${{ matrix.name }} + + - name: Notarize | Build, Sign, and Notarize Pkg + run: bash install/macos_packages/build_and_notarize.sh starship docs ${{ matrix.arch }} ${{ matrix.pkgname }} + + - name: Notarize | Upload Notarized Flat Installer + uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.pkgname }} + path: ${{ matrix.pkgname }} + + - name: Notarize | Package Notarized Binary + run: tar czvf ${{ matrix.name }} starship + + - name: Notarize | Upload Notarized Binary + uses: actions/upload-artifact@v2 + with: + name: ${{ matrix.name }} + path: ${{ matrix.name }} + + - name: Cleanup Secrets + if: ${{ always() }} + run: | + KEYCHAIN_PATH="$RUNNER_TEMP/$KEYCHAIN_FILENAME" + security delete-keychain $KEYCHAIN_PATH + # Create GitHub release with Rust build targets and release notes github_release: name: Create GitHub Release diff --git a/.gitignore b/.gitignore index 62b0a1a1..7e007b41 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ Cargo.lock # Compiled files for documentation docs/node_modules docs/.vuepress/dist/ + +# Ignore pkg files within the install directory +install/**/*.pkg \ No newline at end of file diff --git a/install/macos_packages/aarch64.plist b/install/macos_packages/aarch64.plist new file mode 100644 index 00000000..2b7fc446 --- /dev/null +++ b/install/macos_packages/aarch64.plist @@ -0,0 +1,10 @@ + + + + + arch + + arm64 + + + \ No newline at end of file diff --git a/install/macos_packages/build_and_notarize.sh b/install/macos_packages/build_and_notarize.sh new file mode 100644 index 00000000..1900ed98 --- /dev/null +++ b/install/macos_packages/build_and_notarize.sh @@ -0,0 +1,115 @@ +#!/bin/bash + +set -euo pipefail + +# Envrionmental variables that need to be set. These are sane defaults +# KEYCHAIN_ENTRY=AC_PASSWORD # Or whatever you picked for +# RUNNER_TEMP=~/Library/Keychains/ +# KEYCHAIN_FILENAME=login.keychain-db +# +# Environmental variables that can be set if needed. Else they will default to +# values selected for the CI +# +# The identifier for the application signing key. Can be a name or a fingerprint +# APPLICATION_KEY_IDENT=E03290CABE09E9E42341C8FC82608E91241FAD4A +# The identifier for the installer signing key. Can be a name or a fingerprint +# INSTALLATION_KEY_IDENT=E525359D0B5AE97B7B6F5BB465FEC872C117D681 + +usage(){ + echo "Builds, signs, and notarizes starship." + echo "Read readme.md in the script directory to see the assumptions the script makes." + echo "Usage: $0 [pkgname]" + echo " Example: $0 target/release/starship docs/ x64" + echo " Example: $0 target/debug/starship docs/ arm64 starship-1.2.1-arm64.pkg" + echo "" + echo "If no pkgname is provided, the package will be named starship--.pkg" +} + +script_dir="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source "$script_dir/common.sh" + +if [[ -z ${KEYCHAIN_ENTRY+x} ]]; then + error "Environmental variable KEYCHAIN_ENTRY must be set." +fi + +if [[ -z ${RUNNER_TEMP+x} ]]; then + error "Environmental variable RUNNER_TEMP must be set." +fi + +if [[ -z ${KEYCHAIN_FILENAME+x} ]]; then + error "Environmental variable KEYCHAIN_FILENAME must be set." +fi + +keychain_path="$RUNNER_TEMP/$KEYCHAIN_FILENAME" +if [[ ! -f "$keychain_path" ]]; then + error "Could not find keychain at $keychain_path" +fi + +if [[ -z ${APPLICATION_KEY_IDENT+x} ]]; then + APPLICATION_KEY_IDENT=E03290CABE09E9E42341C8FC82608E91241FAD4A + echo "APPLICATION_KEY_IDENT not set. Using default value of $APPLICATION_KEY_IDENT" +fi + +if [[ -z ${INSTALLATION_KEY_IDENT+x} ]]; then + INSTALLATION_KEY_IDENT=E525359D0B5AE97B7B6F5BB465FEC872C117D681 + echo "INSTALLATION_KEY_IDENT not set. Using default value of $INSTALLATION_KEY_IDENT" +fi + +if [[ -z ${3+x} ]]; then + usage + exit 1 +fi + +starship_binary="$1" +starship_docs_dir="$2" +arch="$3" +pkgname="${4:-}" + +if [[ ! -d "$starship_docs_dir/.vuepress/dist" ]]; then + error "Documentation does not appear to have been built!" +fi + +echo ">>>> Signing binary" +codesign --timestamp --keychain "$keychain_path" --sign "$APPLICATION_KEY_IDENT" --verbose -f -o runtime "$starship_binary" + +# Make ZIP file to notarize binary +if [ "$starship_binary" != "starship" ]; then + cp "$starship_binary" starship +fi +zip starship.zip starship + +echo ">>>> Submitting binary for notarization" +xcrun notarytool submit starship.zip --keychain-profile "$KEYCHAIN_ENTRY" --wait + +# Don't think this is actually necessary, but not costly so why not +rm starship +unzip starship.zip + +# Create the component package +echo ">>>> Building Component Package" +bash "$script_dir/build_component_package.sh" "starship" "$starship_docs_dir/.vuepress/dist" + +# Create the distribution package +echo ">>>> Building Distribution Package" +resources_path="$script_dir/pkg_resources" +bash "$script_dir/build_distribution_package.sh" "starship-component.pkg" "$resources_path" "$arch" + +# Codesign the package installer +productsign --timestamp --sign "$INSTALLATION_KEY_IDENT" starship-unsigned.pkg starship.pkg + +# Notarize the package installer +echo ">>>> Submitting .pkg for notarization" +xcrun notarytool submit starship.pkg --keychain-profile "$KEYCHAIN_ENTRY" --wait + +# Staple things +echo ">>>> Running final steps" +xcrun stapler staple starship.pkg + +# Rename to expected name +if [ "$pkgname" = "" ]; then + version="$(starship_version "$starship_binary")" + pkgname="starship-$version-$arch.pkg" +fi + +echo ">>>> Placing final output at $pkgname" +mv starship.pkg "$pkgname" \ No newline at end of file diff --git a/install/macos_packages/build_component_package.sh b/install/macos_packages/build_component_package.sh new file mode 100644 index 00000000..000c08f6 --- /dev/null +++ b/install/macos_packages/build_component_package.sh @@ -0,0 +1,90 @@ +#!/bin/bash + +set -euo pipefail + +# Requirements: +# - MacOS +# - A starship repository with binaries and documentation already built. +# Usage: run this script, passing $1 to the repository path. The script assumes +# it is being run from within a starship repository if $1 is not provided. + +usage(){ + echo "Builds a component package for macOS." + echo "Assumes that the following items already exist:" + echo " - A starship binary which has already been notarized" + echo " - Documentation created by \`npm run build\`, usually in a dist" + echo " directory at /docs/.vuepress/dist" + echo "Usage: $0 " +} + +script_dir="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source "$script_dir/common.sh" + +cleanup_server(){ + if [[ -n "${server_pid-}" ]]; then + echo "Killing HTTP server ($server_pid) to clean up." + kill "$server_pid" + rm "x86_64-apple-darwin-simple-http-server" + else + echo "No server found, exiting normally." + fi +} + +if [[ "$OSTYPE" != 'darwin'* ]]; then + error "This script only works on MacOS" +fi + +if [[ "${2-undefined}" = "undefined" ]]; then + usage + exit 1 +fi + +starship_program_file="$1" +starship_documentation_dir="$2" + +if [ ! -f "$starship_program_file" ]; then + error "Could not find starship binary at $starship_program_file" +fi + +if [ ! -d "$starship_documentation_dir" ]; then + error "Could not find starship documentation at $starship_documentation_dir" +fi + +pkgdir="$(mktemp -d)" +mkdir -p "$pkgdir/usr/local/bin" +cp "$starship_program_file" "$pkgdir/usr/local/bin/starship" + +# Now we get to make documentation! Vuepress was not designed to build locally +# (too many assumptions about running on an HTTP server), so we do the hackiest +# thing imagineable: start an http server, and use wget to make a local mirror. + +# First, we need to install the server. There are several options, but this one +# provides prebuilt binaries for MacOS, making it the easiest. (yay rust) +server_prog_name="x86_64-apple-darwin-simple-http-server" +latest_server_version="$(curl -L -s -H 'Accept: application/json' https://github.com/TheWaWaR/simple-http-server/releases/latest | sed -e 's/.*"tag_name":"\([^"]*\)".*/\1/')" +curl -LOk "https://github.com/TheWaWaR/simple-http-server/releases/download/$latest_server_version/$server_prog_name" +chmod u+x "$server_prog_name" + +# Next, we build the documentation and serve it via simple-http-server +trap cleanup_server INT +"./$server_prog_name" --ip 127.0.0.1 --index "$starship_documentation_dir" & +server_pid="$!" +# Give the server a chance to come online before trying to mirror it +echo "Sleeping to give the server a chance to come online..." +sleep 3 + +# Use wget to make a mirror of the site and move it into the package. Not installed +# on MacOS by default, but lucky for us, it does exist on GHActions runners. +# Wget may return nonzero exit codes even if things were mostly fine (e.g. 404 for +# some links on translated pages) so we simply ignore if it has a failure +wget --mirror --convert-links --adjust-extension --page-requisites --no-parent 127.0.0.1:8000 &> wget.log || true +mkdir -p "$pkgdir/usr/local/share/doc/" +mv 127.0.0.1:8000 "$pkgdir/usr/local/share/doc/starship" + +# Technically a race condition here, but very unlikely to hit it in practice. +cleanup_server +trap - INT + +# Build the component package +version="$(starship_version "$starship_program_file")" +pkgbuild --identifier com.starshipprompt.starship --version "$version" --root $pkgdir starship-component.pkg \ No newline at end of file diff --git a/install/macos_packages/build_distribution_package.sh b/install/macos_packages/build_distribution_package.sh new file mode 100644 index 00000000..54b4ef89 --- /dev/null +++ b/install/macos_packages/build_distribution_package.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +component_package="$1" +resources="$2" +arch="$3" + +usage(){ + echo "Builds a distribution package for macOS." + echo "Assumes that the following items already exist:" + echo " - A starship component package" + echo " - Resources in a pkg_resources directory" + echo "Usage: $0 " + echo " where arch is one of \"arm64\" or \"x86_64\"" +} + +script_dir="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source "$script_dir/common.sh" + +if [[ "$OSTYPE" != 'darwin'* ]]; then + error "This script only works on MacOS" +fi + +if [[ "${3-undefined}" = "undefined" ]]; then + usage + exit 1 +fi + +# Generate a distribution file with the appropriate architecture plists +if [[ "$arch" == "x86_64" || "$arch" == "x64" ]]; then + archplist="$script_dir/x86_64.plist" +elif [[ "$arch" == "arm64" || "$arch" == "aarch64" ]]; then + archplist="$script_dir/aarch64.plist" +else + error "Invalid architecture: $arch" +fi + +productbuild --synthesize --package starship-component.pkg --product "$archplist" starship_raw.dist + +# A terrible hacky way to insert nodes into XML without needing a full XML parser: +# search for a line that matches our opening tag and insert our desired lines after it +# Solution taken from https://www.theunixschool.com/2012/06/insert-line-before-or-after-pattern.html + +while read -r line +do + echo "$line" + if echo "$line" | grep -qF '' + echo '' + echo '' + echo '' + fi +done < starship_raw.dist > starship.dist + +# The above script does not correctly take care of the last line. Apply fixup. +echo '' >> starship.dist + +echo "Creating distribution package with following distribution file:" +cat starship.dist + +echo "Resource directory is $resources" +echo "Component package is $component_package" + +# Build the distribution package +productbuild --distribution starship.dist --resources "$resources" --package-path "$component_package" starship-unsigned.pkg + +# Clean up the distribution files +rm -- *.dist \ No newline at end of file diff --git a/install/macos_packages/common.sh b/install/macos_packages/common.sh new file mode 100644 index 00000000..9a9c8503 --- /dev/null +++ b/install/macos_packages/common.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +error(){ + echo "[ERROR]: $1" + exit 1 +} + +starship_version(){ + starship_program_file="$1" + # Check if this is a relative path: if so, prepend './' to it + if [ "$1" = "${1#/}" ]; then + starship_program_file="./$starship_program_file" + fi + if "$starship_program_file" -V 2>&1 > /dev/null; then + "$starship_program_file" -V | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' + else + # try to get this information from Cargo.toml + pushd "$(git rev-parse --show-toplevel)" || true + grep '^version = \"\(.*\)\"' Cargo.toml | cut -f 2 -d '"' + popd + fi +} diff --git a/install/macos_packages/pkg_resources/English.lproj/conclusion.html b/install/macos_packages/pkg_resources/English.lproj/conclusion.html new file mode 100644 index 00000000..57831f78 --- /dev/null +++ b/install/macos_packages/pkg_resources/English.lproj/conclusion.html @@ -0,0 +1,28 @@ + + + + + Install Starship + + + + + +

Starship has been installed!

+

Visit https://starship.rs to get + instructions on how to configure your shell to use starship.

+

If you do not have internet access, you can view an offline copy of the + the documentation by opening + + + /usr/local/share/doc/starship/index.html. + + +

+

Unless you modified the installer, your copy of starship was installed to + /usr/local/bin/starship. +

+
+ + + \ No newline at end of file diff --git a/install/macos_packages/pkg_resources/English.lproj/license.html b/install/macos_packages/pkg_resources/English.lproj/license.html new file mode 100644 index 00000000..f446f65e --- /dev/null +++ b/install/macos_packages/pkg_resources/English.lproj/license.html @@ -0,0 +1,26 @@ + + + + + ISC License + + + + + +

ISC License

+

Copyright (c) 2019-2022, Starship Contributors

+

Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies.

+

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

+
+ + + \ No newline at end of file diff --git a/install/macos_packages/pkg_resources/English.lproj/welcome.html b/install/macos_packages/pkg_resources/English.lproj/welcome.html new file mode 100644 index 00000000..e427769b --- /dev/null +++ b/install/macos_packages/pkg_resources/English.lproj/welcome.html @@ -0,0 +1,18 @@ + + + + + Install Starship + + + + + +

Starship is the minimal, blazing-fast, and infinitely customizable prompt for any shell!

+

This installer will install Starship on to /usr/local/ on your system.

+

After the installation, you will need to modify your shell startup files to + use starship.

+
+ + + \ No newline at end of file diff --git a/install/macos_packages/pkg_resources/icon.png b/install/macos_packages/pkg_resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4d16908111077d3f886b47fe6a0bedcac64c79f0 GIT binary patch literal 58035 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

|UHud+7f!tnat37s5jNTuXZRs8>TdoDw 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 J|3()c_df#|ERg^J literal 0 HcmV?d00001 diff --git a/install/macos_packages/readme.md b/install/macos_packages/readme.md new file mode 100644 index 00000000..c56f84c3 --- /dev/null +++ b/install/macos_packages/readme.md @@ -0,0 +1,263 @@ +# MacOS Codesigning Scripts + +Well, here we are. The Apple notarization procedure is complex enough that I +need an actual pile of scripts and writeup to be able to remember how to do it. + +The basic procedure is as follows: + +- Build code +- Build docs +- Sign binary with Developer Application ID +- Upload binary to notarization service to get notarized +- Use code + docs to generate a component package +- Use component package to generate a distribution package +- Sign distribution package with Developer Installer ID +- Upload distribution package to notarization service to get notarized + +Et. voila, you have a notarized distribution package which can be installed. + +I fully anticipate that this procedure will break in the future, so here is the +(scant) documentation that I have been able to scrape together on these procedures, +along with some commentary on things that I've found and requirements for these +scripts. + +You will need XCode installed. + +## Short-form Command Line Invocation + +If you have the prerequisites set up (including the environment variables as +described below, built docs, and built release binary), you can generate the +package file with the following command: + +``` +./install/macos_packages/build_and_notarize.sh target/release/starship docs x64 +``` + +or `arm64` if building on Apple silicon. + +## Setting Up Credentials + +### Apple Developer Account + +In order to get the signing keys, you need to have a developer account. You can +buy one at https://developer.apple.com/programs/ for $100 a year (at time of +writing). + +There is no other way to acquire an account, which is needed to obtain +non-self-signed keys and to be able to notarize files. + +### Signing Keys + +To generate the signing keys, I went through the [XcodeGUI](https://help.apple.com/xcode/mac/current/#/dev154b28f09), though there are +several other methods to do this. You will need at least one Application signing +key and one Installer signing key. + +To check what signing keys are available, you can use the following command: + +``` +security find-identity -p basic -v +``` + +### Notarization Credentials + +To be able to notarize objects, you will need an app-specific password. You will +need to set it up using the instructions [on this page](https://support.apple.com/en-us/HT204397). +You will also need your team ID, which can be found at https://developer.apple.com/account/#/membership +(if it goes to the home page, click on "Membership" on the left panel), and your +Apple ID (usually an email address). + +If you want to enter everything manually, most commands that require these values +accept the `--apple-id`, `--team-id`, and `--password` flags. However, I find it +simpler to store the credentials in the keychain. You can do so with the following +command: + +``` +xcrun notarytool store-credentials "" --apple-id "" --password "" --team-id "" +``` + +where `` is a name you will use later to refer to the credentials, +and the other three items are the Apple ID, the Team ID, and the app-specific password, +respectively. For the rest of this document, I will assume that its value is +`AC_PASSWORD` for compatibility with Apple's website, though you may choose +whatever you like. + +### Script Assumptions + +The scripts in this directory assume that the signing keys and the notarization +credentials are unlocked and available within a specific keychain file, stored +in a file at `$RUNNER_TEMP/$KEYCHAIN_FILENAME`. Additionally, it assumes that +the `AUTH_ITEM_NAME` used to refer to the notarization credentials is found in +the environment under the variable `KEYCHAIN_ENTRY`. + +The CI environment ensures that the keychain file exists at the appropriate +locations and is destroyed after use. If you are running these scripts locally, +the values that correspond to what Apple uses in their tutorials are: + +``` +KEYCHAIN_ENTRY=AC_PASSWORD # Or whatever you picked for above +RUNNER_TEMP=~/Library/Keychains +KEYCHAIN_FILENAME=login.keychain +``` + +Note to developers: because the keychain file may be a user's personal keychain, +you MUST NEVER WRITE TO THE KEYCHAIN FILE in these scripts. On the CI, CI actions +will ensure that the keychain file is shredded after use. + +## Codesigning a Binary + +This is actually fairly simple. Run + +``` +codesign --timestamp --sign "" --verbose -f -o runtime +``` + +to sign the binary file. `--timestamp` is not required for signing, but will be +required for notarization. `` can be one of two things: the name of the +signing key (on the right of `security find-identity -p basic -v`), or the key +hash (the hex string on the left of the command). + +Usually you can use name of the key, but if you have multiple keys like me, you +may need to use the hex string to specify. + +## Notarizing a Binary + +Once the binary has been signed, you need to package it into a .zip file in order +to be able to send it to Apple for notarization. The simplest way to do this is +to run `zip `. + +Then, run `xcrun notarytool submit --keychain-profile "AC_PASSWORD" --wait` +to submit the binary for notarization. The `--wait` flag will cause the tool to +block until the notarialization is complete. If you want to be able to leave and +check the results later, omit `--wait` (though starship notarization usually takes +no more than 60s). + +Finally, you should check the submission logs. To get a record of all notarization +attempts, run + +``` +xcrun notarytool history --keychain-profile "AC_PASSWORD" +``` + +Find the `id` of the attempt you wish to view, then run one of these commmands: + +``` +xcrun notarytool info --keychain-profile "AC_PASSWORD" +xcrun notarytool log --keychain-profile "AC_PASSWORD" +``` + +The `log` command downloads a JSON log of the notarization attempt, and can reveal +warnings that should be fixed before the next submission. + +Additional details on the notarization process can be found at https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow. +Note that while Apple has a lot of requirements on their pages, including stuff +like Hardened Runtime requirements and listing entitlements, as far as I can tell, +starship does not require any of these even though we do things like send +notifications and access the network via an HTTP client. Nonetheless, I'm +linking the [entitlements page](https://developer.apple.com/documentation/bundleresources/entitlements) +here in case it becomes important later. + +## Creating a Component Package + +Since I'm only dealing with one binary, we will make one Component package and +one Distribution package. Surprisingly, the flat package (.pkg) format is not +documented by Apple. [This guide](https://matthew-brett.github.io/docosx/flat_packages.html) +and many of the links within it are the best documentation available on the subject. + +To build a component package, we first need to create a temporary directory and +create a pseudo-filesystem within it (similar to makepkg on Arch). For example, +if we place the directory at `$TEMP_DIR/usr/local/bin/starship`, the binary +will be installed at `/usr/local/bin/starship` once the installer runs. + +An aside on docs: We would also like to include documentation in the pkg. +Unfortunately, Vuepress currently cannot build with relative paths, and any +attempt at hacking this in seems to create even more problems. Instead, the +scripts do the dumbest thing imaginable: build the documentation, serve it with +a simple HTTP server, and then use `wget` to make a local copy which can be +viewed offline. + +Once everything is placed in the correct locations, we can run the following +command to generate the component package: + +``` +pkgbuild --identifier com.starshipprompt.starship --version "" --root output.pkg +``` + +## Notarizing the Component Package (and why we don't need to) + +Fortunately for us, Apple has confirmed that we only need to notarize the +[outermost installer mechanism](https://developer.apple.com/forums/thread/122045). + +Therefore, if we are sending the component package on its own, we should notarize +it now. However, for starship, we will bundle this into a distribution package, +so we don't need to notarize this pkg file. + +## Creating a Distribution Package + +To create a distribution, we do the following steps: + +- Use `productbuild` to generate a skeleton distribution file. +- Insert custom welcome/license/conclusion and icon files into the installer. +- Build the installer with `productbuild`. + +I have elected not to make a fat binary due to concerns over startup cost, so +there are two .plist files that can be used to specify the architecture required. + +## Signing the Distribution package + +This is also fairly simple, and analagous to signing the binary. + +``` +productsign --timestamp --sign "" +``` + +## Notarizing the Distribution Package + +Also analagous to notarizing the binary. We run + +``` +xcrun notarytool submit --keychain-profile "AC_PASSWORD" --wait +``` + +and also check the submission logs. + +Note: you may need to enter your password a ridiculous number of times (like 4+) +in order to successfully notarize this. + +## Stapling the Result + +Finally, we staple the notarization ticket to the package, ensuring that anyone +who downloads the file can see that the installer was notarized: + +``` +xcrun stapler staple +``` + +Note that `.dmg`, `.app`, and `.pkg` files can be stapled, but `.zip` and +binary files cannot. Distributing the latter files alone will require that the +installing computer can access the internet to verify notarization of the app. + +## Putting It All Together + +If you don't want to run these commands, a full workflow is available in +`build_and_notarize` script. Check the documentation at the top of the script +for environment variables and arguments that need to be set--it is a fairly +complicated script, but this is a fairly complicated procedure. + +# Testing Notarization + +To test if a particular item is notarized, run one of the following commands: + +``` +codesign --test-requirement="=notarized" --verify --verbose +spctl -a -vvv -t install +``` + +# External Links + +https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution + +https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow + +https://github.com/akeru-inc/xcnotary + +https://www.reddit.com/r/rust/comments/q8r90b/notarization_of_rust_binary_for_distribution_on/ diff --git a/install/macos_packages/x86_64.plist b/install/macos_packages/x86_64.plist new file mode 100644 index 00000000..535b143d --- /dev/null +++ b/install/macos_packages/x86_64.plist @@ -0,0 +1,10 @@ + + + + + arch + + x86_64 + + + \ No newline at end of file