Skip to content

Commit 24ca51b

Browse files
fix(release): Developer ID sign + notarize macOS builds
Replace ad-hoc codesign with hardened-runtime Developer ID signing, notarize and staple both the .app and the .dmg via notarytool, and import the signing cert into a temp keychain on the runner. Fixes the Gatekeeper rejection on macOS 15+ where right-click - Open no longer bypasses. Update install instructions accordingly.
1 parent b019df4 commit 24ca51b

2 files changed

Lines changed: 61 additions & 4 deletions

File tree

.github/RELEASE_TEMPLATE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
- **Intel Mac**: `ultralog-macos-intel.dmg`
66
- **Apple Silicon (M1/M2/M3/M4)**: `ultralog-macos-arm64.dmg`
77
2. Open the DMG and drag UltraLog to your Applications folder
8-
3. On first run, right-click the app and select "Open" to bypass Gatekeeper
8+
3. Launch UltraLog from Applications. The app is signed and notarized by Apple, so it opens without a Gatekeeper warning.
99

1010
### Windows
1111
1. Download `ultralog-windows.zip`

.github/workflows/release.yml

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,30 @@ jobs:
156156
- name: Install create-dmg
157157
run: brew install create-dmg
158158

159+
- name: Import code signing certificate
160+
env:
161+
MACOS_CERT_P12: ${{ secrets.MACOS_CERT_P12 }}
162+
MACOS_CERT_PASSWORD: ${{ secrets.MACOS_CERT_PASSWORD }}
163+
MACOS_KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
164+
run: |
165+
CERT_PATH="$RUNNER_TEMP/cert.p12"
166+
KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db"
167+
168+
echo "$MACOS_CERT_P12" | base64 --decode > "$CERT_PATH"
169+
170+
# Create a temporary keychain and import the Developer ID cert
171+
security create-keychain -p "$MACOS_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
172+
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
173+
security unlock-keychain -p "$MACOS_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
174+
security import "$CERT_PATH" -P "$MACOS_CERT_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
175+
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
176+
177+
# Make the temp keychain searchable so codesign can find the identity
178+
security list-keychain -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | sed s/\"//g)
179+
180+
security find-identity -v -p codesigning "$KEYCHAIN_PATH"
181+
rm -f "$CERT_PATH"
182+
159183
- name: Get version
160184
id: version
161185
run: echo "VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*\"\(.*\)\".*/\1/')" >> $GITHUB_OUTPUT
@@ -170,6 +194,10 @@ jobs:
170194
BUNDLE_ID: ${{ needs.check-tag.outputs.bundle_id }}
171195
DMG_VOLNAME: ${{ needs.check-tag.outputs.dmg_volname }}
172196
SUFFIX: ${{ needs.check-tag.outputs.suffix }}
197+
MACOS_SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }}
198+
AC_API_KEY_ID: ${{ secrets.AC_API_KEY_ID }}
199+
AC_API_ISSUER_ID: ${{ secrets.AC_API_ISSUER_ID }}
200+
AC_API_KEY_P8: ${{ secrets.AC_API_KEY_P8 }}
173201
run: |
174202
ASSET_NAME="ultralog-macos-${{ matrix.arch }}${SUFFIX}"
175203
mkdir -p output
@@ -253,8 +281,26 @@ jobs:
253281
ICNS_PATH="$APP_DIR/Contents/Resources/AppIcon.icns"
254282
fi
255283
256-
# Ad-hoc sign the app bundle
257-
codesign --force --deep --sign - "$APP_DIR"
284+
# Decode the App Store Connect API key for notarytool
285+
API_KEY_PATH="$RUNNER_TEMP/AuthKey.p8"
286+
echo "$AC_API_KEY_P8" | base64 --decode > "$API_KEY_PATH"
287+
288+
# Sign with Developer ID + hardened runtime (inner binary first, then bundle)
289+
codesign --force --options runtime --timestamp \
290+
--sign "$MACOS_SIGN_IDENTITY" "$APP_DIR/Contents/MacOS/ultralog"
291+
codesign --force --options runtime --timestamp \
292+
--sign "$MACOS_SIGN_IDENTITY" "$APP_DIR"
293+
codesign --verify --strict --verbose=2 "$APP_DIR"
294+
295+
# Notarize the app, then staple the ticket so it opens offline
296+
ditto -c -k --keepParent "$APP_DIR" "$RUNNER_TEMP/app.zip"
297+
xcrun notarytool submit "$RUNNER_TEMP/app.zip" \
298+
--key "$API_KEY_PATH" \
299+
--key-id "$AC_API_KEY_ID" \
300+
--issuer "$AC_API_ISSUER_ID" \
301+
--wait
302+
xcrun stapler staple "$APP_DIR"
303+
rm -f "$RUNNER_TEMP/app.zip"
258304
259305
# Create DMG with volume icon if available
260306
if [ -n "$ICNS_PATH" ]; then
@@ -284,6 +330,17 @@ jobs:
284330
hdiutil create -volname "${DMG_VOLNAME}" -srcfolder "$APP_DIR" -ov -format UDZO "output/${ASSET_NAME}.dmg"
285331
fi
286332
333+
# Sign, notarize, and staple the DMG so the download itself is trusted
334+
codesign --force --timestamp --sign "$MACOS_SIGN_IDENTITY" "output/${ASSET_NAME}.dmg"
335+
xcrun notarytool submit "output/${ASSET_NAME}.dmg" \
336+
--key "$API_KEY_PATH" \
337+
--key-id "$AC_API_KEY_ID" \
338+
--issuer "$AC_API_ISSUER_ID" \
339+
--wait
340+
xcrun stapler staple "output/${ASSET_NAME}.dmg"
341+
xcrun stapler validate "output/${ASSET_NAME}.dmg"
342+
rm -f "$API_KEY_PATH"
343+
287344
- name: Upload artifact
288345
uses: actions/upload-artifact@v4
289346
with:
@@ -362,7 +419,7 @@ jobs:
362419
- **Intel Mac**: `ultralog-macos-intel-beta.dmg`
363420
- **Apple Silicon (M1/M2/M3/M4)**: `ultralog-macos-arm64-beta.dmg`
364421
2. Open the DMG and drag UltraLog to your Applications folder
365-
3. On first run, right-click the app and select "Open" to bypass Gatekeeper
422+
3. Launch UltraLog from Applications. Beta builds are signed and notarized by Apple, so they open without a Gatekeeper warning.
366423
367424
### Windows
368425
1. Download `ultralog-windows-beta.zip`

0 commit comments

Comments
 (0)