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 00000000..4d169081 Binary files /dev/null and b/install/macos_packages/pkg_resources/icon.png differ 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