-
Notifications
You must be signed in to change notification settings - Fork 1
224 lines (201 loc) · 11.5 KB
/
installer_macos.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
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