Skip to content

Commit 9ad27a6

Browse files
committed
Add Native Mac Installer
This commit adds a new native installer for OSARA on Mac (#1291). The generated app bundle contains the installerexecutable, OSARA shared library, localization resources, and OSARA keymap. When built locally, the app bundle is ad-hoc (self) signed. When built via CI (Github Actions), official signing and notarization is supported. See `doc/installer/mac/README.md` for more details.
1 parent f8aae10 commit 9ad27a6

File tree

20 files changed

+2973
-150
lines changed

20 files changed

+2973
-150
lines changed

.github/workflows/build.yml

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,15 @@ jobs:
6767
# We need php for swell_resgen.
6868
run: brew install php
6969
- name: build
70-
run: scons "publisher=${{ env.publisher }}" version=${{ env.version }}
71-
- name: sign Windows
70+
run: |
71+
if [ ${{ matrix.os }} == macos-latest ]; then
72+
echo "Building native Mac installer..."
73+
scons mac_signing_mode=none "publisher=${{ env.publisher }}" version=${{ env.version }}
74+
else
75+
echo "Building Windows installer..."
76+
scons "publisher=${{ env.publisher }}" version=${{ env.version }}
77+
fi
78+
- name: sign
7279
if: ${{ github.event_name == 'push' && matrix.os == 'windows-latest' }}
7380
uses: azure/trusted-signing-action@v0
7481
with:
@@ -114,24 +121,25 @@ jobs:
114121
exit 1
115122
fi
116123
117-
cd installer/mac
124+
cd build/installer
118125
119126
# Set up app bundle and entitlements
120127
APP_BUNDLE="OSARAInstaller.app"
121128
122129
# Create a temporary entitlements file for runtime (consistent with local build.sh)
130+
entitlements=../../installer/OSARAInstaller/OSARAInstaller.entitlements
123131
runtime_entitlements="$APP_BUNDLE/Contents/Resources/runtime.entitlements"
124-
cp OSARAInstaller.entitlements "$runtime_entitlements"
132+
cp $entitlements "$runtime_entitlements"
125133
126134
# Deep sign all components in the app bundle with entitlements
127135
echo "=== Deep signing app bundle with entitlements ==="
128-
find $APP_BUNDLE -type f \( -name "*.dylib" -o -name "*.so" -o -name "*.framework" \) -exec codesign --force --options runtime --entitlements OSARAInstaller.entitlements --sign "$IDENTITY" {} \; || echo "No additional libraries found to sign"
136+
find $APP_BUNDLE -type f \( -name "*.dylib" -o -name "*.so" -o -name "*.framework" \) -exec codesign --force --options runtime --entitlements $entitlements --sign "$IDENTITY" {} \; || echo "No additional libraries found to sign"
129137
130138
# Sign the main executable with timestamp and entitlements
131-
codesign --force --options runtime --timestamp --entitlements OSARAInstaller.entitlements --sign "$IDENTITY" "$APP_BUNDLE/Contents/MacOS/applet"
139+
codesign --force --options runtime --timestamp --entitlements $entitlements --sign "$IDENTITY" "$APP_BUNDLE/Contents/MacOS/OSARAInstaller"
132140
133141
# Sign the app bundle with timestamp and entitlements
134-
codesign --force --options runtime --timestamp --entitlements OSARAInstaller.entitlements --sign "$IDENTITY" $APP_BUNDLE
142+
codesign --force --options runtime --timestamp --entitlements $entitlements --sign "$IDENTITY" $APP_BUNDLE
135143
136144
# Verify signing with detailed output
137145
echo "=== Verifying code signature ==="
@@ -148,7 +156,7 @@ jobs:
148156
echo "ERROR: Missing Info.plist"
149157
exit 1
150158
fi
151-
if [[ ! -f "$APP_BUNDLE/Contents/MacOS/applet" ]]; then
159+
if [[ ! -f "$APP_BUNDLE/Contents/MacOS/OSARAInstaller" ]]; then
152160
echo "ERROR: Missing main executable"
153161
exit 1
154162
fi
@@ -232,13 +240,13 @@ jobs:
232240
echo "⚠️ Final spctl validation failed"
233241
fi
234242
235-
# Create final signed and notarized zip with app bundle and license
236-
rm -f ../osara_${{ env.version }}.zip
237-
zip -r ../osara_${{ env.version }}.zip $APP_BUNDLE
243+
# Create final signed and notarized zip with app bundle
244+
rm -f ../../installer/osara_${{ env.version }}.zip
245+
zip -r ../../installer/osara_${{ env.version }}.zip $APP_BUNDLE
238246
cd ../..
239247
240248
# Cleanup
241-
rm installer/mac/osara_temp.zip certificate.p12
249+
rm build/installer/osara_temp.zip certificate.p12
242250
security delete-keychain build.keychain
243251
244252
echo "Mac app signed and notarized successfully!"

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ build
88
*.exe
99
.DS_Store
1010
*.dmg
11+
*.zip
1112
*.rc_mac_*
1213
__pycache__
1314
locale/*.pot
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Apple Developer Certificate Setup for OSARA
2+
3+
This guide covers obtaining and configuring Apple Developer certificates for signing and notarizing OSARA builds.
4+
5+
## Prerequisites
6+
7+
- Apple Developer Program membership ($99/year)
8+
- macOS development machine with Xcode installed
9+
10+
## Step 1: Obtain Developer ID Application Certificate
11+
12+
1. **Log into Apple Developer Portal**
13+
- Go to https://developer.apple.com/account
14+
- Sign in with your Apple ID
15+
16+
2. **Create Certificate Signing Request (CSR)**
17+
- Open Keychain Access on your Mac
18+
- Go to Keychain Access → Certificate Assistant → Request a Certificate from a Certificate Authority
19+
- Enter your email address and name
20+
- Select "Saved to disk" and "Let me specify key pair information"
21+
- Click Continue and save the CSR file
22+
23+
3. **Generate Certificate**
24+
- In Apple Developer Portal, go to Certificates, Identifiers & Profiles
25+
- Click the "+" button to create a new certificate
26+
- Select "Developer ID Application" under "Production"
27+
- Upload your CSR file
28+
- Download the generated certificate (.cer file)
29+
30+
4. **Install Certificate**
31+
- Double-click the downloaded .cer file to install it in Keychain Access
32+
- The certificate should appear in your "login" keychain
33+
34+
## Step 2: Export Certificate for CI
35+
36+
1. **Export as P12**
37+
- In Keychain Access, find your "Developer ID Application" certificate
38+
- Right-click and select "Export"
39+
- Choose "Personal Information Exchange (.p12)" format
40+
- Set a strong password and save the file
41+
42+
2. **Convert to Base64**
43+
```bash
44+
base64 -i your_certificate.p12 | pbcopy
45+
```
46+
This copies the base64-encoded certificate to your clipboard
47+
48+
## Step 3: Set Up App-Specific Password
49+
50+
1. **Generate App-Specific Password**
51+
- Go to https://appleid.apple.com
52+
- Sign in and go to Security section
53+
- Under "App-Specific Passwords", click "Generate Password"
54+
- Enter a label like "OSARA CI Notarization"
55+
- Save the generated password securely
56+
57+
## Step 4: Configure GitHub Secrets
58+
59+
Add these secrets to your GitHub repository:
60+
61+
- `APPLE_ID`: Your Apple ID email address
62+
- `APPLE_ID_PASSWORD`: The app-specific password from Step 3
63+
- `TEAM_ID`: Your Apple Developer Team ID (found in Apple Developer Portal)
64+
- `APPLE_CERTIFICATE_P12`: The base64-encoded certificate from Step 2
65+
- `APPLE_CERTIFICATE_PASSWORD`: The password you set when exporting the P12
66+
67+
## Step 5: Find Your Team ID
68+
69+
1. Go to https://developer.apple.com/account
70+
2. Look for "Team ID" in the membership section
71+
3. It's a 10-character alphanumeric string (e.g., "ABCD123456")
72+
73+
## Security Notes
74+
75+
- Never commit certificates or passwords to version control
76+
- Use app-specific passwords, not your main Apple ID password
77+
- Store the P12 certificate securely as a backup
78+
- Rotate app-specific passwords periodically
79+
80+
## Troubleshooting
81+
82+
**Certificate not found during signing:**
83+
- Verify the certificate is properly imported in Keychain Access
84+
- Check that it's a "Developer ID Application" certificate, not "Mac Developer"
85+
86+
**Notarization fails:**
87+
- Ensure you're using an app-specific password, not your regular Apple ID password
88+
- Verify your Team ID is correct
89+
- Check that the app is signed with a Developer ID certificate
90+
91+
**"Developer ID Application" not available:**
92+
- Ensure you have a paid Apple Developer Program membership
93+
- Individual accounts can create Developer ID certificates
94+
- Organization accounts may need admin approval
95+
96+
## Local vs CI Builds
97+
98+
**Local Development:**
99+
- Local builds now use ad-hoc signing only
100+
- No production certificates needed for local testing
101+
- Apps will show security warnings but can be opened with right-click → Open
102+
103+
**CI/Production Builds:**
104+
- CI automatically signs with Developer ID Application certificate
105+
- Apps are notarized and ready for distribution
106+
- No security warnings for end users
107+
108+
## Testing the Setup
109+
110+
After configuring the GitHub secrets, push a commit to the master branch and check the GitHub Actions workflow. The Mac signing step should:
111+
112+
1. Import the certificate successfully
113+
2. Sign the app bundle with your Developer ID
114+
3. Submit for notarization
115+
4. Staple the notarization ticket
116+
5. Create the final signed ZIP file
117+
118+
If any step fails, check the GitHub Actions logs for specific error messages and verify your secrets are configured correctly.

doc/installer/mac/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Native Mac Installer
2+
3+
OSARA uses a native Mac installer built as an app bundle that can be signed and notarized for distribution.
4+
5+
## SCons Build Options
6+
7+
### Local Development
8+
```bash
9+
# Build with ad-hoc signing (default - for local testing)
10+
scons
11+
12+
# Build without signing (for CI or when codesign is not available)
13+
scons mac_signing_mode=none
14+
```
15+
16+
### Signing Modes
17+
18+
The `mac_signing_mode` variable controls code signing behavior:
19+
20+
- `mac_signing_mode=ad-hoc` (default) → Ad-hoc signs for local testing
21+
- `mac_signing_mode=none` → No signing (used by CI)
22+
23+
## CI/CD Pipeline
24+
25+
The GitHub Actions workflow automatically:
26+
27+
1. **PR Builds**: Build with `mac_signing_mode=none` (no signing)
28+
2. **Push Builds**: Build with `mac_signing_mode=none`, then CI handles proper Developer ID signing and notarization
29+
30+
## Apple Developer Requirements
31+
32+
For production releases, the CI pipeline requires:
33+
- Apple Developer Program membership
34+
- Developer ID Application certificate
35+
- App-specific password for notarization
36+
- Team ID
37+
38+
### Required Secrets (for CI)
39+
- `APPLE_ID`: Apple ID email
40+
- `APPLE_ID_PASSWORD`: App-specific password
41+
- `APPLE_TEAM_ID`: Apple Developer Team ID
42+
- `APPLE_CERTIFICATE_P12`: Base64-encoded certificate
43+
- `APPLE_CERTIFICATE_PASSWORD`: Certificate password
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
<key>CFBundleDevelopmentRegion</key>
6+
<string>en</string>
7+
<key>CFBundleExecutable</key>
8+
<string>OSARAInstaller</string>
9+
<key>CFBundleIconFile</key>
10+
<string>osara_icon</string>
11+
<key>CFBundleIdentifier</key>
12+
<string>co.osara.installer</string>
13+
<key>CFBundleInfoDictionaryVersion</key>
14+
<string>6.0</string>
15+
<key>CFBundleName</key>
16+
<string>OSARA Installer</string>
17+
<key>CFBundlePackageType</key>
18+
<string>APPL</string>
19+
<key>CFBundleShortVersionString</key>
20+
<string>1.0</string>
21+
<key>CFBundleVersion</key>
22+
<string>1</string>
23+
<key>LSApplicationCategoryType</key>
24+
<string>public.app-category.developer-tools</string>
25+
<key>LSMinimumSystemVersion</key>
26+
<string>10.13</string>
27+
<key>NSHumanReadableCopyright</key>
28+
<string>Copyright 2014-2025 NV Access Limited, James Teh &amp; other contributors.</string>
29+
<key>NSMainNibFile</key>
30+
<string>MainMenu</string>
31+
<key>NSPrincipalClass</key>
32+
<string>SWELLApplication</string>
33+
<key>NSHighResolutionCapable</key>
34+
<true/>
35+
<key>LSUIElement</key>
36+
<false/>
37+
<key>NSSupportsAutomaticGraphicsSwitching</key>
38+
<true/>
39+
</dict>
40+
</plist>
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
<!-- Security hardening - disable dangerous capabilities -->
6+
<key>com.apple.security.cs.allow-jit</key>
7+
<false/>
8+
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
9+
<false/>
10+
<key>com.apple.security.cs.disable-executable-page-protection</key>
11+
<false/>
12+
<key>com.apple.security.cs.disable-library-validation</key>
13+
<false/>
14+
15+
<!-- File access permissions for installer functionality -->
16+
<key>com.apple.security.files.user-selected.read-write</key>
17+
<true/>
18+
<key>com.apple.security.files.downloads.read-write</key>
19+
<true/>
20+
<key>com.apple.security.files.bookmarks.app-scope</key>
21+
<true/>
22+
23+
<!-- Network access for potential update checks or validation -->
24+
<key>com.apple.security.network.client</key>
25+
<true/>
26+
</dict>
27+
</plist>

0 commit comments

Comments
 (0)