Refactor build script for Apple Silicon
- Drop universal2 enforcement; arm64 wheels used directly - Single 'recommended' flavor; remove minimal/no_customization branches - Long-flag arguments; derive major version from full version - Functions: parse_args, prepare_build_dirs, download_tool, build_framework, codesign_framework, build_pkg, notarize_and_staple, zip_framework, cleanup - Collapse signed/ad-hoc codesign duplication; fixes latent path bug - Bump relocatable-python and munki-pkg SHAs - Gate CI-only steps (brew remove, xcode-select) on $CI
This commit is contained in:
+221
-291
@@ -1,324 +1,254 @@
|
||||
#!/bin/zsh
|
||||
#
|
||||
# Build script for Python 3 frameworks
|
||||
# Adaptd from https://github.com/munki/munki/blob/Munki3dev/code/tools/build_python_framework.sh
|
||||
# IMPORTANT
|
||||
# Run this with your current directory being the path where this script is located
|
||||
# Build the macadmins Python 3 framework.
|
||||
# Produces an installable .pkg (when signing identities are supplied) and a
|
||||
# portable framework zip targeting Apple Silicon.
|
||||
#
|
||||
# Adapted from https://github.com/munki/munki/blob/Munki3dev/code/tools/build_python_framework.sh
|
||||
|
||||
# Harcoded (commit) versions of relocatable-python & munki-pkg
|
||||
RP_SHA="fb4dd9b024b249c71713f14d887f4bcea78aa8b0" # https://github.com/gregneagle/relocatable-python/commits/main/
|
||||
MP_SHA="96cffb4eac9207c1130404ec1fee8f4777fa38fd" # https://github.com/munki/munki-pkg/commits/main/
|
||||
MACOS_VERSION=11 # use 10.9 for non-universal
|
||||
PYTHON_PRERELEASE_VERSION=
|
||||
PYTHON_BASEURL="https://www.python.org/ftp/python/%s/python-%s${PYTHON_PRERELEASE_VERSION}-macos%s.pkg"
|
||||
# Hardcoded paths
|
||||
set -eu
|
||||
|
||||
# --- Pinned upstream commits ---
|
||||
RP_SHA="8ee72fe3a5dbef733365370ebf44f25022b895ef" # gregneagle/relocatable-python
|
||||
MP_SHA="bbd07730d1b93ed3828246575ef5676bba74b5d1" # munki/munki-pkg
|
||||
|
||||
# --- Paths and constants ---
|
||||
TYPE="recommended"
|
||||
FRAMEWORKDIR="/Library/ManagedFrameworks/Python"
|
||||
PYTHON_BIN="$FRAMEWORKDIR/Python3.framework/Versions/Current/bin/python3"
|
||||
PYTHON_BIN_NEW="$FRAMEWORKDIR/Python3.framework/Versions/Current/Resources/Python.app/Contents/MacOS/Python"
|
||||
PYTHON_BASEURL="https://www.python.org/ftp/python/%s/python-%s-macos11.pkg"
|
||||
TOOLSDIR="$(/usr/bin/dirname "$0")"
|
||||
OUTPUTSDIR="$TOOLSDIR/outputs"
|
||||
RP_BINDIR="/tmp/relocatable-python"
|
||||
MP_BINDIR="/tmp/munki-pkg"
|
||||
CONSOLEUSER=$(/usr/bin/stat -f "%Su" /dev/console)
|
||||
PIPCACHEDIR="/Users/${CONSOLEUSER}/Library/Caches/pip"
|
||||
XCODE_PATH="/Applications/Xcode_15.2.app"
|
||||
XCODE_NOTARY_PATH="$XCODE_PATH/Contents/Developer/usr/bin/notarytool"
|
||||
XCODE_STAPLER_PATH="$XCODE_PATH/Contents/Developer/usr/bin/stapler"
|
||||
NEWSUBBUILD=$((80620 + $(/usr/bin/git rev-parse HEAD~0 | xargs -I{} /usr/bin/git rev-list --count {})))
|
||||
|
||||
# Sanity Checks
|
||||
## Type Check
|
||||
if [ -n "$1" ]; then
|
||||
if [[ "$1" == 'minimal' ]]; then
|
||||
TYPE=$1
|
||||
elif [[ "$1" == "no_customization" ]]; then
|
||||
TYPE=$1
|
||||
elif [[ "$1" == 'recommended' ]]; then
|
||||
TYPE=$1
|
||||
else
|
||||
echo "Specified positional argument other than recommended. Using minimal workflow"
|
||||
TYPE='minimal'
|
||||
fi
|
||||
else
|
||||
echo "runner.zsh"
|
||||
echo ""
|
||||
echo " Configures Relocatable Python"
|
||||
echo " Options:"
|
||||
echo " minimal"
|
||||
echo " Identical to the original relocatable python code"
|
||||
echo ""
|
||||
echo " no_customization"
|
||||
echo " A python framework without any customizations"
|
||||
echo ""
|
||||
echo " recommended"
|
||||
echo " A python framework with libraries for commonly used tools like autopkg and munki"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -n "$4" ]; then
|
||||
PYTHON_VERSION=$4
|
||||
else
|
||||
PYTHON_VERSION=3.13.5
|
||||
fi
|
||||
|
||||
if [ -n "$5" ]; then
|
||||
PYTHON_MAJOR_VERSION=$5
|
||||
else
|
||||
PYTHON_MAJOR_VERSION=3.13
|
||||
fi
|
||||
# Set python bin version based on PYTHON_VERSION
|
||||
PYTHON_BIN_VERSION="${PYTHON_VERSION%.*}"
|
||||
AUTOMATED_PYTHON_BUILD="$PYTHON_VERSION.$NEWSUBBUILD"
|
||||
|
||||
# Variables
|
||||
TOOLSDIR=$(dirname $0)
|
||||
OUTPUTSDIR="$TOOLSDIR/outputs"
|
||||
CONSOLEUSER=$(/usr/bin/stat -f "%Su" /dev/console)
|
||||
RP_ZIP="/tmp/relocatable-python.zip"
|
||||
MP_ZIP="/tmp/munki-pkg.zip"
|
||||
CONSOLEUSER="$(/usr/bin/stat -f "%Su" /dev/console)"
|
||||
PIPCACHEDIR="/Users/${CONSOLEUSER}/Library/Caches/pip"
|
||||
|
||||
# Create files to use for build process info
|
||||
echo "$AUTOMATED_PYTHON_BUILD" > $TOOLSDIR/build_info.txt
|
||||
# --- CLI arguments (set by parse_args) ---
|
||||
PYTHON_VERSION=""
|
||||
INSTALLER_ID=""
|
||||
APPLICATION_ID=""
|
||||
NOTARY_PASSWORD=""
|
||||
XCODE_PATH=""
|
||||
|
||||
echo "Creating Python Framework - $TYPE"
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $(/usr/bin/basename "$0") --python-version X.Y.Z [options]
|
||||
|
||||
# Create framework path if not present with 777 so sudo is not needed
|
||||
if [ ! -d "${FRAMEWORKDIR}" ]; then
|
||||
/usr/bin/sudo /bin/mkdir -m 777 -p "${FRAMEWORKDIR}"
|
||||
fi
|
||||
Required:
|
||||
--python-version Full Python version, e.g. 3.13.13
|
||||
|
||||
# remove existing Python.framework if present
|
||||
if [ -d "${FRAMEWORKDIR}/Python.framework" ]; then
|
||||
/usr/bin/sudo /bin/rm -rf "${FRAMEWORKDIR}/Python.framework"
|
||||
fi
|
||||
Optional (omit for an unsigned local build):
|
||||
--installer-id Developer ID Installer identity
|
||||
--application-id Developer ID Application identity
|
||||
--notary-password App-specific password for notarytool
|
||||
--xcode-path Path to Xcode.app (CI only)
|
||||
EOF
|
||||
}
|
||||
|
||||
# remove existing library Python.framework if present
|
||||
if [ -d "${PIPCACHEDIR}" ]; then
|
||||
echo "Removing pip cache to reduce framework build errors"
|
||||
/usr/bin/sudo /bin/rm -rf "${PIPCACHEDIR}"
|
||||
fi
|
||||
parse_args() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--python-version) PYTHON_VERSION="$2"; shift 2 ;;
|
||||
--installer-id) INSTALLER_ID="$2"; shift 2 ;;
|
||||
--application-id) APPLICATION_ID="$2"; shift 2 ;;
|
||||
--notary-password) NOTARY_PASSWORD="$2"; shift 2 ;;
|
||||
--xcode-path) XCODE_PATH="$2"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "Unknown argument: $1" >&2; usage; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# kill homebrew packages on GitHub runner
|
||||
/usr/local/bin/brew remove --force $(/usr/local/bin/brew list)
|
||||
if [[ -z "$PYTHON_VERSION" ]]; then
|
||||
echo "error: --python-version is required" >&2
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure Xcode is set to run-time
|
||||
sudo xcode-select -s "$XCODE_PATH"
|
||||
PYTHON_MAJOR_VERSION="${PYTHON_VERSION%.*}" # 3.13.13 -> 3.13
|
||||
PYTHON_BIN_VERSION="$PYTHON_MAJOR_VERSION"
|
||||
}
|
||||
|
||||
if [ -e $XCODE_BUILD_PATH ]; then
|
||||
XCODE_BUILD="$XCODE_BUILD_PATH"
|
||||
else
|
||||
ls -la /Applications
|
||||
echo "Could not find required Xcode build. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
is_ci() {
|
||||
[[ -n "${CI:-}" || -n "${GITHUB_ACTIONS:-}" ]]
|
||||
}
|
||||
|
||||
# Download specific version of relocatable-python
|
||||
echo "Downloading relocatable-python tool from github..."
|
||||
if [ -f "${RP_ZIP}" ]; then
|
||||
/usr/bin/sudo /bin/rm -rf ${RP_ZIP}
|
||||
fi
|
||||
/usr/bin/curl https://github.com/gregneagle/relocatable-python/archive/${RP_SHA}.zip -L -o ${RP_ZIP}
|
||||
if [ -d ${RP_BINDIR} ]; then
|
||||
/usr/bin/sudo /bin/rm -rf ${RP_BINDIR}
|
||||
fi
|
||||
/usr/bin/unzip ${RP_ZIP} -d ${RP_BINDIR}
|
||||
DL_RESULT="$?"
|
||||
if [ "${DL_RESULT}" != "0" ]; then
|
||||
echo "Error downloading relocatable-python tool: ${DL_RESULT}" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
derive_build_version() {
|
||||
local rev_count
|
||||
rev_count="$(/usr/bin/git -C "$TOOLSDIR" rev-list --count HEAD)"
|
||||
NEWSUBBUILD=$((80620 + rev_count))
|
||||
AUTOMATED_PYTHON_BUILD="$PYTHON_VERSION.$NEWSUBBUILD"
|
||||
echo "$AUTOMATED_PYTHON_BUILD" > "$TOOLSDIR/build_info.txt"
|
||||
echo "Build version: $AUTOMATED_PYTHON_BUILD"
|
||||
}
|
||||
|
||||
prepare_ci_env() {
|
||||
if ! is_ci; then
|
||||
return
|
||||
fi
|
||||
echo "CI detected — clearing Homebrew and selecting Xcode."
|
||||
/usr/local/bin/brew remove --force "$(/usr/local/bin/brew list)" || true
|
||||
if [[ -n "$XCODE_PATH" && -d "$XCODE_PATH" ]]; then
|
||||
/usr/bin/sudo /usr/bin/xcode-select -s "$XCODE_PATH"
|
||||
fi
|
||||
}
|
||||
|
||||
prepare_build_dirs() {
|
||||
/usr/bin/sudo /bin/mkdir -m 777 -p "$FRAMEWORKDIR"
|
||||
if [[ -d "$FRAMEWORKDIR/Python.framework" ]]; then
|
||||
/usr/bin/sudo /bin/rm -rf "$FRAMEWORKDIR/Python.framework"
|
||||
fi
|
||||
if [[ -d "$PIPCACHEDIR" ]]; then
|
||||
echo "Removing pip cache to reduce build errors"
|
||||
/usr/bin/sudo /bin/rm -rf "$PIPCACHEDIR"
|
||||
fi
|
||||
|
||||
# remove existing Python package folders and recreate
|
||||
if [ -d "$TOOLSDIR/$TYPE" ]; then
|
||||
/bin/rm -rf "$TOOLSDIR/$TYPE"
|
||||
/bin/mkdir -p "$TOOLSDIR/$TYPE/scripts"
|
||||
/bin/mkdir -p "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}"
|
||||
/bin/mkdir -p "$TOOLSDIR/$TYPE/payload/usr/local/bin"
|
||||
/usr/bin/sudo /usr/sbin/chown -R ${CONSOLEUSER}:wheel "$TOOLSDIR/$TYPE"
|
||||
else
|
||||
/bin/mkdir -p "$TOOLSDIR/$TYPE/scripts"
|
||||
/bin/mkdir -p "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}"
|
||||
/bin/mkdir -p "$TOOLSDIR/$TYPE/payload/usr/local/bin"
|
||||
/usr/bin/sudo /usr/sbin/chown -R ${CONSOLEUSER}:wheel "$TOOLSDIR/$TYPE"
|
||||
fi
|
||||
/usr/bin/sudo /usr/sbin/chown -R "${CONSOLEUSER}":wheel "$TOOLSDIR/$TYPE"
|
||||
|
||||
# make a symbolic link to help with interactive use
|
||||
if [[ "${PYTHON_MAJOR_VERSION}" == "3.9" ]]; then
|
||||
/bin/ln -s "$PYTHON_BIN_NEW" "$TOOLSDIR/$TYPE/payload/usr/local/bin/managed_python3"
|
||||
fi
|
||||
if [[ "${PYTHON_MAJOR_VERSION}" == "3.10" ]]; then
|
||||
/bin/ln -s "$PYTHON_BIN_NEW" "$TOOLSDIR/$TYPE/payload/usr/local/bin/managed_python3"
|
||||
fi
|
||||
if [[ "${PYTHON_MAJOR_VERSION}" == "3.11" ]]; then
|
||||
/bin/ln -s "$PYTHON_BIN_NEW" "$TOOLSDIR/$TYPE/payload/usr/local/bin/managed_python3"
|
||||
fi
|
||||
if [[ "${PYTHON_MAJOR_VERSION}" == "3.12" ]]; then
|
||||
/bin/ln -s "$PYTHON_BIN_NEW" "$TOOLSDIR/$TYPE/payload/usr/local/bin/managed_python3"
|
||||
fi
|
||||
if [[ "${PYTHON_MAJOR_VERSION}" == "3.13" ]]; then
|
||||
/bin/ln -s "$PYTHON_BIN_NEW" "$TOOLSDIR/$TYPE/payload/usr/local/bin/managed_python3"
|
||||
fi
|
||||
/bin/ln -s "$PYTHON_BIN_NEW" "$TOOLSDIR/$TYPE/payload/usr/local/bin/managed_python3"
|
||||
}
|
||||
|
||||
SB_RESULT="$?"
|
||||
if [ "${SB_RESULT}" != "0" ]; then
|
||||
echo "Failed create managed_python3 object" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
download_tool() {
|
||||
local name="$1" sha="$2" url="$3" zip_path="$4" dest="$5"
|
||||
echo "Downloading $name @ $sha"
|
||||
/bin/rm -rf "$zip_path" "$dest"
|
||||
/usr/bin/curl -fL "$url" -o "$zip_path"
|
||||
/usr/bin/unzip -q "$zip_path" -d "$dest"
|
||||
}
|
||||
|
||||
# build the framework
|
||||
# Force the C path depending on the version of Python to allow tools like cffi/xattr to build without wheels otherwise it errors
|
||||
# Can't use Apple's headers for 3.10 and higher as they are (currently) 3.9
|
||||
# C_INCLUDE_PATH="/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/Current/Headers/"
|
||||
build_framework() {
|
||||
export C_INCLUDE_PATH="/Library/ManagedFrameworks/Python/Python.framework/Versions/Current/Headers/"
|
||||
local rp_extract="${RP_BINDIR}/relocatable-python-${RP_SHA}"
|
||||
"${rp_extract}/make_relocatable_python_framework.py" \
|
||||
--baseurl "${PYTHON_BASEURL}" \
|
||||
--python-version "${PYTHON_VERSION}" \
|
||||
--os-version 11 \
|
||||
--upgrade-pip \
|
||||
--no-unsign \
|
||||
--pip-requirements "${TOOLSDIR}/requirements_${TYPE}.txt" \
|
||||
--destination "${FRAMEWORKDIR}"
|
||||
|
||||
export C_INCLUDE_PATH="/Library/ManagedFrameworks/Python/Python.framework/Versions/Current/Headers/"
|
||||
/bin/mv "${FRAMEWORKDIR}/Python.framework" \
|
||||
"$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework"
|
||||
}
|
||||
|
||||
C_INCLUDE_PATH="/Library/ManagedFrameworks/Python/Python.framework/Versions/Current/Headers/" RP_EXTRACT_BINDIR="${RP_BINDIR}/relocatable-python-${RP_SHA}"
|
||||
"${RP_EXTRACT_BINDIR}/make_relocatable_python_framework.py" \
|
||||
--baseurl "${PYTHON_BASEURL}" \
|
||||
--python-version "${PYTHON_VERSION}" \
|
||||
--os-version "${MACOS_VERSION}" \
|
||||
--upgrade-pip \
|
||||
--no-unsign \
|
||||
--pip-requirements "${TOOLSDIR}/requirements_${TYPE}.txt" \
|
||||
--destination "${FRAMEWORKDIR}"
|
||||
codesign_framework() {
|
||||
local identity="${APPLICATION_ID:--}" # `-` means ad-hoc
|
||||
local framework_root="$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework"
|
||||
local versioned="$framework_root/Versions/${PYTHON_BIN_VERSION}"
|
||||
|
||||
RP_RESULT="$?"
|
||||
if [ "${RP_RESULT}" != "0" ]; then
|
||||
echo "Error running relocatable-python tool: ${RP_RESULT}" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# move the framework to the Python package folder
|
||||
echo "Moving Python.framework to payload folder"
|
||||
/bin/mv "${FRAMEWORKDIR}/Python.framework" "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework"
|
||||
|
||||
RP_RESULT2="$?"
|
||||
if [ "${RP_RESULT2}" != "0" ]; then
|
||||
echo "Failed to move Python framework, likely due to a bug with relocatable python" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# confirm truly universal
|
||||
TOTAL_DYLIB=$(/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib" -name "*.dylib" | /usr/bin/wc -l | /usr/bin/xargs)
|
||||
UNIVERSAL_DYLIB=$(/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib" -name "*.dylib" | /usr/bin/xargs file | /usr/bin/grep "2 architectures" | /usr/bin/wc -l | /usr/bin/xargs)
|
||||
if [ "${TOTAL_DYLIB}" != "${UNIVERSAL_DYLIB}" ] ; then
|
||||
echo "Dynamic Libraries do not match, resulting in a non-universal Python framework."
|
||||
echo "Total Dynamic Libraries found: ${TOTAL_DYLIB}"
|
||||
echo "Universal Dynamic Libraries found: ${UNIVERSAL_DYLIB}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Dynamic Libraries are confirmed as universal"
|
||||
|
||||
TOTAL_SO=$(/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib" -name "*.so" | /usr/bin/wc -l | /usr/bin/xargs)
|
||||
UNIVERSAL_SO=$(/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib" -name "*.so" | /usr/bin/xargs file | /usr/bin/grep "2 architectures" | /usr/bin/wc -l | /usr/bin/xargs)
|
||||
if [ "${TOTAL_SO}" != "${UNIVERSAL_SO}" ] ; then
|
||||
echo "Shared objects do not match, resulting in a non-universal Python framework."
|
||||
echo "Total shared objects found: ${TOTAL_SO}"
|
||||
echo "Universal shared objects found: ${UNIVERSAL_SO}"
|
||||
UNIVERSAL_SO_ARRAY=("${(@f)$(/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib" -name "*.so" | /usr/bin/xargs file | /usr/bin/grep "2 architectures" | awk '{print $1;}' | sed 's/:*$//g')}")
|
||||
TOTAL_SO_ARRAY=("${(@f)$(/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib" -name "*.so" )}")
|
||||
echo ${TOTAL_SO_ARRAY[@]} ${UNIVERSAL_SO_ARRAY[@]} | tr ' ' '\n' | sort | uniq -u
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Shared objects are confirmed as universal"
|
||||
|
||||
# re-sign the framework so it will run on Apple Silicon
|
||||
if [ -n "$3" ]; then
|
||||
echo "Adding developer id code signing so the framework will run on Apple Silicon..."
|
||||
/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/bin" -type f -perm -u=x -exec /usr/bin/codesign --sign "$3" --timestamp --preserve-metadata=identifier,entitlements,flags,runtime -f {} \;
|
||||
/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib" -type f -perm -u=x -exec /usr/bin/codesign --sign "$3" --timestamp --preserve-metadata=identifier,entitlements,flags,runtime -f {} \;
|
||||
/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib" -type f -name "*dylib" -exec /usr/bin/codesign --sign "$3" --timestamp --preserve-metadata=identifier,entitlements,flags,runtime -f {} \;
|
||||
/usr/bin/codesign --sign "$3" --timestamp --deep --force --preserve-metadata=identifier,entitlements,flags,runtime "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/Resources/Python.app"
|
||||
/usr/bin/codesign --sign "$3" --timestamp --force --preserve-metadata=identifier,entitlements,flags,runtime "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/Python"
|
||||
/usr/bin/codesign --sign "$3" --timestamp --force --preserve-metadata=identifier,entitlements,flags,runtime "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/Current/Python"
|
||||
else
|
||||
echo "Adding ad-hoc code signing so the framework will run on Apple Silicon..."
|
||||
/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/bin" -type f -perm -u=x -exec /usr/bin/codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f {} \;
|
||||
/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib" -type f -perm -u=x -exec /usr/bin/codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f {} \;
|
||||
/usr/bin/find "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib" -type f -name "*dylib" -exec /usr/bin/codesign -s - --preserve-metadata=identifier,entitlements,flags,runtime -f {} \;
|
||||
/usr/bin/codesign -s - --deep --force --preserve-metadata=identifier,entitlements,flags,runtime "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/Resources/Python.app"
|
||||
/usr/bin/codesign -s - --force --preserve-metadata=identifier,entitlements,flags,runtime "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/Python"
|
||||
/usr/bin/codesign -s - --force --preserve-metadata=identifier,entitlements,flags,runtime "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}Python3.framework/Versions/Current/Python"
|
||||
fi
|
||||
|
||||
# Print out some information about the signatures
|
||||
/usr/sbin/spctl -a -vvvv "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/Python"
|
||||
/usr/sbin/spctl -a -vvvv "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/Python3.framework/Versions/${PYTHON_BIN_VERSION}/lib/libssl.1.1.dylib"
|
||||
|
||||
# take ownership of the payload folder
|
||||
echo "Taking ownership of the Payload directory"
|
||||
/usr/bin/sudo /usr/sbin/chown -R ${CONSOLEUSER}:wheel "$TOOLSDIR/$TYPE"
|
||||
|
||||
# Download specific version of munki-pkg
|
||||
echo "Downloading munki-pkg tool from github..."
|
||||
if [ -f "${MP_ZIP}" ]; then
|
||||
/usr/bin/sudo /bin/rm -rf ${MP_ZIP}
|
||||
fi
|
||||
/usr/bin/curl https://github.com/munki/munki-pkg/archive/${MP_SHA}.zip -L -o ${MP_ZIP}
|
||||
if [ -d ${MP_BINDIR} ]; then
|
||||
/usr/bin/sudo /bin/rm -rf ${MP_BINDIR}
|
||||
fi
|
||||
/usr/bin/unzip ${MP_ZIP} -d ${MP_BINDIR}
|
||||
DL_RESULT="$?"
|
||||
if [ "${DL_RESULT}" != "0" ]; then
|
||||
echo "Error downloading munki-pkg tool: ${DL_RESULT}" 1>&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create outputs folder
|
||||
/bin/mkdir -p "$TOOLSDIR/outputs"
|
||||
|
||||
#
|
||||
/bin/cp "${TOOLSDIR}/preinstall-cleanup" "$TOOLSDIR/$TYPE/scripts/preinstall"
|
||||
|
||||
if [ -n "$2" ]; then
|
||||
# Create the json file for munki-pkg (signed)
|
||||
/bin/cat << SIGNED_JSONFILE > "$TOOLSDIR/$TYPE/build-info.json"
|
||||
{
|
||||
"ownership": "recommended",
|
||||
"suppress_bundle_relocation": true,
|
||||
"identifier": "io.macadmins.python.$TYPE",
|
||||
"postinstall_action": "none",
|
||||
"distribution_style": true,
|
||||
"version": "$AUTOMATED_PYTHON_BUILD",
|
||||
"name": "python_${TYPE}_signed-$AUTOMATED_PYTHON_BUILD.pkg",
|
||||
"install_location": "/",
|
||||
"preserve_xattr": true,
|
||||
"signing_info": {
|
||||
"identity": "$2",
|
||||
"timestamp": true
|
||||
}
|
||||
}
|
||||
SIGNED_JSONFILE
|
||||
# Create the signed pkg
|
||||
"${MP_BINDIR}/munki-pkg-${MP_SHA}/munkipkg" "$TOOLSDIR/$TYPE"
|
||||
PKG_RESULT="$?"
|
||||
if [ "${PKG_RESULT}" != "0" ]; then
|
||||
echo "Could not sign package: ${PKG_RESULT}" 1>&2
|
||||
exit 1
|
||||
else
|
||||
if [ -n "$6" ]; then
|
||||
# Notarize and staple the package
|
||||
$XCODE_NOTARY_PATH store-credentials --apple-id "opensource@macadmins.io" --team-id "T4SK8ZXCXG" --password "$NOTARY_APP_PASSWORD" macadminpython
|
||||
# If these fail, it will bail on the entire process
|
||||
$XCODE_NOTARY_PATH submit "$TOOLSDIR/$TYPE/build/python_${TYPE}_signed-$AUTOMATED_PYTHON_BUILD.pkg" --keychain-profile "macadminpython" --wait
|
||||
$XCODE_STAPLER_PATH staple "$TOOLSDIR/$TYPE/build/python_${TYPE}_signed-$AUTOMATED_PYTHON_BUILD.pkg"
|
||||
if [[ "$identity" == "-" ]]; then
|
||||
echo "Ad-hoc signing framework"
|
||||
else
|
||||
echo "Signing framework with identity: $identity"
|
||||
fi
|
||||
# Move the signed + notarized pkg
|
||||
/bin/mv "$TOOLSDIR/$TYPE/build/python_${TYPE}_signed-$AUTOMATED_PYTHON_BUILD.pkg" "$OUTPUTSDIR"
|
||||
fi
|
||||
else
|
||||
echo "no signing identity passed, skipping signed package creation"
|
||||
fi
|
||||
|
||||
# Zip and move the framework
|
||||
ZIPFILE="Python3.framework_$TYPE-$AUTOMATED_PYTHON_BUILD.zip"
|
||||
/usr/bin/ditto -c -k --sequesterRsrc "$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/" ${ZIPFILE}
|
||||
/bin/mv ${ZIPFILE} "$OUTPUTSDIR"
|
||||
local -a cs_args
|
||||
if [[ "$identity" == "-" ]]; then
|
||||
cs_args=(--preserve-metadata=identifier,entitlements,flags,runtime -f)
|
||||
else
|
||||
cs_args=(--timestamp --preserve-metadata=identifier,entitlements,flags,runtime -f)
|
||||
fi
|
||||
|
||||
# Ensure outputs directory is owned by the current user
|
||||
/usr/bin/sudo /usr/sbin/chown -R ${CONSOLEUSER}:wheel "$OUTPUTSDIR"
|
||||
/usr/bin/find "$versioned/bin" -type f -perm -u=x -exec \
|
||||
/usr/bin/codesign -s "$identity" "${cs_args[@]}" {} \;
|
||||
/usr/bin/find "$versioned/lib" -type f -perm -u=x -exec \
|
||||
/usr/bin/codesign -s "$identity" "${cs_args[@]}" {} \;
|
||||
/usr/bin/find "$versioned/lib" -type f -name "*dylib" -exec \
|
||||
/usr/bin/codesign -s "$identity" "${cs_args[@]}" {} \;
|
||||
/usr/bin/codesign -s "$identity" --deep "${cs_args[@]}" "$versioned/Resources/Python.app"
|
||||
/usr/bin/codesign -s "$identity" "${cs_args[@]}" "$versioned/Python"
|
||||
/usr/bin/codesign -s "$identity" "${cs_args[@]}" "$framework_root/Versions/Current/Python"
|
||||
|
||||
# Cleanup the temporary files
|
||||
/usr/bin/sudo /bin/rm -rf "$TOOLSDIR/$TYPE"
|
||||
/usr/bin/sudo /bin/rm -rf "${FRAMEWORKDIR}"
|
||||
/usr/sbin/spctl -a -vvvv "$versioned/Python" || true
|
||||
}
|
||||
|
||||
build_pkg() {
|
||||
/bin/mkdir -p "$OUTPUTSDIR"
|
||||
/bin/cp "${TOOLSDIR}/preinstall-cleanup" "$TOOLSDIR/$TYPE/scripts/preinstall"
|
||||
|
||||
if [[ -z "$INSTALLER_ID" ]]; then
|
||||
echo "No installer identity provided; skipping signed pkg"
|
||||
return
|
||||
fi
|
||||
|
||||
/bin/cat <<JSON > "$TOOLSDIR/$TYPE/build-info.json"
|
||||
{
|
||||
"ownership": "recommended",
|
||||
"suppress_bundle_relocation": true,
|
||||
"identifier": "io.macadmins.python.$TYPE",
|
||||
"postinstall_action": "none",
|
||||
"distribution_style": true,
|
||||
"version": "$AUTOMATED_PYTHON_BUILD",
|
||||
"name": "python_${TYPE}_signed-$AUTOMATED_PYTHON_BUILD.pkg",
|
||||
"install_location": "/",
|
||||
"preserve_xattr": true,
|
||||
"signing_info": {
|
||||
"identity": "$INSTALLER_ID",
|
||||
"timestamp": true
|
||||
}
|
||||
}
|
||||
JSON
|
||||
|
||||
"${MP_BINDIR}/munki-pkg-${MP_SHA}/munkipkg" "$TOOLSDIR/$TYPE"
|
||||
}
|
||||
|
||||
notarize_and_staple() {
|
||||
if [[ -z "$NOTARY_PASSWORD" || -z "$INSTALLER_ID" ]]; then
|
||||
echo "Skipping notarization (no notary password or installer id)"
|
||||
return
|
||||
fi
|
||||
local xcode_dev xcode_notary xcode_stapler pkg
|
||||
xcode_dev="$(/usr/bin/xcode-select -p)"
|
||||
xcode_notary="$xcode_dev/usr/bin/notarytool"
|
||||
xcode_stapler="$xcode_dev/usr/bin/stapler"
|
||||
pkg="$TOOLSDIR/$TYPE/build/python_${TYPE}_signed-$AUTOMATED_PYTHON_BUILD.pkg"
|
||||
|
||||
"$xcode_notary" store-credentials \
|
||||
--apple-id "opensource@macadmins.io" \
|
||||
--team-id "T4SK8ZXCXG" \
|
||||
--password "$NOTARY_PASSWORD" \
|
||||
macadminpython
|
||||
"$xcode_notary" submit "$pkg" --keychain-profile macadminpython --wait
|
||||
"$xcode_stapler" staple "$pkg"
|
||||
/bin/mv "$pkg" "$OUTPUTSDIR"
|
||||
}
|
||||
|
||||
zip_framework() {
|
||||
local zipfile="Python3.framework_$TYPE-$AUTOMATED_PYTHON_BUILD.zip"
|
||||
/usr/bin/ditto -c -k --sequesterRsrc \
|
||||
"$TOOLSDIR/$TYPE/payload${FRAMEWORKDIR}/" "$zipfile"
|
||||
/bin/mv "$zipfile" "$OUTPUTSDIR"
|
||||
/usr/bin/sudo /usr/sbin/chown -R "${CONSOLEUSER}":wheel "$OUTPUTSDIR"
|
||||
}
|
||||
|
||||
cleanup() {
|
||||
/usr/bin/sudo /bin/rm -rf "$TOOLSDIR/$TYPE"
|
||||
/usr/bin/sudo /bin/rm -rf "$FRAMEWORKDIR"
|
||||
}
|
||||
|
||||
# --- Main ---
|
||||
parse_args "$@"
|
||||
echo "Building Python framework — $PYTHON_VERSION"
|
||||
prepare_ci_env
|
||||
derive_build_version
|
||||
prepare_build_dirs
|
||||
download_tool relocatable-python "$RP_SHA" \
|
||||
"https://github.com/gregneagle/relocatable-python/archive/${RP_SHA}.zip" \
|
||||
"$RP_ZIP" "$RP_BINDIR"
|
||||
download_tool munki-pkg "$MP_SHA" \
|
||||
"https://github.com/munki/munki-pkg/archive/${MP_SHA}.zip" \
|
||||
"$MP_ZIP" "$MP_BINDIR"
|
||||
build_framework
|
||||
codesign_framework
|
||||
build_pkg
|
||||
notarize_and_staple
|
||||
zip_framework
|
||||
cleanup
|
||||
echo "Done."
|
||||
|
||||
Reference in New Issue
Block a user