forked from trent_larson/crowd-funder-for-time-pwa
docs: add macOS build and packaging instructions
- Add detailed macOS build procedure for Electron app - Include instructions for Intel and Universal builds - Add code signing and notarization requirements - Document running instructions for .app, .dmg, and .zip formats - Add security warning handling instructions
This commit is contained in:
76
BUILDING.md
76
BUILDING.md
@@ -236,21 +236,75 @@ docker run -d \
|
|||||||
- AppImage: `dist-electron-packages/TimeSafari-x.x.x.AppImage`
|
- AppImage: `dist-electron-packages/TimeSafari-x.x.x.AppImage`
|
||||||
- DEB: `dist-electron-packages/timesafari_x.x.x_amd64.deb`
|
- DEB: `dist-electron-packages/timesafari_x.x.x_amd64.deb`
|
||||||
|
|
||||||
|
### macOS Build
|
||||||
|
|
||||||
|
1. Build the electron app in production mode:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build:electron-prod
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Package the Electron app for macOS:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# For Intel Macs
|
||||||
|
npm run electron:build-mac
|
||||||
|
|
||||||
|
# For Universal build (Intel + Apple Silicon)
|
||||||
|
npm run electron:build-mac-universal
|
||||||
|
```
|
||||||
|
|
||||||
|
3. The packaged applications will be in `dist-electron-packages/`:
|
||||||
|
- `.app` bundle: `TimeSafari.app`
|
||||||
|
- `.dmg` installer: `TimeSafari-x.x.x.dmg`
|
||||||
|
- `.zip` archive: `TimeSafari-x.x.x-mac.zip`
|
||||||
|
|
||||||
|
### Code Signing and Notarization (macOS)
|
||||||
|
|
||||||
|
For public distribution on macOS, you need to code sign and notarize your app:
|
||||||
|
|
||||||
|
1. Set up environment variables:
|
||||||
|
```bash
|
||||||
|
export CSC_LINK=/path/to/your/certificate.p12
|
||||||
|
export CSC_KEY_PASSWORD=your_certificate_password
|
||||||
|
export APPLE_ID=your_apple_id
|
||||||
|
export APPLE_ID_PASSWORD=your_app_specific_password
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Build with signing:
|
||||||
|
```bash
|
||||||
|
npm run electron:build-mac
|
||||||
|
```
|
||||||
|
|
||||||
### Running the Packaged App
|
### Running the Packaged App
|
||||||
|
|
||||||
- AppImage: Make executable and run
|
- **Linux**:
|
||||||
|
- AppImage: Make executable and run
|
||||||
|
```bash
|
||||||
|
chmod +x dist-electron-packages/TimeSafari-*.AppImage
|
||||||
|
./dist-electron-packages/TimeSafari-*.AppImage
|
||||||
|
```
|
||||||
|
- DEB: Install and run
|
||||||
|
```bash
|
||||||
|
sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb
|
||||||
|
timesafari
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
- **macOS**:
|
||||||
chmod +x dist-electron-packages/TimeSafari-*.AppImage
|
- `.app` bundle: Double-click `TimeSafari.app` in Finder
|
||||||
./dist-electron-packages/TimeSafari-*.AppImage
|
- `.dmg` installer:
|
||||||
```
|
1. Double-click the `.dmg` file
|
||||||
|
2. Drag the app to your Applications folder
|
||||||
|
3. Launch from Applications
|
||||||
|
- `.zip` archive:
|
||||||
|
1. Extract the `.zip` file
|
||||||
|
2. Move `TimeSafari.app` to your Applications folder
|
||||||
|
3. Launch from Applications
|
||||||
|
|
||||||
- DEB: Install and run
|
Note: If you get a security warning when running the app:
|
||||||
|
1. Right-click the app
|
||||||
```bash
|
2. Select "Open"
|
||||||
sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb
|
3. Click "Open" in the security dialog
|
||||||
timesafari
|
|
||||||
```
|
|
||||||
|
|
||||||
### Development Testing
|
### Development Testing
|
||||||
|
|
||||||
|
|||||||
20
build/entitlements.mac.plist
Normal file
20
build/entitlements.mac.plist
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.cs.allow-jit</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.cs.debugger</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.audio-input</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.device.camera</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.personal-information.addressbook</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.personal-information.calendars</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -4448,9 +4448,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@eslint-community/eslint-utils": {
|
"node_modules/@eslint-community/eslint-utils": {
|
||||||
"version": "4.6.1",
|
"version": "4.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
|
||||||
"integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==",
|
"integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
29
package.json
29
package.json
@@ -41,7 +41,9 @@
|
|||||||
"fastlane:ios:beta": "cd ios && fastlane beta",
|
"fastlane:ios:beta": "cd ios && fastlane beta",
|
||||||
"fastlane:ios:release": "cd ios && fastlane release",
|
"fastlane:ios:release": "cd ios && fastlane release",
|
||||||
"fastlane:android:beta": "cd android && fastlane beta",
|
"fastlane:android:beta": "cd android && fastlane beta",
|
||||||
"fastlane:android:release": "cd android && fastlane release"
|
"fastlane:android:release": "cd android && fastlane release",
|
||||||
|
"electron:build-mac": "npm run build:electron-prod && electron-builder --mac",
|
||||||
|
"electron:build-mac-universal": "npm run build:electron-prod && electron-builder --mac --universal"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@capacitor-mlkit/barcode-scanning": "^6.0.0",
|
"@capacitor-mlkit/barcode-scanning": "^6.0.0",
|
||||||
@@ -185,6 +187,29 @@
|
|||||||
"category": "Office",
|
"category": "Office",
|
||||||
"icon": "build/icon.png"
|
"icon": "build/icon.png"
|
||||||
},
|
},
|
||||||
"asar": true
|
"asar": true,
|
||||||
|
"mac": {
|
||||||
|
"target": ["dmg", "zip"],
|
||||||
|
"category": "public.app-category.productivity",
|
||||||
|
"icon": "build/icon.png",
|
||||||
|
"hardenedRuntime": true,
|
||||||
|
"gatekeeperAssess": false,
|
||||||
|
"entitlements": "build/entitlements.mac.plist",
|
||||||
|
"entitlementsInherit": "build/entitlements.mac.plist"
|
||||||
|
},
|
||||||
|
"dmg": {
|
||||||
|
"contents": [
|
||||||
|
{
|
||||||
|
"x": 130,
|
||||||
|
"y": 220
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"x": 410,
|
||||||
|
"y": 220,
|
||||||
|
"type": "link",
|
||||||
|
"path": "/Applications"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -441,8 +441,8 @@ const configureIosProject = async (log) => {
|
|||||||
try {
|
try {
|
||||||
// Try to run pod install normally first
|
// Try to run pod install normally first
|
||||||
log('🔄 Running "pod install" in ios/App directory...');
|
log('🔄 Running "pod install" in ios/App directory...');
|
||||||
execSync('cd ios/App && pod install', { stdio: 'inherit' });
|
execSync('cd ios/App && pod install', { stdio: 'inherit' });
|
||||||
log('✅ CocoaPods installation completed');
|
log('✅ CocoaPods installation completed');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If that fails, provide detailed instructions
|
// If that fails, provide detailed instructions
|
||||||
log(`⚠️ CocoaPods installation failed: ${error.message}`);
|
log(`⚠️ CocoaPods installation failed: ${error.message}`);
|
||||||
@@ -623,28 +623,28 @@ const runDeeplinkTests = async (log) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now we can safely create the deeplink tests knowing we have valid data
|
// Now we can safely create the deeplink tests knowing we have valid data
|
||||||
const deeplinkTests = [
|
const deeplinkTests = [
|
||||||
{
|
{
|
||||||
url: `timesafari://claim/${testEnv.CLAIM_ID}`,
|
url: `timesafari://claim/${testEnv.CLAIM_ID}`,
|
||||||
description: 'Claim view'
|
description: 'Claim view'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: `timesafari://claim-cert/${testEnv.CERT_ID || testEnv.CLAIM_ID}`,
|
url: `timesafari://claim-cert/${testEnv.CERT_ID || testEnv.CLAIM_ID}`,
|
||||||
description: 'Claim certificate view'
|
description: 'Claim certificate view'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: `timesafari://claim-add-raw/${testEnv.RAW_CLAIM_ID || testEnv.CLAIM_ID}`,
|
url: `timesafari://claim-add-raw/${testEnv.RAW_CLAIM_ID || testEnv.CLAIM_ID}`,
|
||||||
description: 'Raw claim addition'
|
description: 'Raw claim addition'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: 'timesafari://did/test',
|
url: 'timesafari://did/test',
|
||||||
description: 'DID view with test identifier'
|
description: 'DID view with test identifier'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: `timesafari://did/${testEnv.CONTACT1_DID}`,
|
url: `timesafari://did/${testEnv.CONTACT1_DID}`,
|
||||||
description: 'DID view with contact DID'
|
description: 'DID view with contact DID'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: (() => {
|
url: (() => {
|
||||||
if (!testEnv?.CONTACT1_DID) {
|
if (!testEnv?.CONTACT1_DID) {
|
||||||
throw new Error('Cannot construct contact-edit URL: CONTACT1_DID is missing');
|
throw new Error('Cannot construct contact-edit URL: CONTACT1_DID is missing');
|
||||||
@@ -653,13 +653,13 @@ const runDeeplinkTests = async (log) => {
|
|||||||
log('Created contact-edit URL:', url);
|
log('Created contact-edit URL:', url);
|
||||||
return url;
|
return url;
|
||||||
})(),
|
})(),
|
||||||
description: 'Contact editing'
|
description: 'Contact editing'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: `timesafari://contacts/import?contacts=${encodeURIComponent(JSON.stringify(contacts))}`,
|
url: `timesafari://contacts/import?contacts=${encodeURIComponent(JSON.stringify(contacts))}`,
|
||||||
description: 'Contacts import'
|
description: 'Contacts import'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
// Log the final test configuration
|
// Log the final test configuration
|
||||||
log('\n5. Final Test Configuration:');
|
log('\n5. Final Test Configuration:');
|
||||||
|
|||||||
Reference in New Issue
Block a user