diff --git a/buildResources/entitlements.inherit.plist b/buildResources/entitlements.inherit.plist
index 8c3993f..a7a925c 100644
--- a/buildResources/entitlements.inherit.plist
+++ b/buildResources/entitlements.inherit.plist
@@ -4,6 +4,8 @@
com.apple.security.app-sandbox
+com.apple.security.cs.allow-unsigned-executable-memory
+
com.apple.security.inherit
diff --git a/buildResources/entitlements.plist b/buildResources/entitlements.plist
index 53effcb..44d4e94 100644
--- a/buildResources/entitlements.plist
+++ b/buildResources/entitlements.plist
@@ -4,6 +4,8 @@
com.apple.security.app-sandbox
+com.apple.security.cs.allow-unsigned-executable-memory
+
com.apple.security.device.audio-input
com.apple.security.device.camera
diff --git a/electron-builder.yaml b/electron-builder.yaml
index aaf7c39..8ff6017 100644
--- a/electron-builder.yaml
+++ b/electron-builder.yaml
@@ -3,8 +3,10 @@ directories:
output: dist
appId: edu.mit.scratch.scratch-desktop
productName: "Scratch Desktop"
+afterSign: "scripts/afterSign.js"
mac:
category: public.app-category.education
+ hardenedRuntime: true
icon: buildResources/ScratchDesktop.icns
provisioningProfile: embedded.provisionprofile
target:
diff --git a/package-lock.json b/package-lock.json
index 06a048b..d9ef948 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4317,6 +4317,29 @@
}
}
},
+ "electron-notarize": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-0.2.1.tgz",
+ "integrity": "sha512-oZ6/NhKeXmEKNROiFmRNfytqu3cxqC95sjooG7kBXQVEUSQkZnbiAhxVh5jXngL881G197pbwpeVPJyM7Ikmxw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "fs-extra": "^8.1.0"
+ },
+ "dependencies": {
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ }
+ }
+ },
"electron-publish": {
"version": "22.2.0",
"resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-22.2.0.tgz",
diff --git a/package.json b/package.json
index 8299b6c..70001c5 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
"electron": "^6.1.7",
"electron-builder": "^22.2.0",
"electron-devtools-installer": "^2.2.4",
+ "electron-notarize": "^0.2.1",
"electron-store": "^3.3.0",
"electron-webpack": "^2.7.4",
"eslint": "^5.16.0",
diff --git a/scripts/afterSign.js b/scripts/afterSign.js
new file mode 100644
index 0000000..d544191
--- /dev/null
+++ b/scripts/afterSign.js
@@ -0,0 +1,42 @@
+const {notarize} = require('electron-notarize');
+
+const notarizeMacBuild = async function (context) {
+ // keep this in sync with appId in the electron-builder config
+ const appId = 'edu.mit.scratch.scratch-desktop';
+
+ if (!process.env.AC_USERNAME) {
+ throw new Error(
+ 'Notarizing the macOS build requires an Apple ID.\n' +
+ 'Please set the environment variable AC_USERNAME.\n' +
+ 'Make sure your keychain has an item for "Application Loader: your@apple.id"'
+ );
+ }
+
+ const appleId = process.env.AC_USERNAME;
+ const appleIdKeychainItem = `Application Loader: ${appleId}`;
+
+ console.log(`Notarizing with Apple ID "${appleId}" and keychain item "${appleIdKeychainItem}"`);
+
+ const {appOutDir} = context;
+ const productFilename = context.packager.appInfo.productFilename;
+ await notarize({
+ appBundleId: appId,
+ appPath: `${appOutDir}/${productFilename}.app`,
+ appleId,
+ appleIdPassword: `@keychain:${appleIdKeychainItem}`
+ });
+};
+
+const afterSign = async function (context) {
+ const {electronPlatformName} = context;
+
+ switch (electronPlatformName) {
+ case 'mas': // macOS build for Mac App Store
+ break;
+ case 'darwin': // macOS build NOT for Mac App Store
+ await notarizeMacBuild(context);
+ break;
+ }
+};
+
+module.exports = afterSign;
diff --git a/scripts/electron-builder-wrapper.js b/scripts/electron-builder-wrapper.js
index ed50dc8..f5aa406 100644
--- a/scripts/electron-builder-wrapper.js
+++ b/scripts/electron-builder-wrapper.js
@@ -52,11 +52,20 @@ const runBuilder = function (targetGroup) {
const platformFlag = getPlatformFlag();
const command = `electron-builder ${platformFlag} ${targetGroup}`;
console.log(`running: ${command}`);
- spawnSync(command, {
+ const result = spawnSync(command, {
env: childEnvironment,
shell: true,
stdio: 'inherit'
});
+ if (result.error) {
+ throw result.error;
+ }
+ if (result.signal) {
+ throw new Error(`Child process terminated due to signal ${result.signal}`);
+ }
+ if (result.status) {
+ throw new Error(`Child process returned status code ${result.status}`);
+ }
};
/**
@@ -69,8 +78,10 @@ const calculateTargets = function () {
// run in two passes so we can skip signing the appx
return ['nsis', 'appx'];
case 'darwin':
- // run in one pass for slightly better speed
- return ['dmg mas'];
+ // Running 'dmg' and 'mas' in the same pass causes electron-builder to skip signing the non-MAS app copy.
+ // Running them as separate passes means they both get signed.
+ // Seems like a bug in electron-builder...
+ return ['dmg', 'mas'];
}
throw new Error(`Could not determine targets for platform: ${process.platform}`);
};