Skip to content

Commit a129094

Browse files
fix(newm-admin): Add macOS code signing and notarization
1 parent 05a919c commit a129094

File tree

5 files changed

+137
-7
lines changed

5 files changed

+137
-7
lines changed

.github/workflows/newm-admin-release.yml

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ jobs:
9494
path: newm-admin/${{ steps.package.outputs.PKG_PATH }}
9595

9696
package-macos:
97-
name: Package macOS DMG
97+
name: Package macOS DMG (Universal)
9898
needs: build
9999
runs-on: macos-latest
100100

@@ -110,6 +110,7 @@ jobs:
110110
uses: dtolnay/rust-toolchain@stable
111111
with:
112112
toolchain: stable
113+
targets: aarch64-apple-darwin,x86_64-apple-darwin
113114

114115
- name: Setup Rust cache
115116
uses: Swatinem/rust-cache@v2
@@ -119,26 +120,93 @@ jobs:
119120
- name: Install create-dmg
120121
run: brew install create-dmg
121122

122-
- name: Build Release
123+
- name: Build ARM64 (Apple Silicon)
123124
run: cargo build --release --target aarch64-apple-darwin --locked
124125

126+
- name: Build x86_64 (Intel)
127+
run: cargo build --release --target x86_64-apple-darwin --locked
128+
125129
- name: Get version
126130
id: version
127131
run: |
128132
VERSION=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)
129133
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
130134
131-
- name: Create DMG
135+
- name: Import Code Signing Certificate
136+
env:
137+
MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }}
138+
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
139+
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
140+
run: |
141+
# Create a temporary keychain
142+
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
143+
security default-keychain -s build.keychain
144+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
145+
security set-keychain-settings -t 3600 -u build.keychain
146+
147+
# Decode certificate
148+
echo "$MACOS_CERTIFICATE" | base64 --decode > certificate.p12
149+
150+
# Debug: Check file size (should be ~4300 bytes based on 5760 base64 chars)
151+
echo "Certificate file size:"
152+
ls -la certificate.p12
153+
154+
# Debug: Verify the p12 file is valid with openssl first
155+
echo "Verifying p12 with openssl..."
156+
openssl pkcs12 -in certificate.p12 -nokeys -passin pass:"$MACOS_CERTIFICATE_PWD" -legacy 2>&1 || {
157+
echo "OpenSSL verification failed. Trying without -legacy flag..."
158+
openssl pkcs12 -in certificate.p12 -nokeys -passin pass:"$MACOS_CERTIFICATE_PWD" 2>&1 || {
159+
echo "ERROR: p12 file appears invalid or password is wrong"
160+
echo "Password length: ${#MACOS_CERTIFICATE_PWD}"
161+
exit 1
162+
}
163+
}
164+
165+
# Import certificate
166+
security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign -T /usr/bin/security
167+
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
168+
169+
# Verify certificate was imported
170+
security find-identity -v -p codesigning build.keychain
171+
172+
# Clean up certificate file
173+
rm certificate.p12
174+
175+
- name: Create Universal App Bundle and DMG
176+
env:
177+
MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }}
132178
run: |
133179
chmod +x packaging/macos/bundle.sh
134180
packaging/macos/bundle.sh ${{ steps.version.outputs.VERSION }} aarch64-apple-darwin
135181
182+
- name: Notarize App
183+
env:
184+
APPLE_ID: ${{ secrets.APPLE_ID }}
185+
APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }}
186+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
187+
run: |
188+
DMG_PATH="target/aarch64-apple-darwin/release/NEWM-Admin-${{ steps.version.outputs.VERSION }}-macos.dmg"
189+
190+
echo "Submitting DMG for notarization..."
191+
xcrun notarytool submit "$DMG_PATH" \
192+
--apple-id "$APPLE_ID" \
193+
--password "$APPLE_APP_PASSWORD" \
194+
--team-id "$APPLE_TEAM_ID" \
195+
--wait
196+
197+
echo "Stapling notarization ticket to DMG..."
198+
xcrun stapler staple "$DMG_PATH"
199+
200+
echo "Verifying notarization..."
201+
xcrun stapler validate "$DMG_PATH"
202+
136203
- name: Upload DMG
137204
uses: actions/upload-artifact@v4
138205
with:
139206
name: NEWM-Admin-${{ steps.version.outputs.VERSION }}-macos.dmg
140207
path: newm-admin/target/aarch64-apple-darwin/release/NEWM-Admin-${{ steps.version.outputs.VERSION }}-macos.dmg
141208

209+
142210
package-linux:
143211
name: Package Linux AppImage
144212
needs: build

newm-admin/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

newm-admin/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "newm-admin"
3-
version = "0.1.2"
3+
version = "0.1.3-alpha.3"
44
edition = "2024"
55
authors = ["Andrew Westberg <[email protected]>"]
66
license = "Apache-2.0"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<!-- Required for JIT compilation and some memory operations -->
6+
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
7+
<true/>
8+
<!-- Allow network client connections (required for API calls) -->
9+
<key>com.apple.security.network.client</key>
10+
<true/>
11+
</dict>
12+
</plist>

newm-admin/packaging/macos/bundle.sh

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ set -euo pipefail
44
# macOS App Bundle and DMG Creator for NEWM Admin
55
# Usage: ./bundle.sh <version> <target>
66
# Example: ./bundle.sh 0.1.0 aarch64-apple-darwin
7+
#
8+
# Environment variables:
9+
# MACOS_SIGNING_IDENTITY - Code signing identity (optional, skips signing if not set)
710

811
VERSION="${1:-0.1.0}"
912
TARGET="${2:-aarch64-apple-darwin}"
@@ -14,15 +17,33 @@ BUILD_DIR="$PROJECT_DIR/target/$TARGET/release"
1417
APP_NAME="NEWM Admin.app"
1518
DMG_NAME="NEWM-Admin-${VERSION}-macos.dmg"
1619

20+
# Signing identity from environment (set by GitHub Actions)
21+
SIGNING_IDENTITY="${MACOS_SIGNING_IDENTITY:-}"
22+
1723
echo "Creating macOS app bundle..."
24+
echo " Version: $VERSION"
25+
echo " Target: $TARGET"
26+
echo " Signing: ${SIGNING_IDENTITY:-(unsigned)}"
1827

1928
# Create app bundle structure
2029
rm -rf "$BUILD_DIR/$APP_NAME"
2130
mkdir -p "$BUILD_DIR/$APP_NAME/Contents/MacOS"
2231
mkdir -p "$BUILD_DIR/$APP_NAME/Contents/Resources"
2332

24-
# Copy executable
25-
cp "$BUILD_DIR/newm-admin" "$BUILD_DIR/$APP_NAME/Contents/MacOS/"
33+
# Check for universal binary opportunity
34+
INTEL_BINARY="$PROJECT_DIR/target/x86_64-apple-darwin/release/newm-admin"
35+
ARM_BINARY="$PROJECT_DIR/target/aarch64-apple-darwin/release/newm-admin"
36+
37+
if [[ -f "$INTEL_BINARY" && -f "$ARM_BINARY" ]]; then
38+
echo "Creating universal binary (Intel + ARM)..."
39+
lipo -create -output "$BUILD_DIR/$APP_NAME/Contents/MacOS/newm-admin" \
40+
"$INTEL_BINARY" "$ARM_BINARY"
41+
echo " Universal binary created successfully"
42+
else
43+
# Fallback to single-arch binary
44+
echo "Using single-architecture binary for $TARGET"
45+
cp "$BUILD_DIR/newm-admin" "$BUILD_DIR/$APP_NAME/Contents/MacOS/"
46+
fi
2647

2748
# Copy icon
2849
cp "$PROJECT_DIR/assets/icon.icns" "$BUILD_DIR/$APP_NAME/Contents/Resources/icon.icns"
@@ -32,6 +53,22 @@ sed "s/\${VERSION}/$VERSION/g" "$SCRIPT_DIR/Info.plist" > "$BUILD_DIR/$APP_NAME/
3253

3354
echo "App bundle created at: $BUILD_DIR/$APP_NAME"
3455

56+
# Code signing (if identity is provided)
57+
if [[ -n "$SIGNING_IDENTITY" ]]; then
58+
echo "Signing app bundle with: $SIGNING_IDENTITY"
59+
codesign --force --deep --sign "$SIGNING_IDENTITY" \
60+
--options runtime \
61+
--entitlements "$SCRIPT_DIR/Entitlements.plist" \
62+
--timestamp \
63+
"$BUILD_DIR/$APP_NAME"
64+
65+
echo "Verifying signature..."
66+
codesign --verify --verbose=2 "$BUILD_DIR/$APP_NAME"
67+
echo " Signature verified successfully"
68+
else
69+
echo "WARNING: No signing identity provided, app will be unsigned"
70+
fi
71+
3572
# Create DMG if create-dmg is available
3673
if command -v create-dmg &> /dev/null; then
3774
echo "Creating DMG..."
@@ -49,11 +86,24 @@ if command -v create-dmg &> /dev/null; then
4986
"$BUILD_DIR/$DMG_NAME" \
5087
"$BUILD_DIR/$APP_NAME"
5188

89+
# Sign the DMG as well if we have a signing identity
90+
if [[ -n "$SIGNING_IDENTITY" ]]; then
91+
echo "Signing DMG..."
92+
codesign --force --sign "$SIGNING_IDENTITY" --timestamp "$BUILD_DIR/$DMG_NAME"
93+
fi
94+
5295
echo "DMG created at: $BUILD_DIR/$DMG_NAME"
5396
else
5497
echo "create-dmg not found, creating simple DMG with hdiutil..."
5598
rm -f "$BUILD_DIR/$DMG_NAME"
5699
hdiutil create -volname "NEWM Admin" -srcfolder "$BUILD_DIR/$APP_NAME" -ov -format UDZO "$BUILD_DIR/$DMG_NAME"
100+
101+
# Sign the DMG as well if we have a signing identity
102+
if [[ -n "$SIGNING_IDENTITY" ]]; then
103+
echo "Signing DMG..."
104+
codesign --force --sign "$SIGNING_IDENTITY" --timestamp "$BUILD_DIR/$DMG_NAME"
105+
fi
106+
57107
echo "DMG created at: $BUILD_DIR/$DMG_NAME"
58108
fi
59109

0 commit comments

Comments
 (0)