macOS Specify numpy version on mac/win builds. #105
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build | |
run-name: macOS ${{ github.event.pull_request.title }} ${{ github.event.head_commit.message }} | |
# see https://docs.github.com/en/webhooks/webhook-events-and-payloads#pull_request | |
on: | |
push: | |
branches: [ master ] | |
pull_request: | |
branches: [ master ] | |
jobs: | |
build: | |
runs-on: ${{ matrix.os }} | |
strategy: | |
matrix: | |
# installer is always built with a specific version of Python | |
os: [macos-12] | |
python-version: ["3.12"] | |
architecture: [x86_64] | |
include: | |
- os: macos-14 | |
python-version: "3.12" | |
architecture: arm64 | |
env: | |
# define environment and configuration variables here. | |
# app_name is how the application will be named on the disk image. | |
# product_name is the name of the disk image (.dmg) that gets built. | |
# python_version specifies which version of Python to use. | |
# this should be regularly updated to the latest usable version. | |
app_name: "Nion Swift 16" | |
product_name: NionSwift-16.11.0 | |
python_version: 3.12.4 | |
# version of nionui-tool to use as basis of tool build | |
tool_source_version: 0.4.24 | |
# qt_version, qt_platform specify which version of Qt to use. | |
# these should be regularly updated to the latest usable version. | |
# these are only used if building the tool from source. | |
qt_version: 6.7.2 | |
qt_platform: macos | |
# numpy version | |
numpy_version: 1.26.4 | |
steps: | |
- uses: actions/checkout@v4 | |
# first set up Python | |
- name: Set up Python ${{ matrix.python-version }} | |
uses: actions/setup-python@v5 | |
with: | |
python-version: ${{ matrix.python-version }} | |
- name: Display Python version | |
run: python -c "import sys; print(sys.version)" | |
# next build the installer (it's a disk image with the application). | |
- name: Build | |
env: | |
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} | |
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }} | |
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }} | |
MACOS_DEVELOPER_TEAM_ID: ${{ secrets.MACOS_DEVELOPER_TEAM_ID }} | |
run: | | |
# NOTE: not building from source fails to codesign because the framework soft links aren't preserved. | |
# TODO: fix installed tool builds. | |
# for now, build from source always but use a specific source version if desired. | |
build_from_source=true | |
do_codesign=true | |
# the first step in the build process is to create a virtual environment to be | |
# used by the installation scripts and activate it. also update pip. | |
mkdir venv | |
python -m venv venv/installer | |
source venv/installer/bin/activate | |
python -m pip install --upgrade pip | |
# grab nionswift-tool for splash and toml files | |
git clone https://github.com/nion-software/nionswift-tool.git --branch $tool_source_version --single-branch | |
# OPTIONAL: build tool from source | |
# next, install Qt using the aqt tool. only install the required Qt modules. | |
# then configure the Qt6_DIR variable (used by cmake), install numpy (used by | |
# cmake). then git clone the tool source code. finally run cmake. | |
if $build_from_source; then | |
pip install aqtinstall | |
mkdir Qt | |
echo "Installing Qt $qt_version ($qt_platform)" | |
python -m aqt install-qt --outputdir Qt mac desktop $qt_version clang_64 | |
ls Qt | |
export Qt6=`pwd`/Qt/$qt_version/$qt_platform | |
export Qt6_DIR="$Qt6/lib/cmake/Qt6" | |
# see https://bugreports.qt.io/browse/QTBUG-97615 | |
export CMAKE_PREFIX_PATH="$Qt6" | |
pip install numpy==$numpy_version | |
git clone https://github.com/nion-software/nionui-tool.git | |
pushd nionui-tool/launcher | |
PYTHON=`python -c "import sys; print(sys.executable, end='')"` | |
cmake CMakeLists.txt -DPython3_EXECUTABLE="$PYTHON" | |
cmake --build . --config Release | |
popd | |
fi | |
# now install a fresh Python from python.org. use a version from the earliest supported target OS. | |
# on macOS, manually extract Python from the pkg. then run mac_fix_framework to fix dylib references. | |
# the default Python assumes it is installed in /Library/Frameworks. However, the tool application | |
# needs to be relocatable. So mac_fix_frameworks adjusts all of the dylibs to be located relative | |
# to the tool executable (rpath). | |
# finally copy the extracted Python to the distribution directory, create a new environment (python-nionswift), | |
# and activate the environment. | |
curl -O https://www.python.org/ftp/python/$python_version/python-$python_version-macos11.pkg | |
# print md5 to be used for verification if desired. | |
md5 python-$python_version-macos11.pkg | |
mkdir python-$python_version-macos11 | |
xar -xf python-$python_version-macos11.pkg -C python-$python_version-macos11 | |
rm -rf Python.framework; mkdir Python.framework | |
tar -zxf python-$python_version-macos11/Python_Framework.pkg/Payload -C Python.framework | |
echo "Running fix" | |
python mac_fix_framework.py Python.framework/ | |
python3.12 -m venv python-nionswift | |
source python-nionswift/bin/activate | |
# install latest version of nionswift (UNUSED EXCEPT FOR DEBUGGING) | |
# git clone https://github.com/nion-software/nionutils.git | |
# git clone https://github.com/nion-software/niondata.git | |
# git clone https://github.com/nion-software/nionui.git | |
# git clone https://github.com/nion-software/nionswift.git | |
# git clone https://github.com/nion-software/nionswift-io.git | |
# git clone https://github.com/nion-software/nionswift-instrumentation-kit.git | |
# git clone https://github.com/nion-software/eels-analysis.git nionswift-eels-analysis | |
# git clone https://github.com/nion-software/nionswift-usim.git | |
# python -m pip install ./nionutils | |
# python -m pip install ./niondata | |
# python -m pip install ./nionui | |
# python -m pip install ./nionswift | |
# python -m pip install ./nionswift-instrumentation-kit | |
# python -m pip install ./nionswift-eels-analysis | |
# python -m pip install ./nionswift-usim | |
# install the latest released version in the activated Python environment. | |
python -m pip install nionswift nionswift-tool nionswift-eels-analysis nionswift-usim | |
# deactivate the environment since everything is installed. | |
deactivate | |
# OPTIONAL: build tool from source | |
# copy the application built in the last step to the distribution directory. | |
rm -rf dist; mkdir -p dist/ | |
if $build_from_source; then | |
cp -R "nionui-tool/launcher/build/Nion UI Launcher.app" "dist/$app_name.app" | |
mv "dist/$app_name.app/Contents/MacOS/Nion UI Launcher" "dist/$app_name.app/Contents/MacOS/Nion Swift" | |
cp "python-nionswift/bin/Nion Swift.app/Contents/Info.plist" "dist/$app_name.app/Contents/Info.plist" | |
else | |
cp -R "python-nionswift/bin/Nion Swift.app" "dist/$app_name.app" | |
fi | |
# configure the environment by copying site-packages, the toolconfig.toml file, application icon, | |
# and splash screen. | |
# strip arm64 leaving only native (for now) | |
ditto "dist/$app_name.app/Contents/MacOS/Nion Swift" --arch ${{ matrix.architecture }} "dist/$app_name.app/Contents/MacOS/Nion Swift ${{ matrix.architecture }}" | |
mv "dist/$app_name.app/Contents/MacOS/Nion Swift ${{ matrix.architecture }}" "dist/$app_name.app/Contents/MacOS/Nion Swift" | |
# copy the Python framework. must go after copying the app. | |
mv Python.framework "dist/$app_name.app/Contents/Frameworks" | |
# site packages need to go in the right spot. | |
cp -R python-nionswift/lib/python3.12/site-packages/* "dist/$app_name.app/Contents/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages" | |
# config files for the tool. to allow codesigning, toolconfig is put into Resources. | |
cp mac_toolconfig.toml "dist/$app_name.app/Contents/Resources/toolconfig.toml" | |
cp "nionswift-tool/launcher/Graphics/MacIcon.icns" "dist/$app_name.app/Contents/Resources/" | |
cp "nionswift-tool/launcher/Artwork/splash_600x400.png" "dist/$app_name.app/Contents/Resources/splash.png" | |
if $do_codesign; then | |
# codesign | |
echo -n "$MACOS_CERTIFICATE" | base64 -d > certificate.p12 | |
echo "Create keychain" | |
security create-keychain -p "$MACOS_KEYCHAIN_PASSWORD" build.keychain | |
echo "Set default keychain" | |
security default-keychain -s build.keychain | |
echo "Unlock keychain" | |
security unlock-keychain -p "$MACOS_KEYCHAIN_PASSWORD" build.keychain | |
echo "Import certificate (disabled)" | |
security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign | |
echo "Find identity" | |
security find-identity -v | |
echo "Set key partition list (avoids dialog)" | |
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_KEYCHAIN_PASSWORD" build.keychain | |
echo "Code sign" | |
# remove files that will not be signed and have can be regenerated | |
find "dist/$app_name.app" -name "__pycache__" -exec rm -rf {} \; -prune | |
find "dist/$app_name.app" -name "*.pyc" -exec rm -rf {} \; -prune | |
# sign files from the inside out. first ensure that dylibs are executable. then find all executable files | |
# and sign them. then sign apps within frameworks. then sign frameworks. then sign the app itself. | |
find "dist/$app_name.app" -name "*.dylib" -exec chmod +x {} \; | |
find "dist/$app_name.app" -type f -perm +111 -and -not -name "*.py" -exec /usr/bin/codesign -s "$MACOS_DEVELOPER_TEAM_ID" {} --options runtime --timestamp --deep --force --verbose \; | |
find "dist/$app_name.app/Contents/Frameworks" -name "*.app" -exec /usr/bin/codesign -s "$MACOS_DEVELOPER_TEAM_ID" {} --options runtime --timestamp --deep --force --verbose \; | |
find "dist/$app_name.app" -name "*.framework" -exec /usr/bin/codesign -s "$MACOS_DEVELOPER_TEAM_ID" {} --options runtime --timestamp --deep --force --verbose \; | |
/usr/bin/codesign -s "$MACOS_DEVELOPER_TEAM_ID" "dist/$app_name.app" --options runtime --timestamp --deep --force --verbose | |
# self signing for testing | |
# /usr/bin/codesign -s - "dist/$app_name.app" --deep --force --verbose | |
rm certificate.p12 | |
fi | |
# finally, create the disk image | |
echo "Create disk image ${{ matrix.architecture }}" | |
hdiutil create tmp.dmg -ov -volname "$app_name Install ${{ matrix.architecture }}" -fs APFS -srcfolder "dist/" | |
hdiutil convert tmp.dmg -format UDZO -o "$product_name-${{ matrix.architecture }}.dmg" | |
# notarize | |
- name: Notarize DMG | |
uses: cocoalibs/xcode-notarization-action@v1 | |
with: | |
app-path: "$product_name-${{ matrix.architecture }}.dmg" | |
apple-id: ${{ secrets.MACOS_DEVELOPER_APPLE_ID }} | |
password: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }} | |
team-id: ${{ secrets.MACOS_DEVELOPER_TEAM_ID }} | |
xcode-path: /Applications/Xcode.app | |
# finally, upload the artifacts of the build so that they are available on the GitHub action page. | |
- name: Upload Product DMG | |
uses: actions/upload-artifact@v4 | |
with: | |
name: ${{ env.product_name }}-${{ matrix.architecture }}.dmg | |
path: ${{ env.product_name }}-${{ matrix.architecture }}.dmg |