aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2022-05-05 01:06:13 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2022-05-16 00:55:03 +0200
commita9d9b6cc1b29e424ade426d82db2fc54f0ea8988 (patch)
tree84da5ec1c544863ab7731aefbac657cf2d80ead5
parentfeat: use wayland when available (diff)
downloadsophie-a9d9b6cc1b29e424ade426d82db2fc54f0ea8988.tar.gz
sophie-a9d9b6cc1b29e424ade426d82db2fc54f0ea8988.tar.zst
sophie-a9d9b6cc1b29e424ade426d82db2fc54f0ea8988.zip
refactor: electron-builder config
Signed-off-by: Kristóf Marussy <kristof@marussy.com>
-rw-r--r--.electron-builder.config.cjs104
-rw-r--r--config/burnFuses.cjs47
-rw-r--r--config/enableWaylandAutoDetection.cjs86
3 files changed, 152 insertions, 85 deletions
diff --git a/.electron-builder.config.cjs b/.electron-builder.config.cjs
index 3eef91c..4402088 100644
--- a/.electron-builder.config.cjs
+++ b/.electron-builder.config.cjs
@@ -1,90 +1,9 @@
1const { readFile, rename, writeFile } = require('node:fs/promises');
2const path = require('node:path');
3 1
4const { Arch } = require('electron-builder'); 2const { Arch } = require('electron-builder');
5const { flipFuses, FuseV1Options, FuseVersion } = require('@electron/fuses'); 3const { FuseV1Options, FuseVersion } = require('@electron/fuses');
6 4
7/** 5const burnFuses = require('./config/burnFuses.cjs');
8 * Hardens the shipped electron binary by burning some electron fuses. 6const enableWaylandAutoDetection = require('./config/enableWaylandAutoDetection.cjs');
9 *
10 * Enabled chromium cookie encryption and disables options that could be
11 * used to execute arbitrary code in the main process to circumvent cookie encryption:
12 * - Running the application as a plain node process is disabled.
13 * - Setting options through the `NODE_OPTIONS` environment variable is disabled.
14 * - Attaching a debugger through the `--inspect` family of options is disabled.
15 * - Will onload load the application from the ASAR archive.
16 *
17 * @param {import('electron-builder').AfterPackContext} context The `electron-builder` context.
18 * @return {Promise<void>} The promise to flip the fuses.
19 * @see https://github.com/electron/fuses
20 */
21async function burnFuses(context) {
22 /** @type {string} */
23 const ext =
24 {
25 darwin: '.app',
26 win32: '.exe',
27 }[context.electronPlatformName] || '';
28 const electronBinaryPath = path.join(
29 context.appOutDir,
30 `${context.packager.appInfo.productFilename}${ext}`,
31 );
32 /** @type {import('@electron/fuses').FuseConfig<boolean>} */
33 const fuseConfig = {
34 version: FuseVersion.V1,
35 resetAdHocDarwinSignature:
36 context.electronPlatformName === 'darwin' && context.arch === Arch.arm64,
37 [FuseV1Options.RunAsNode]: false,
38 [FuseV1Options.EnableCookieEncryption]: true,
39 [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
40 [FuseV1Options.EnableNodeCliInspectArguments]: false,
41 // TODO: Revisit this: IF set to 'true' the packaged app doesn't start up on macos (x86)
42 [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: false,
43 [FuseV1Options.OnlyLoadAppFromAsar]: true,
44 };
45 return flipFuses(electronBinaryPath, fuseConfig);
46}
47
48/**
49 * Adds a wrapper scripts that detects in wayland is in use and enabled it in chromium.
50 *
51 * The script in `build-heleprs/detect_wayland.sh` uses the `WAYLAND_DISPLAY` environmental
52 * variable to detect whether wayland is in use.
53 *
54 * If wayland is in use, the script enables the wayland ozone backed for chromium
55 * and pipewire screen sharing. Otherwise, the x11 ozone backend will be used.
56 *
57 * @param {import('electron-builder').AfterPackContext} context The `electron-builder` context.
58 * @return {Promise<void>} The promise to add the wrapper script.
59 * @see https://stackoverflow.com/a/45537237
60 */
61async function enableWaylandAutoDetection(context) {
62 const { appOutDir, packager: { appInfo: { productName, productFilename } } } = context;
63 const electronBinaryPath = path.join(appOutDir, productFilename);
64 const newFilename = `${productFilename}-bin`;
65 const newElectronBinaryPath = path.join(appOutDir, newFilename);
66 await rename(electronBinaryPath, newElectronBinaryPath);
67 const wrapperScriptPath = path.join(__dirname, 'build-helpers/detect_wayland.sh');
68 const wrapperScriptTempate = await readFile(wrapperScriptPath, 'utf8');
69 const replacements = new Map([
70 ['PRODUCT_NAME', productName],
71 ['REAL_BINARY_NAME', newFilename],
72 ]);
73 const wrapperScript = wrapperScriptTempate.replaceAll(
74 /\{\{([^}]+)\}\}/g,
75 (_match, /** @type {string} */ variable) => {
76 const replacement = replacements.get(variable);
77 if (replacement === undefined) {
78 throw new Error(`Unknown variable: ${variable}`);
79 }
80 return replacement;
81 },
82 );
83 await writeFile(electronBinaryPath, wrapperScript, {
84 encoding: 'utf8',
85 mode: 0o755,
86 });
87}
88 7
89/** 8/**
90 * @type {import('electron-builder').Configuration} 9 * @type {import('electron-builder').Configuration}
@@ -105,7 +24,22 @@ const config = {
105 '!**/*.map', 24 '!**/*.map',
106 ], 25 ],
107 afterPack(context) { 26 afterPack(context) {
108 return burnFuses(context); 27 /*
28 * Enables chromium cookie encryption and disables options that could be
29 * used to execute arbitrary code in the main process to circumvent cookie encryption:
30 */
31 return burnFuses(context, {
32 version: FuseVersion.V1,
33 resetAdHocDarwinSignature:
34 context.electronPlatformName === 'darwin' && context.arch === Arch.arm64,
35 [FuseV1Options.RunAsNode]: false,
36 [FuseV1Options.EnableCookieEncryption]: true,
37 [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
38 [FuseV1Options.EnableNodeCliInspectArguments]: false,
39 // TODO: Revisit this: IF set to `true` the packaged app doesn't start up on macos (x86)
40 [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: false,
41 [FuseV1Options.OnlyLoadAppFromAsar]: true,
42 });
109 }, 43 },
110 async afterSign(context) { 44 async afterSign(context) {
111 if (context.electronPlatformName === 'linux') { 45 if (context.electronPlatformName === 'linux') {
diff --git a/config/burnFuses.cjs b/config/burnFuses.cjs
new file mode 100644
index 0000000..a6ea197
--- /dev/null
+++ b/config/burnFuses.cjs
@@ -0,0 +1,47 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21const path = require('node:path');
22
23const { flipFuses } = require('@electron/fuses');
24
25/**
26 * Hardens the shipped electron binary by burning some electron fuses.
27 *
28 *
29 * @param {import('electron-builder').AfterPackContext} context The `electron-builder` context.
30 * @param {import('@electron/fuses').FuseConfig<boolean>} config The fuses to burn.
31 * @return {Promise<void>} The promise to flip the fuses.
32 * @see https://github.com/electron/fuses
33 */
34module.exports = async function burnFuses(context, config) {
35 /** @type {string} */
36 const ext =
37 {
38 darwin: '.app',
39 win32: '.exe',
40 }[context.electronPlatformName] || '';
41 const electronBinaryPath = path.join(
42 context.appOutDir,
43 `${context.packager.appInfo.productFilename}${ext}`,
44 );
45 /** @type {import('@electron/fuses').FuseConfig<boolean>} */
46 return flipFuses(electronBinaryPath, config);
47};
diff --git a/config/enableWaylandAutoDetection.cjs b/config/enableWaylandAutoDetection.cjs
new file mode 100644
index 0000000..393ec91
--- /dev/null
+++ b/config/enableWaylandAutoDetection.cjs
@@ -0,0 +1,86 @@
1/*
2 * Copyright (C) 2022 Kristóf Marussy <kristof@marussy.com>
3 *
4 * This file is part of Sophie.
5 *
6 * Sophie is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as
8 * published by the Free Software Foundation, version 3.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: AGPL-3.0-only
19 */
20
21const { readFile, rename, writeFile } = require('node:fs/promises');
22const path = require('node:path');
23
24const templatePath = path.join(__dirname, '../build-helpers/detect_wayland.sh');
25
26/**
27 * Replaces the `{{handlebars}}` in the template with the replacements.
28 *
29 * Each `{{VARIABLE}}` will be repalced by `value` in the replacement `['VARIABLE', value]`.
30 *
31 * @param {string} template The template.
32 * @param {[string, string][]} replacements The replacements to apply.
33 * @returns
34 */
35function executeTemplate(template, ...replacements) {
36 const replacementsMap = new Map(replacements);
37 return template.replaceAll(
38 /{{([^}]+)}}/g,
39 (_match, /** @type {string} */ variable) => {
40 const replacement = replacementsMap.get(variable);
41 if (replacement === undefined) {
42 throw new Error(`Unknown variable: ${variable}`);
43 }
44 return replacement;
45 },
46 );
47}
48
49/**
50 * Adds a wrapper scripts that detects in wayland is in use and enabled it in chromium.
51 *
52 * The script in `build-heleprs/detect_wayland.sh` uses the `WAYLAND_DISPLAY` environmental
53 * variable to detect whether wayland is in use.
54 *
55 * If wayland is in use, the script enables the wayland ozone backed for chromium
56 * and pipewire screen sharing. Otherwise, the x11 ozone backend will be used.
57 *
58 * @param {import('electron-builder').AfterPackContext} context The `electron-builder` context.
59 * @return {Promise<void>} The promise to add the wrapper script.
60 * @see https://stackoverflow.com/a/45537237
61 */
62module.exports = async function enableWaylandAutoDetection(context) {
63 const {
64 appOutDir,
65 packager: {
66 appInfo: { productName, productFilename },
67 },
68 } = context;
69
70 const electronBinaryPath = path.join(appOutDir, productFilename);
71 const newFilename = `${productFilename}-bin`;
72 const newElectronBinaryPath = path.join(appOutDir, newFilename);
73
74 await rename(electronBinaryPath, newElectronBinaryPath);
75
76 const wrapperScriptTempate = await readFile(templatePath, 'utf8');
77 const wrapperScript = executeTemplate(
78 wrapperScriptTempate,
79 ['PRODUCT_NAME', productName],
80 ['REAL_BINARY_NAME', newFilename],
81 );
82 await writeFile(electronBinaryPath, wrapperScript, {
83 encoding: 'utf8',
84 mode: 0o755,
85 });
86};