diff --git a/BUILDING.md b/BUILDING.md index bd5584d4..258103de 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -236,21 +236,75 @@ docker run -d \ - AppImage: `dist-electron-packages/TimeSafari-x.x.x.AppImage` - DEB: `dist-electron-packages/timesafari_x.x.x_amd64.deb` -### Running the Packaged App +### macOS Build + +1. Build the electron app in production mode: -- AppImage: Make executable and run + ```bash + npm run build:electron-prod + ``` - ```bash - chmod +x dist-electron-packages/TimeSafari-*.AppImage - ./dist-electron-packages/TimeSafari-*.AppImage - ``` +2. Package the Electron app for macOS: -- DEB: Install and run + ```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 - ```bash - sudo dpkg -i dist-electron-packages/timesafari_*_amd64.deb - timesafari - ``` +- **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 + ``` + +- **macOS**: + - `.app` bundle: Double-click `TimeSafari.app` in Finder + - `.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 + + Note: If you get a security warning when running the app: + 1. Right-click the app + 2. Select "Open" + 3. Click "Open" in the security dialog ### Development Testing diff --git a/build/entitlements.mac.plist b/build/entitlements.mac.plist new file mode 100644 index 00000000..e987db78 --- /dev/null +++ b/build/entitlements.mac.plist @@ -0,0 +1,20 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.debugger + + com.apple.security.device.audio-input + + com.apple.security.device.camera + + com.apple.security.personal-information.addressbook + + com.apple.security.personal-information.calendars + + + diff --git a/package-lock.json b/package-lock.json index c47a4061..95227c44 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4448,9 +4448,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", - "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 0224fe64..724b6a22 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,9 @@ "fastlane:ios:beta": "cd ios && fastlane beta", "fastlane:ios:release": "cd ios && fastlane release", "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": { "@capacitor-mlkit/barcode-scanning": "^6.0.0", @@ -185,6 +187,29 @@ "category": "Office", "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" + } + ] + } } } diff --git a/scripts/test-ios.js b/scripts/test-ios.js index f0106ed1..472169f6 100644 --- a/scripts/test-ios.js +++ b/scripts/test-ios.js @@ -441,8 +441,8 @@ const configureIosProject = async (log) => { try { // Try to run pod install normally first log('🔄 Running "pod install" in ios/App directory...'); - execSync('cd ios/App && pod install', { stdio: 'inherit' }); - log('✅ CocoaPods installation completed'); + execSync('cd ios/App && pod install', { stdio: 'inherit' }); + log('✅ CocoaPods installation completed'); } catch (error) { // If that fails, provide detailed instructions 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 - const deeplinkTests = [ - { + const deeplinkTests = [ + { url: `timesafari://claim/${testEnv.CLAIM_ID}`, - description: 'Claim view' - }, - { + description: 'Claim view' + }, + { 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}`, - description: 'Raw claim addition' - }, - { - url: 'timesafari://did/test', - description: 'DID view with test identifier' - }, - { - url: `timesafari://did/${testEnv.CONTACT1_DID}`, - description: 'DID view with contact DID' - }, - { + description: 'Raw claim addition' + }, + { + url: 'timesafari://did/test', + description: 'DID view with test identifier' + }, + { + url: `timesafari://did/${testEnv.CONTACT1_DID}`, + description: 'DID view with contact DID' + }, + { url: (() => { if (!testEnv?.CONTACT1_DID) { 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); return url; })(), - description: 'Contact editing' - }, - { - url: `timesafari://contacts/import?contacts=${encodeURIComponent(JSON.stringify(contacts))}`, - description: 'Contacts import' - } - ]; + description: 'Contact editing' + }, + { + url: `timesafari://contacts/import?contacts=${encodeURIComponent(JSON.stringify(contacts))}`, + description: 'Contacts import' + } + ]; // Log the final test configuration log('\n5. Final Test Configuration:');