7777 <@ j.id @> :
7878
7979 name : <@ j.name @>
80+ <% if j.os == 'macOS' and j.type == 'notarize' % >
81+ environment : production
82+ <% endif % >
8083 runs-on : <@ j.platform @>
8184 needs : [analyze, <@ j.needs|join(', ') @>]
8285 if : >-
8588 && (needs.<@ need_id @>.result == 'success' || needs.<@ need_id @>.result == 'skipped')
8689 <% endfor % >
8790 && needs.analyze.outputs.<@ j.id @>_skip_job == 'no'
91+ <% if j.os == 'macOS' and j.type == 'notarize' % >
92+ env :
93+ # Code signing
94+ MACOS_CODESIGN_IDENTITY : ${{ secrets.MACOS_CODESIGN_IDENTITY }}
95+ MACOS_CODESIGN_CERT_P12_BASE64 : ${{ secrets.MACOS_CODESIGN_CERT_P12_BASE64 }}
96+ MACOS_CODESIGN_CERT_PASSWORD : ${{ secrets.MACOS_CODESIGN_CERT_PASSWORD }}
97+ MACOS_TEMP_KEYCHAIN_NAME : plover-build
98+ MACOS_TEMP_KEYCHAIN_PASSWORD : ${{ secrets.MACOS_TEMP_KEYCHAIN_PASSWORD }}
99+ # Notarization
100+ MACOS_NOTARIZE_ENABLED : ${{ secrets.MACOS_NOTARIZE_ENABLED || '0' }}
101+ MACOS_NOTARIZE_TEAM_ID : ${{ secrets.MACOS_NOTARIZE_TEAM_ID }}
102+ MACOS_NOTARIZE_KEY_ID : ${{ secrets.MACOS_NOTARIZE_KEY_ID }}
103+ MACOS_NOTARIZE_ISSUER_ID : ${{ secrets.MACOS_NOTARIZE_ISSUER_ID }}
104+ MACOS_NOTARIZE_KEY_CONTENTS : ${{ secrets.MACOS_NOTARIZE_KEY_CONTENTS }}
105+ <% endif % >
88106
89107 steps :
90108
@@ -120,7 +138,7 @@ jobs:
120138 path : .cache
121139 key : <@ cache_epoch @>_${{ steps.set_cache.outputs.cache_name }}_${{ hashFiles('reqs/constraints.txt'<% for d in (j.reqs + j.cache_extra_deps) %><@ ', %r' % d @><% endfor %>) }}
122140
123- <% if j.os == 'macOS' % >
141+ <% if j.os == 'macOS' and j.type != 'notarize' % >
124142 # To support older macOS versions, setup Python from an official installer.
125143 - name : Setup Python
126144 run : setup_osx_python '<@ j.python @>'
@@ -131,10 +149,12 @@ jobs:
131149 run : apt_get_install libdbus-1-dev libdbus-glib-1-dev libudev-dev libusb-1.0-0-dev libegl-dev libxkbcommon-x11-0
132150
133151 <% endif % >
152+ <% if j.type != 'notarize' % >
134153 - name : Setup Python environment
135154 run : setup_python_env -c reqs/constraints.txt<% for r in j.reqs %> -r <@ r @><% endfor %>
136155
137156
157+ <% endif % >
138158 <% if j.type == 'test_code_quality' % >
139159 - name : Run Ruff (format check)
140160 run : ruff format --check .
@@ -214,15 +234,49 @@ jobs:
214234
215235 <% endif % >
216236 <% if j.os == 'macOS' % >
217- - name : Build distribution (macOS DMG)
218- run : python setup.py -q bdist_dmg
237+ <% if j.type == 'build' and j.variant == 'macOS App' % >
238+ - name : Build distribution (macOS app)
239+ run : python setup.py -q bdist_app
240+
241+ - name : Pack app as tar (preserve symlinks)
242+ run : |
243+ rm -f dist/Plover.app.tgz
244+ tar -C dist -czf dist/Plover.app.tgz Plover.app
245+
246+ - name : Save app tarball to internal cache
247+ uses : <@ action_cache_save @>
248+ with :
249+ path : dist/Plover.app.tgz
250+ key : <@ cache_epoch @>_macos-app-raw-${{ github.run_id }}
251+
252+ <% elif j.type == 'build' and j.variant == 'macOS DMG' % >
253+ - name : Restore app tarball from internal cache (prefer notarized)
254+ uses : <@ action_cache_restore @>
255+ with :
256+ path : dist/Plover.app.tgz
257+ key : <@ cache_epoch @>_macos-app-notarized-${{ github.run_id }}
258+ restore-keys : <@ cache_epoch @>_macos-app-raw-${{ github.run_id }}
259+
260+ - name : Extract app tarball
261+ run : |
262+ mkdir -p dist
263+ tgz="$(ls -1 dist/*.tgz)"
264+ echo "Using: $tgz"
265+ tar -C dist -xzf "$tgz"
219266
220- - name : Archive artifact (macOS DMG)
267+ - name : Ensure app exists
268+ run : test -d dist/*.app
269+
270+ - name : Build distribution (macOS DMG from existing app)
271+ run : python setup.py -q bdist_dmg --skip-app-build
272+
273+ - name : Upload artifact (macOS DMG)
221274 uses : <@ action_upload_artifact @>
222275 with :
223276 name : macOS DMG
224277 path : dist/*.dmg
225-
278+ overwrite : true
279+ <% endif % >
226280 <% endif % >
227281 <% if j.os == 'Windows' % >
228282 - name : Build distributions (Windows)
@@ -246,6 +300,82 @@ jobs:
246300 <% endif % >
247301 # }}}
248302
303+ <% endif % >
304+ <% if j.os == 'macOS' and j.type == 'notarize' % >
305+ # Notarize {{{
306+
307+ <% if j.variant == 'macOS App' % >
308+ - name : Restore app tarball from internal cache
309+ uses : <@ action_cache_restore @>
310+ with :
311+ path : dist/Plover.app.tgz
312+ key : <@ cache_epoch @>_macos-app-raw-${{ github.run_id }}
313+
314+ - name : Extract app tarball
315+ run : |
316+ mkdir -p dist
317+ tar -C dist -xzf dist/*.tgz
318+
319+ - name : Install Developer ID certificate into temporary keychain
320+ if : ${{ env.MACOS_NOTARIZE_ENABLED == '1' }}
321+ run : install_dev_id_cert_into_temp_keychain
322+
323+ - name : Set codesign keychain env
324+ run : echo "MACOS_CODESIGN_KEYCHAIN=${MACOS_TEMP_KEYCHAIN_NAME}.keychain" >> $GITHUB_ENV
325+
326+ - name : Notarize & staple app
327+ run : |
328+ chmod +x osx/notarize_app.sh
329+ ./osx/notarize_app.sh dist/*.app
330+
331+ - name : Cleanup temporary keychain
332+ if : ${{ always() && env.MACOS_NOTARIZE_ENABLED == '1' }}
333+ run : cleanup_dev_id_temp_keychain
334+
335+ - name : Repack notarized app as tar
336+ run : |
337+ rm -f dist/Plover.app.tgz
338+ tar -C dist -czf dist/Plover.app.tgz Plover.app
339+
340+ - name : Save notarized app tarball to internal cache
341+ uses : <@ action_cache_save @>
342+ with :
343+ path : dist/Plover.app.tgz
344+ key : <@ cache_epoch @>_macos-app-notarized-${{ github.run_id }}
345+
346+ <% elif j.variant == 'macOS DMG' % >
347+ - name : Download artifact (macOS DMG)
348+ uses : <@ action_download_artifact @>
349+ with :
350+ name : macOS DMG
351+ path : dist
352+ pattern : " *.dmg"
353+
354+ - name : Install Developer ID certificate into temporary keychain
355+ if : ${{ env.MACOS_NOTARIZE_ENABLED == '1' }}
356+ run : install_dev_id_cert_into_temp_keychain
357+
358+ - name : Set codesign keychain env
359+ run : echo "MACOS_CODESIGN_KEYCHAIN=${MACOS_TEMP_KEYCHAIN_NAME}.keychain" >> $GITHUB_ENV
360+
361+ - name : Notarize & staple DMG
362+ run : |
363+ chmod +x osx/notarize_dmg.sh
364+ ./osx/notarize_dmg.sh dist/*.dmg
365+
366+ - name : Cleanup temporary keychain
367+ if : ${{ always() && env.MACOS_NOTARIZE_ENABLED == '1' }}
368+ run : cleanup_dev_id_temp_keychain
369+
370+ - name : Upload artifact (macOS DMG)
371+ uses : <@ action_upload_artifact @>
372+ with :
373+ name : macOS DMG
374+ path : dist/*.dmg
375+ overwrite : true
376+ <% endif % >
377+
378+ # }}}
249379 <% endif % >
250380 <% if skippy_enabled % >
251381 - name : Update skip cache 1
0 commit comments