Add mac sandbox build support

This commit is contained in:
yash-signal
2026-02-27 12:57:31 -06:00
committed by Yash
parent 54e5b64ab0
commit e3560adbcf
9 changed files with 102 additions and 10 deletions

View File

@@ -308,16 +308,15 @@ Then, run the tests using `pnpm run test-release`.
macOS requires apps to be code signed with an Apple certificate. To test development builds
you can ad-hoc sign the packaged app which will let you run it locally.
1. In `package.json` remove the macOS signing script: `"sign": "./ts/scripts/sign-macos.node.js",`
2. Build the app and ad-hoc sign the app bundle:
1. Build the app while skipping the custom macOS signing script:
```
pnpm run generate
pnpm run build
SKIP_SIGNING_SCRIPT=1 pnpm run build
cd release
# Pick the desired app bundle: mac, mac-arm64, or mac-universal
cd mac-arm64
codesign --force --deep --sign - Signal.app
```
3. Now you can run the app locally.
2. Now you can run the app locally.

View File

@@ -0,0 +1,15 @@
<!-- Copyright 2026 Signal Messenger, LLC -->
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
<?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.app-sandbox</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,31 @@
<!-- Copyright 2026 Signal Messenger, LLC -->
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
<?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.app-sandbox</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.microphone</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-write</key>
<true/>
<key>com.apple.security.personal-information.photos-library</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>U68MSDN6DR.org.whispersystems.signal-desktop</string>
</array>
</dict>
</plist>

View File

@@ -100,6 +100,7 @@
"build:styles:tailwind": "tailwindcss -i ./stylesheets/tailwind-config.css -o ./stylesheets/tailwind.css",
"build:electron": "electron-builder --config.extraMetadata.environment=$SIGNAL_ENV",
"build:release": "cross-env SIGNAL_ENV=production pnpm run build:electron --config.directories.output=release",
"build:mas-dev": "bash ./scripts/build-mas-dev.sh",
"build:release-win32-all": "pnpm run build:release --arm64 --x64",
"build:preload-cache": "node ts/scripts/generate-preload-cache.node.js",
"build:emoji": "run-p build:emoji:32 build:emoji:64",
@@ -442,12 +443,12 @@
}
],
"mergeASARs": true,
"sign": "./ts/scripts/sign-macos.node.js",
"releaseInfo": {
"vendor": {
"minOSVersion": "21.0.1"
}
},
"sign": "./ts/scripts/sign-macos.node.js",
"singleArchFiles": "node_modules/@signalapp/{libsignal-client/prebuilds/**,ringrtc/build/**,sqlcipher/prebuilds/**}",
"target": [
{
@@ -469,6 +470,20 @@
"NSAutoFillRequiresTextContentTypeForOneTimeCodeOnMac": true
}
},
"masDev": {
"type": "development",
"sign": null,
"hardenedRuntime": false,
"entitlements": "./build/entitlements.mas-dev.plist",
"entitlementsInherit": "./build/entitlements.mas-dev.inherit.plist",
"preAutoEntitlements": false,
"extendInfo": {
"ElectronTeamID": "U68MSDN6DR",
"NSCameraUsageDescription": "Signal uses your camera for video calling.",
"NSMicrophoneUsageDescription": "Signal uses your microphone for voice and video calling.",
"ITSAppUsesNonExemptEncryption": true
}
},
"win": {
"signtoolOptions": {
"certificateSubjectName": "Signal Messenger, LLC",

25
scripts/build-mas-dev.sh Executable file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/env bash
# Copyright 2026 Signal Messenger, LLC
# SPDX-License-Identifier: AGPL-3.0-only
set -euo pipefail
if [[ -z "${MAS_PROVISIONING_PROFILE:-}" ]]; then
echo "MAS_PROVISIONING_PROFILE is required" >&2
exit 1
fi
# Electron Builder applies `mac` config first and then `masDev`.
# Override mac configuration during build to ensure consistency between
# the two sets of values, otherwise the mas-dev build will crash on launch.
SIGNAL_ENV="${SIGNAL_ENV:-production}" \
SKIP_SIGNING_SCRIPT=1 \
pnpm run build:electron \
--config.directories.output=release \
--mac mas-dev \
--arm64 \
--publish=never \
--config.mac.entitlements=./build/entitlements.mas-dev.plist \
--config.mac.entitlementsInherit=./build/entitlements.mas-dev.inherit.plist \
--config.mac.preAutoEntitlements=false \
--config.masDev.provisioningProfile="$MAS_PROVISIONING_PROFILE"

View File

@@ -16,7 +16,7 @@ export async function afterPack({
);
let localesPath: string;
if (electronPlatformName === 'darwin') {
if (electronPlatformName === 'darwin' || electronPlatformName === 'mas') {
const { productFilename } = packager.appInfo;
// en.lproj/*

View File

@@ -13,7 +13,7 @@ export async function afterPack({
const { productFilename } = packager.appInfo;
let target;
if (electronPlatformName === 'darwin') {
if (electronPlatformName === 'darwin' || electronPlatformName === 'mas') {
target = `${productFilename}.app`;
} else if (electronPlatformName === 'win32') {
target = `${productFilename}.exe`;
@@ -43,9 +43,11 @@ export async function afterPack({
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
// Disables the --inspect and --inspect-brk family of CLI options
[FuseV1Options.EnableNodeCliInspectArguments]: enableInspectArguments,
// Enables validation of the app.asar archive on macOS/Windows
// Enables validation of the app.asar archive on macOS/Windows.
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]:
electronPlatformName === 'darwin' || electronPlatformName === 'win32',
electronPlatformName === 'darwin' ||
electronPlatformName === 'mas' ||
electronPlatformName === 'win32',
// Enforces that Electron will only load your app from "app.asar" instead of
// its normal search paths
[FuseV1Options.OnlyLoadAppFromAsar]: true,

View File

@@ -21,7 +21,7 @@ export async function afterPack({
packager,
electronPlatformName,
}: AfterPackContext): Promise<void> {
if (electronPlatformName !== 'darwin') {
if (electronPlatformName !== 'darwin' && electronPlatformName !== 'mas') {
return;
}

View File

@@ -10,6 +10,11 @@ const { realpath } = fsExtra;
// eslint-disable-next-line max-len
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
export async function sign(configuration: any): Promise<void> {
if (process.env.SKIP_SIGNING_SCRIPT === '1') {
console.log('SKIP_SIGNING_SCRIPT=1, skipping custom macOS signing script');
return;
}
const scriptPath = process.env.SIGN_MACOS_SCRIPT;
if (!scriptPath) {
throw new Error(