Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
APP_NAME=Openscreen
BUNDLE_ID=com.siddharthvaddem.openscreen

APPLE_ID=
TEAM_ID=
SIGN_IDENTITY="Developer ID Application: Samir Patil ()"
CSC_NAME="Samir Patil ()"

NOTARY_PROFILE=OpenScreen-notary
APPLE_APP_SPECIFIC_PASSWORD=
176 changes: 164 additions & 12 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ name: Build Electron App

on:
workflow_dispatch:
inputs:
arch:
description: 'Architecture to build'
required: true
default: 'both'
type: choice
options:
- arm64
- x64
- both

jobs:
build-windows:
Expand Down Expand Up @@ -36,38 +46,180 @@ jobs:

build-macos:
runs-on: macos-latest
strategy:
matrix:
arch: ${{ github.event.inputs.arch == 'both' && fromJSON('["arm64", "x64"]') || fromJSON(format('["{0}"]', github.event.inputs.arch)) }}

Comment thread
siddharthvaddem marked this conversation as resolved.
steps:
# ─── Checkout ─────────────────────────────────────────────
- name: Checkout code
uses: actions/checkout@v3
uses: actions/checkout@v4

# ─── Setup Node.js ────────────────────────────────────────
- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '22'
node-version: 22
cache: npm

# ─── Setup Python (needed by some native deps) ────────────
- name: Setup Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: '3.11'

# ─── Install Dependencies ─────────────────────────────────
- name: Install dependencies
run: npm ci
Comment thread
siddharthvaddem marked this conversation as resolved.

- name: Install app dependencies
run: npx electron-builder install-app-deps

- name: Build macOS app
run: npm run build:mac
# ─── Import Code Signing Certificate ──────────────────────
# This is the KEY step that makes CI signing work.
# We create a temporary keychain, import the .p12 cert into it,
# and set it as the default so codesign can find it.
- name: Import code signing certificate
env:
MAC_CERTIFICATE_P12: ${{ secrets.MAC_CERTIFICATE_P12 }}
MAC_CERTIFICATE_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }}
run: |
# Create a temporary keychain
KEYCHAIN_PATH=$RUNNER_TEMP/build.keychain-db
KEYCHAIN_PASSWORD=$(openssl rand -base64 32)

# Create and configure keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

# Decode and import certificate
echo "$MAC_CERTIFICATE_P12" | base64 --decode > $RUNNER_TEMP/certificate.p12
security import $RUNNER_TEMP/certificate.p12 \
-k "$KEYCHAIN_PATH" \
-P "$MAC_CERTIFICATE_PASSWORD" \
-T /usr/bin/codesign \
-T /usr/bin/security

# Allow codesign to access the keychain without UI prompt
security set-key-partition-list -S apple-tool:,apple: -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"

# Add to keychain search path (makes it the default)
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"')

# Verify the identity is available
security find-identity -v -p codesigning "$KEYCHAIN_PATH"

# Clean up the .p12 file
rm -f $RUNNER_TEMP/certificate.p12
Comment thread
siddharthvaddem marked this conversation as resolved.

# ─── Build Vite + Electron ────────────────────────────────
- name: Build Vite + Electron
run: npx tsc && npx vite build

# ─── Package with electron-builder ────────────────────────
# electron-builder handles deep codesigning the .app bundle
# "notarize: false" in electron-builder.json5 prevents it from
# trying its own notarization flow
- name: Package .app bundle
run: npx electron-builder --mac --${{ matrix.arch }} --dir
env:
CSC_NAME: "Samir Patil (N26FZ4GW28)"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Comment thread
siddharthvaddem marked this conversation as resolved.

- name: Upload macOS build
# ─── Read version from package.json ───────────────────────
- name: Get version
id: version
run: echo "version=$(node -p 'require(\"./package.json\").version')" >> $GITHUB_OUTPUT

# ─── Locate the .app bundle ───────────────────────────────
- name: Find .app bundle
id: find_app
run: |
VERSION="${{ steps.version.outputs.version }}"
echo "=== Release directory contents ==="
ls -laR "release/${VERSION}/" || echo "release/${VERSION}/ not found"
echo "=== Searching for .app bundle ==="
APP_BUNDLE=$(find "release/${VERSION}" -maxdepth 4 -name "*.app" -type d | head -n1)
if [ -z "$APP_BUNDLE" ]; then
echo "::error::No .app bundle found in release/${VERSION}/"
exit 1
fi
echo "app_bundle=$APP_BUNDLE" >> $GITHUB_OUTPUT
echo "Found: $APP_BUNDLE"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# ─── Verify .app signature ────────────────────────────────
- name: Verify .app code signature
run: codesign --verify --deep --strict "${{ steps.find_app.outputs.app_bundle }}"

# ─── Create DMG ───────────────────────────────────────────
- name: Create DMG
id: dmg
run: |
VERSION="${{ steps.version.outputs.version }}"
ARCH="${{ matrix.arch }}"
DMG_NAME="Openscreen-Mac-${ARCH}-${VERSION}.dmg"
RELEASE_DIR="release/${VERSION}"
DMG_OUTPUT="${RELEASE_DIR}/${DMG_NAME}"
STAGING="${RELEASE_DIR}/dmg-staging"

mkdir -p "$STAGING"
cp -R "${{ steps.find_app.outputs.app_bundle }}" "$STAGING/"
ln -s /Applications "$STAGING/Applications"

hdiutil create \
-srcfolder "$STAGING" \
-volname "Openscreen" \
-fs HFS+ \
-fsargs "-c c=64,a=16,e=16" \
-format UDBZ \
"$DMG_OUTPUT"

rm -rf "$STAGING"

echo "dmg_path=$DMG_OUTPUT" >> $GITHUB_OUTPUT
echo "dmg_name=$DMG_NAME" >> $GITHUB_OUTPUT

# ─── Sign DMG ─────────────────────────────────────────────
- name: Sign DMG
run: |
codesign --force \
--sign "Developer ID Application: Samir Patil (N26FZ4GW28)" \
--timestamp \
"${{ steps.dmg.outputs.dmg_path }}"

# ─── Notarize DMG ────────────────────────────────────────
# On CI we can't use keychain profiles for notarytool, so we
# pass credentials directly via env vars / flags
- name: Notarize DMG
run: |
xcrun notarytool submit "${{ steps.dmg.outputs.dmg_path }}" \
--apple-id "${{ secrets.APPLE_ID }}" \
--team-id "${{ secrets.APPLE_TEAM_ID }}" \
--password "${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}" \
--wait
timeout-minutes: 15

# ─── Staple ───────────────────────────────────────────────
- name: Staple notarization ticket
run: xcrun stapler staple "${{ steps.dmg.outputs.dmg_path }}"

# ─── Validate ─────────────────────────────────────────────
- name: Validate stapled DMG
run: |
xcrun stapler validate "${{ steps.dmg.outputs.dmg_path }}"
spctl -a -vv -t install "${{ steps.dmg.outputs.dmg_path }}"

# ─── Upload Artifact ──────────────────────────────────────
- name: Upload notarized DMG
uses: actions/upload-artifact@v4
with:
name: macos-installer
path: release/**/*.dmg
name: openscreen-mac-${{ matrix.arch }}
path: ${{ steps.dmg.outputs.dmg_path }}
retention-days: 30

# ─── Cleanup Keychain ─────────────────────────────────────
- name: Cleanup keychain
if: always()
run: security delete-keychain $RUNNER_TEMP/build.keychain-db || true

build-linux:
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dist
dist-electron
dist-ssr
*.local
.env

# Editor directories and files
.vscode/*
Expand Down
58 changes: 30 additions & 28 deletions electron-builder.json5
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,18 @@
"!CONTRIBUTING.md",
"!LICENSE"
],
"extraResources": [
{
"from": "public/wallpapers",
"to": "assets/wallpapers"
}
],
"publish": [{"provider": "github"}],

"mac": {
"hardenedRuntime": false,
"extraResources": [
{
"from": "public/wallpapers",
"to": "assets/wallpapers"
}
],

"mac": {
"notarize": false,
"hardenedRuntime": true,
"entitlements": "macos.entitlements",
"entitlementsInherit": "macos.entitlements",
"target": [
{
"target": "dmg",
Expand All @@ -38,13 +40,13 @@
],
"icon": "icons/icons/mac/icon.icns",
"artifactName": "${productName}-Mac-${arch}-${version}-Installer.${ext}",
"extendInfo": {
"NSAudioCaptureUsageDescription": "OpenScreen needs audio capture permission to record system audio.",
"NSMicrophoneUsageDescription": "OpenScreen needs microphone access to record voice audio.",
"NSCameraUsageDescription": "OpenScreen needs camera access to record webcam video.",
"NSCameraUseContinuityCameraDeviceType": true,
"com.apple.security.device.audio-input": true
}
"extendInfo": {
"NSAudioCaptureUsageDescription": "OpenScreen needs audio capture permission to record system audio.",
"NSMicrophoneUsageDescription": "OpenScreen needs microphone access to record voice audio.",
"NSCameraUsageDescription": "OpenScreen needs camera access to record webcam video.",
"NSCameraUseContinuityCameraDeviceType": true,
"com.apple.security.device.audio-input": true
}
Comment thread
siddharthvaddem marked this conversation as resolved.
},
"linux": {
"target": [
Expand All @@ -54,14 +56,14 @@
"artifactName": "${productName}-Linux-${version}.${ext}",
"category": "AudioVideo"
},
"win": {
"target": [
"nsis"
],
"icon": "icons/icons/win/icon.ico"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
}
}
"win": {
"target": [
"nsis"
],
"icon": "icons/icons/win/icon.ico"
},
"nsis": {
"oneClick": false,
"allowToChangeInstallationDirectory": true
}
}
25 changes: 25 additions & 0 deletions macos.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?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>
<!-- Required for Electron's V8 JIT compilation -->
<key>com.apple.security.cs.allow-jit</key>
<true/>

<!-- Required for Electron's native module loading -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>

<!-- Required for loading Electron's bundled frameworks/dylibs -->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>

<!-- Audio input (microphone / system audio capture) -->
<key>com.apple.security.device.audio-input</key>
<true/>

<!-- Camera (webcam capture) -->
<key>com.apple.security.device.camera</key>
<true/>
</dict>
</plist>
Loading
Loading