Skip to content

Instantly share code, notes, and snippets.

@petered
Last active February 16, 2023 22:43
Show Gist options
  • Save petered/a80096cc93332411dc6cb5cd197670b8 to your computer and use it in GitHub Desktop.
Save petered/a80096cc93332411dc6cb5cd197670b8 to your computer and use it in GitHub Desktop.
# Author: Peter O'Connor
# This script starts with a pyinstaller spec file, builds a macOS app,
# codesigns it, notarizes it, staples the notarization ticket, and zips it up.
# Full Guide at: https://sites.google.com/site/petesjunkyard/how-to-turn-your-python-program-into-a-working-macos-app
#
# Before using:
# 1) You should have a python app with a main script.
# 2) Become apple developer - See guide https://sites.google.com/site/petesjunkyard/how-to-turn-your-python-program-into-a-working-macos-app
# 3) Install pyinstaller with
# pip install pyinstaller
# 4) Install "command line tools for Xcode 14.2" (or newer) from https://developer.apple.com/download/all/ (this give you notarytool)
# 5) make the pyinstaller spec file with, e.g.
# pyi-makespec --windowed main.py # Should create a main.spec file
# 6) cd to the directory containing main.spec
# 7) Create a file called entitlements.plist - see https://sites.google.com/d/1gkIG2qmZi5e0qIeDGVoCT333ssUmGKLg/p/1eVkztBPP5nxSP5pnLN_B1N8TwgCBeGDP/edit for what to put in it.
# 8) Update the values below to match your app
# 9) You may also want to set your app-specific password (from https://appleid.apple.com) as an environment variable
# so that you don't have to enter it every time you run the script
# export APP_SPECIFIC_PASSWORD="your password"
#
# Now to do a fresh build, run this script with the spec file you created as the first argument
# ./publish_macos.sh main.spec
#
# And now you can distribute the resulting zip file, whiuch will look like YOUR_APP_NAME_osx-arm64.zip
APPLE_ID="poconn4@gmail.com" # Your Apple ID - should be an email address, and registered as a developer with Apple
APP_NAME="EagleEyesScan" # Name of your app - should match the "name" argument to BUNDLE(...) in the spec file
TEAM_ID="3992QD63RK" # Your team ID - can be found in the Apple Developer Portal # From https://developer.apple.com/account
CERTIFICATE_NAME="Developer ID Application: Eagle Eyes Search Inc (3992QD63RK)" # From https://developer.apple.com/account/resources/certificates/list
# Assign first argument to variable SPEC_FILE, or if not specified, exit with error
SPEC_FILE=${1:?Please specify the path to the pyinstaller spec file as the first argument.}
# Get platform name as a variable from python platform.machine()
# or from the second argument if it is specified
PLATFORM=$(python -c "import platform; print(platform.machine())")
ARCH=${2:-$PLATFORM}
echo "Platform name: '$ARCH'"
# Set this file so that it fails immediately if any command fails
set -e
# Get the password as a variable from the environment or prompt user for it if it does not exist.
# Generate it at https://appleid.apple.com
if [ -z "$APP_SPECIFIC_PASSWORD" ]; then
read -s -p "Enter your app specific password: " APP_SPECIFIC_PASSWORD
echo
fi
# Delete the dist folder
rm -rf dist
# Create the dist folder with the app name
pyinstaller --noconfirm "$SPEC_FILE"
# Delete .DS_Score files which screw up the codesigning
find ./dist/$APP_NAME.app -name .DS_Store -delete
# Codes sign the Resources folder, which for some reason is not signed by default
codesign -s "$CERTIFICATE_NAME" -vvv --deep --timestamp --entitlements entitlements.plist dist/$APP_NAME.app/Contents/Resources/*.dylib --force
# Codesign the app
codesign -s "$CERTIFICATE_NAME" -v --deep --timestamp --entitlements entitlements.plist -o runtime "dist/$APP_NAME.app" --force
# Create a zip file
NOTARIZATION_ZIP_PATH=dist/"$APP_NAME"_"$ARCH"_SUBMISSION.zip
ditto -c -k --keepParent "dist/$APP_NAME.app" "$NOTARIZATION_ZIP_PATH"
# Submit the zip file to notarization
xcrun notarytool submit "$NOTARIZATION_ZIP_PATH" --apple-id $APPLE_ID --team-id $TEAM_ID --password "$APP_SPECIFIC_PASSWORD" --wait
# TODO: Check the status of the notarization and quit if invalid. Github Copilot has some ideas.
echo "Notarization complete...About to staple"
# Staple the notarization ticket to the app -- requires ID from previous command
xcrun stapler staple dist/$APP_NAME.app
rm "$NOTARIZATION_ZIP_PATH"
# Now with stapled ticket, create the final zip file (note - this still happens if we have failure in notarization)
FINAL_ZIP_PATH=dist/"$APP_NAME"_"$ARCH".zip
ditto -c -k --keepParent "dist/$APP_NAME.app" "$FINAL_ZIP_PATH"
echo "Final zip file created at $FINAL_ZIP_PATH. Spread it unto the world!"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment