Merge origin/rc175 (macOS signing and notarization) into rc175
Build Packages / Create release (push) Successful in 8s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 8m53s
Build Packages / build:portable:macos-arm64:nocuda (push) Failing after 10m37s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 14m38s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 19m15s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 19m49s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 21m41s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 23m5s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 23m47s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 25m48s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 19m39s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 28m52s
Build Packages / Generate python client (push) Successful in 26s
Build Packages / Build documentation (push) Successful in 1m0s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 20m25s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 17m44s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 21m46s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 23m32s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 22m48s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 18m46s
Build Packages / Unit tests (push) Successful in 1h37m57s
Build Packages / Validation Battery (push) Successful in 37m52s
Build Packages / Create release (push) Successful in 8s
Build Packages / build:rugnux:linux-aarch64:cuda (push) Successful in 8m53s
Build Packages / build:portable:macos-arm64:nocuda (push) Failing after 10m37s
Build Packages / build:portable:linux-x86_64:nocuda (push) Successful in 14m38s
Build Packages / build:portable:linux-x86_64:cuda (push) Successful in 19m15s
Build Packages / build:jfjoch:ubuntu2204:nocuda (push) Successful in 19m49s
Build Packages / build:portable:windows-x86_64:nocuda (push) Successful in 21m41s
Build Packages / build:jfjoch:rocky9:nocuda (push) Successful in 23m5s
Build Packages / build:jfjoch:rocky8:nocuda (push) Successful in 23m47s
Build Packages / build:portable:windows-x86_64:cuda (push) Successful in 25m48s
Build Packages / build:jfjoch:ubuntu2404:nocuda (push) Successful in 19m39s
Build Packages / HDF5 consumer tests (DIALS, XDS) (push) Successful in 28m52s
Build Packages / Generate python client (push) Successful in 26s
Build Packages / Build documentation (push) Successful in 1m0s
Build Packages / build:jfjoch:rocky8:cuda-sls9 (push) Successful in 20m25s
Build Packages / build:jfjoch:ubuntu2204:cuda (push) Successful in 17m44s
Build Packages / build:jfjoch:rocky8:cuda (push) Successful in 21m46s
Build Packages / build:jfjoch:rocky9:cuda-sls9 (push) Successful in 23m32s
Build Packages / build:jfjoch:rocky9:cuda (push) Successful in 22m48s
Build Packages / build:jfjoch:ubuntu2404:cuda (push) Successful in 18m46s
Build Packages / Unit tests (push) Successful in 1h37m57s
Build Packages / Validation Battery (push) Successful in 37m52s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -244,11 +244,25 @@ jobs:
|
||||
- name: Clean previous build
|
||||
shell: bash
|
||||
run: rm -rf build-macos # host runners keep the workspace between runs
|
||||
# The Developer ID in the runner's login keychain signs the app (hardened runtime, secure
|
||||
# timestamp) so the .dmg can be notarized below; without it the build must not go on, as an
|
||||
# ad-hoc signed download is refused by Gatekeeper. Passed on as the certificate's SHA-1 (see
|
||||
# JFJOCH_MACOS_SIGNING_IDENTITY in CMakeLists.txt). The notarytool credentials sit in the same
|
||||
# keychain under the profile jfjoch-notary (xcrun notarytool store-credentials --keychain
|
||||
# ~/Library/Keychains/login.keychain-db: that file never locks here, the default data-protection
|
||||
# keychain does with the screen).
|
||||
- name: Signing identity
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
identity=$(security find-identity -v -p codesigning | sed -n 's/^ *[0-9]*) \([0-9A-F]*\) "Developer ID Application: .*/\1/p' | head -1)
|
||||
if [ -z "$identity" ]; then echo "no Developer ID Application identity in the runner's keychain"; exit 1; fi
|
||||
echo "MACOS_SIGNING_IDENTITY=$identity" >> "$GITHUB_ENV"
|
||||
- name: Configure
|
||||
shell: bash
|
||||
# JFJOCH_VIEWER_ONLY is forced on macOS. No -mcpu: the arm64 baseline is what every Apple
|
||||
# Silicon Mac runs, and NEON is part of it.
|
||||
run: cmake -S . -B build-macos -DCMAKE_BUILD_TYPE=Release -DJFJOCH_USE_CUDA=OFF -DCMAKE_PREFIX_PATH="$QT_PREFIX"
|
||||
run: cmake -S . -B build-macos -DCMAKE_BUILD_TYPE=Release -DJFJOCH_USE_CUDA=OFF -DCMAKE_PREFIX_PATH="$QT_PREFIX" -DJFJOCH_MACOS_SIGNING_IDENTITY="$MACOS_SIGNING_IDENTITY"
|
||||
# The quick cross-platform checks (tests/PortableTest.cpp and the [portable] cases), before
|
||||
# anything is packaged.
|
||||
- name: Portable tests
|
||||
@@ -274,11 +288,13 @@ jobs:
|
||||
# ... and nothing in the bundle may point at this machine's Qt (or anything else under /Users)
|
||||
find "$app" -type f \( -perm +111 -o -name '*.dylib' \) -exec otool -L {} + > /tmp/jfjv.libs 2>/dev/null || true
|
||||
if grep -q "/Users/" /tmp/jfjv.libs; then grep "/Users/" /tmp/jfjv.libs; echo "absolute path into the build machine"; exit 1; fi
|
||||
# the (ad-hoc) signature must survive everything installed into the bundle
|
||||
# the Developer ID signature must survive everything installed into the bundle, and carry
|
||||
# the hardened runtime that notarization demands
|
||||
codesign --verify --deep --strict "$app"
|
||||
# rugnux is in an archive of its own, not in the app
|
||||
find "$app" -name rugnux > /tmp/jfjv.extra
|
||||
if [ -s /tmp/jfjv.extra ]; then cat /tmp/jfjv.extra; echo "the bundle carries another program"; exit 1; fi
|
||||
codesign -d --verbose=2 "$app" 2>&1 | grep -q "flags=.*runtime" || { echo "no hardened runtime"; exit 1; }
|
||||
echo "self-contained and validly signed"
|
||||
- name: Verify rugnux is self-contained
|
||||
shell: bash
|
||||
@@ -293,17 +309,17 @@ jobs:
|
||||
# and it runs: with no arguments rugnux prints its usage
|
||||
"$bin" > /tmp/rgxm.out 2>&1 || true
|
||||
if ! grep -qi "usage" /tmp/rgxm.out; then cat /tmp/rgxm.out; echo "rugnux did not start"; exit 1; fi
|
||||
echo "self-contained and runs"
|
||||
# Notarization - needs an Apple Developer ID, which the project does not have yet. Once it
|
||||
# does, the app has to be signed with it (hardened runtime, secure timestamp) BEFORE cpack
|
||||
# packs it, e.g. by passing to macdeployqt, through qt_generate_deploy_app_script's
|
||||
# DEPLOY_TOOL_OPTIONS in viewer/CMakeLists.txt:
|
||||
# -codesign="Developer ID Application: <NAME> (<TEAMID>)" -hardened-runtime -timestamp
|
||||
# and the DMG then notarized and stapled here, with an app-specific password stored once on
|
||||
# the runner (xcrun notarytool store-credentials jfjoch-notary ...):
|
||||
# xcrun notarytool submit build-macos/jfjoch-viewer-*.dmg --keychain-profile jfjoch-notary --wait
|
||||
# xcrun stapler staple build-macos/jfjoch-viewer-*.dmg
|
||||
# Until then a downloaded DMG is blocked by Gatekeeper on first open (right-click > Open).
|
||||
# and carries the Developer ID signature with the hardened runtime that notarization demands
|
||||
codesign --verify --strict "$bin"
|
||||
codesign -d --verbose=2 "$bin" 2>&1 | grep -q "flags=.*runtime" || { echo "no hardened runtime"; exit 1; }
|
||||
echo "self-contained, runs and is validly signed"
|
||||
# Every push, not only a release: a rejection by Apple (a library macdeployqt left unsigned, an
|
||||
# entitlement the hardened runtime needs) should show up when the change is made, not when the
|
||||
# release is cut. Signs the image, waits for Apple's verdict, staples the ticket to the image;
|
||||
# the rugnux binary only gets its hash registered, a bare executable has nowhere to staple.
|
||||
- name: Notarize
|
||||
shell: bash
|
||||
run: tools/notarize_macos.sh build-macos/jfjoch-viewer-*.dmg build-macos/rugnux-*.tar.gz
|
||||
- name: Upload DMG and tgz to release
|
||||
if: github.event_name == 'workflow_dispatch' && (github.event.inputs.create_release == 'true' || github.event.inputs.create_release == true)
|
||||
shell: bash
|
||||
|
||||
@@ -79,6 +79,15 @@ SET(JFJOCH_VIEWER_BUILD OFF CACHE BOOL "Compile Jungfraujoch viewer")
|
||||
# artifact has to ship a runtime library beside its executable.
|
||||
SET(JFJOCH_RUGNUX_ONLY OFF CACHE BOOL "Compile only rugnux, the offline analysis CLI (no Qt)")
|
||||
|
||||
# macOS only: the Developer ID Application certificate to sign the shipped program with at install
|
||||
# time - hardened runtime and a secure timestamp, which is what Apple's notarization demands. Given
|
||||
# as the certificate's SHA-1 hash, the 40 hex digits `security find-identity -v -p codesigning`
|
||||
# prints in front of the name: the name itself ("Developer ID Application: <name> (<team>)") has
|
||||
# spaces, and Qt's generated deploy script would split it into separate macdeployqt arguments. Empty
|
||||
# (the default) keeps the ad-hoc signature, which runs locally but is refused by Gatekeeper after a
|
||||
# download. Notarizing the packed artefact is a separate step after cpack: tools/notarize_macos.sh.
|
||||
SET(JFJOCH_MACOS_SIGNING_IDENTITY "" CACHE STRING "macOS: SHA-1 of the Developer ID Application certificate to codesign with (empty: ad-hoc)")
|
||||
|
||||
# Only the viewer (and its portable dependency tree) is supported on Windows and macOS -- the
|
||||
# broker/receiver/FPGA/writer server stack is Linux-only. Force viewer-only on those platforms so a
|
||||
# plain configure builds the right subset; on Linux it remains a user-togglable option. A rugnux-only
|
||||
|
||||
@@ -10,8 +10,7 @@ Gitea release page, and for Linux also in the Jungfraujoch RPM/APT repositories
|
||||
[Release contents](RELEASE_CONTENTS.md) for what each package contains and what it requires, and
|
||||
[Deployment](DEPLOYMENT.md) for how to install it. The macOS build needs **macOS 13 (Ventura) or
|
||||
newer on an Apple Silicon Mac** (M1 and newer; Intel Macs are not supported) and is CPU-only — see
|
||||
[Release contents ▸ macOS](RELEASE_CONTENTS.md#macos), which also covers opening it for the first
|
||||
time, as the release is not yet notarized by Apple.
|
||||
[Release contents ▸ macOS](RELEASE_CONTENTS.md#macos).
|
||||
|
||||
## Where it fits among the three analysis tools
|
||||
|
||||
@@ -332,3 +331,11 @@ As on Windows, `CMAKE_PREFIX_PATH` (Qt) is the only required flag. To produce th
|
||||
Qt frameworks and the license notices copied into the app with `macdeployqt`, packed into
|
||||
`jfjoch-viewer-<version>-macos-arm64.dmg` — run `cpack` in the build directory. What comes out is
|
||||
described in [Release contents ▸ macOS](RELEASE_CONTENTS.md#macos).
|
||||
|
||||
The released image is signed with the project's Apple Developer ID and notarized by Apple. A build of
|
||||
your own is signed ad hoc, which runs on the machine that built it but is refused by Gatekeeper once
|
||||
downloaded. To sign with a Developer ID from your own keychain, configure with
|
||||
`-DJFJOCH_MACOS_SIGNING_IDENTITY=<hash>` before `cpack`, where the hash is the 40 hex digits
|
||||
`security find-identity -v -p codesigning` prints in front of the certificate's name, then notarize
|
||||
and staple the image with `tools/notarize_macos.sh build-mac/jfjoch-viewer-*.dmg` (it needs
|
||||
notarytool credentials stored once on the machine; the script's header says how).
|
||||
|
||||
@@ -173,17 +173,10 @@ app onto the *Applications* shortcut next to it. The app is self-contained: the
|
||||
inside it, and so are the license notices (`Contents/Resources`), which is where a macOS app keeps
|
||||
them.
|
||||
|
||||
**The release is not notarized yet** (that needs an Apple Developer account), so macOS refuses to
|
||||
open a downloaded copy the first time. On macOS 15 and newer: try to open it once, then allow it in
|
||||
*System Settings ▸ Privacy & Security* with *Open Anyway*. On macOS 13 and 14, Control-click the app
|
||||
and choose *Open*. Alternatively, clear the download flag in Terminal:
|
||||
|
||||
```
|
||||
xattr -dr com.apple.quarantine /Applications/jfjoch_viewer.app
|
||||
```
|
||||
|
||||
The same applies to the `rugnux` binary from its `.tgz` — see
|
||||
[Installing Rugnux](RUGNUX_INSTALL.md#from-the-release-archive).
|
||||
Both artefacts are signed with the project's Apple Developer ID and **notarized by Apple**, so a
|
||||
downloaded copy opens like any other Mac application. The disk image carries its notarization ticket;
|
||||
the `rugnux` binary in the `.tgz` cannot (a bare executable has nowhere to staple one), so the first
|
||||
run of a freshly downloaded `rugnux` looks the ticket up online and needs an internet connection.
|
||||
|
||||
The toolchain of the released macOS artefacts is Xcode (Apple Clang) and Qt 6.11 for macOS,
|
||||
including Qt Charts; to build the viewer yourself see
|
||||
|
||||
+4
-11
@@ -62,17 +62,10 @@ Nothing is written outside that directory, nothing needs root, and several versi
|
||||
side. To remove it, delete the directory. Put `bin/` on your `PATH` if you want to type `rugnux`
|
||||
rather than the full path.
|
||||
|
||||
> **macOS: the first run is blocked.** The release is not yet notarized by Apple, and a `.tgz`
|
||||
> downloaded with a browser hands its download flag on to everything `tar` extracts from it, so
|
||||
> macOS refuses to run `rugnux` ("cannot be opened because the developer cannot be verified"). Clear
|
||||
> the flag once for the whole directory:
|
||||
>
|
||||
> ```
|
||||
> xattr -dr com.apple.quarantine ~/rugnux-1.0.0
|
||||
> ```
|
||||
>
|
||||
> An archive fetched with `curl` carries no such flag. Unpacking into a directory under your home
|
||||
> directory (`~/rugnux-<version>`) rather than `/opt` avoids needing `sudo` on a Mac.
|
||||
> **macOS:** the binary is signed with the project's Apple Developer ID and notarized, so a `.tgz`
|
||||
> downloaded with a browser runs without further steps; the first run of a freshly downloaded copy
|
||||
> checks the notarization online. Unpacking into a directory under your home directory
|
||||
> (`~/rugnux-<version>`) rather than `/opt` avoids needing `sudo` on a Mac.
|
||||
|
||||
> **Mixing the two.** If a `rugnux` package is also installed, `/usr/bin/rugnux` will normally win
|
||||
> on `PATH`. Put the archive's `bin/` first, or call it by its full path, to be sure which one you
|
||||
|
||||
@@ -37,6 +37,16 @@ TARGET_LINK_LIBRARIES(JFJochRugnux JFJochReader JFJochImageAnalysis JFJochWriter
|
||||
ADD_EXECUTABLE(rugnux rugnux_cli.cpp RugnuxConsole.cpp RugnuxConsole.h)
|
||||
TARGET_LINK_LIBRARIES(rugnux JFJochRugnux JFJochReader JFJochImageAnalysis JFJochWriter)
|
||||
INSTALL(TARGETS rugnux RUNTIME COMPONENT rugnux)
|
||||
# macOS: sign the installed binary with the Developer ID (hardened runtime, secure timestamp) so the
|
||||
# .tgz can be notarized - see JFJOCH_MACOS_SIGNING_IDENTITY in the top-level CMakeLists.txt. The
|
||||
# binary links only system libraries, so no entitlements are needed.
|
||||
IF(APPLE AND JFJOCH_MACOS_SIGNING_IDENTITY)
|
||||
INSTALL(CODE "execute_process(COMMAND codesign --force --options runtime --timestamp
|
||||
--sign \"${JFJOCH_MACOS_SIGNING_IDENTITY}\"
|
||||
\"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/bin/rugnux\"
|
||||
COMMAND_ERROR_IS_FATAL ANY)"
|
||||
COMPONENT rugnux)
|
||||
ENDIF()
|
||||
|
||||
# libcufft_static.a carries a relocatable-device-code object (separate_callback.o), so an executable
|
||||
# linking it needs a CUDA device-link step -- without it the host link fails on an undefined
|
||||
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
# Notarize the macOS release artefacts with Apple and staple the ticket where one can be stapled.
|
||||
#
|
||||
# tools/notarize_macos.sh <jfjoch-viewer-*.dmg | rugnux-*.tar.gz> ...
|
||||
#
|
||||
# The programs inside must already carry a Developer ID signature with hardened runtime and secure
|
||||
# timestamp: configure with -DJFJOCH_MACOS_SIGNING_IDENTITY=<certificate SHA-1> and let cpack produce
|
||||
# the artefact (see the top-level CMakeLists.txt). The same certificate signs the .dmg here: the
|
||||
# one JFJOCH_MACOS_SIGNING_IDENTITY names, else the Developer ID Application in the keychain. Needs
|
||||
# notarytool credentials stored once on the machine, under the profile named by JFJOCH_NOTARY_PROFILE
|
||||
# (default jfjoch-notary). On an unattended machine store them in the login keychain file: without
|
||||
# --keychain they go into the data-protection keychain, which is unreadable while the screen is
|
||||
# locked, and the step then fails with "No Keychain password item found".
|
||||
#
|
||||
# xcrun notarytool store-credentials jfjoch-notary --apple-id <apple id> --team-id <team id> \
|
||||
# --keychain ~/Library/Keychains/login.keychain-db
|
||||
#
|
||||
# .dmg signed with the same Developer ID, submitted, the ticket stapled to the image.
|
||||
# .tar.gz the rugnux binary inside is zipped and submitted; Apple then knows its hash, which is
|
||||
# all Gatekeeper needs (a bare executable has nowhere to staple a ticket). The archive is
|
||||
# shipped unchanged.
|
||||
set -euo pipefail
|
||||
|
||||
profile=${JFJOCH_NOTARY_PROFILE:-jfjoch-notary}
|
||||
identity=${JFJOCH_MACOS_SIGNING_IDENTITY:-$(security find-identity -v -p codesigning | sed -n 's/^ *[0-9]*) \([0-9A-F]*\) "Developer ID Application: .*/\1/p' | head -1)}
|
||||
if [ -z "$identity" ]; then echo "no Developer ID Application identity in the keychain"; exit 1; fi
|
||||
|
||||
submit() { # submit <file>: wait for Apple's verdict, print the log on rejection
|
||||
local out id status
|
||||
out=$(xcrun notarytool submit "$1" --keychain-profile "$profile" --wait --output-format plist)
|
||||
id=$(echo "$out" | plutil -extract id raw -)
|
||||
status=$(echo "$out" | plutil -extract status raw -)
|
||||
echo "notarization of $(basename "$1"): $status (submission $id)"
|
||||
if [ "$status" != "Accepted" ]; then
|
||||
xcrun notarytool log "$id" --keychain-profile "$profile"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
for file in "$@"; do
|
||||
case "$file" in
|
||||
*.dmg)
|
||||
codesign --force --timestamp --sign "$identity" "$file"
|
||||
submit "$file"
|
||||
xcrun stapler staple "$file"
|
||||
spctl -a -t open --context context:primary-signature -v "$file"
|
||||
;;
|
||||
*.tar.gz|*.tgz)
|
||||
tmp=$(mktemp -d)
|
||||
tar xf "$file" -C "$tmp"
|
||||
bin=$(find "$tmp" -name rugnux -type f)
|
||||
ditto -c -k "$bin" "$tmp/rugnux.zip"
|
||||
submit "$tmp/rugnux.zip"
|
||||
rm -rf "$tmp"
|
||||
;;
|
||||
*)
|
||||
echo "don't know how to notarize $file"; exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
+10
-1
@@ -246,11 +246,20 @@ qt_import_plugins(jfjoch_viewer EXCLUDE Qt::QJpegPlugin Qt::QTiffPlugin)
|
||||
# which is when the OUTPUT_SCRIPT signature and the Linux deploy path landed (6.3/6.4 used the older
|
||||
# FILENAME_VARIABLE and had no Linux support); skipped on older Qt. NO_UNSUPPORTED_PLATFORM_ERROR
|
||||
# keeps it from hard-failing on configurations it does not handle.
|
||||
# On macOS macdeployqt also signs the bundle (ad-hoc by default); with JFJOCH_MACOS_SIGNING_IDENTITY
|
||||
# set it signs with that Developer ID instead, hardened runtime and secure timestamp included, which
|
||||
# is what the .dmg needs to be notarized afterwards (DEPLOY_TOOL_OPTIONS needs Qt >= 6.7).
|
||||
IF(Qt6_VERSION VERSION_GREATER_EQUAL "6.5")
|
||||
SET(_viewer_deploy_tool_options)
|
||||
IF(APPLE AND JFJOCH_MACOS_SIGNING_IDENTITY)
|
||||
SET(_viewer_deploy_tool_options
|
||||
DEPLOY_TOOL_OPTIONS "-sign-for-notarization=${JFJOCH_MACOS_SIGNING_IDENTITY}")
|
||||
ENDIF()
|
||||
qt_generate_deploy_app_script(
|
||||
TARGET jfjoch_viewer
|
||||
OUTPUT_SCRIPT jfjoch_viewer_deploy_script
|
||||
NO_UNSUPPORTED_PLATFORM_ERROR)
|
||||
NO_UNSUPPORTED_PLATFORM_ERROR
|
||||
${_viewer_deploy_tool_options})
|
||||
INSTALL(SCRIPT ${jfjoch_viewer_deploy_script} COMPONENT viewer)
|
||||
ENDIF()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user