openSUSE:打包 Electron

跳转到:导航搜索

这是一个未完成的草稿


在开始之前

Electron 不要与 nw.js 混淆,nw.js 远不如 Electron 流行,并且(截至 2024 年 5 月)没有发行版对其进行打包。尚不清楚 Electron 是否可以未经修改地运行 nw.js 应用程序。

下载依赖项

大多数(全部?)应用程序都有一个package.json在根目录下,并使用流行的包管理器,例如 npm 或 yarn。 这两者通常可以在分发依赖项期间执行任意命令

避免 使用“离线缓存”功能在构建期间调用包管理器。 这使得修补变得困难(npm)或不可能(yarn)。

相反,您必须以不运行构建脚本(并且最好产生可重现的结果)的方式调用您的包管理器

#npm (package-lock.json)
npm ci  --verbose --ignore-scripts
#yarn (yarn.lock)
yarn install --frozen-lockfile --ignore-engines --ignore-platform  --ignore-scripts --link-duplicates

如果 yarn 无法通过 shrinkwrap 文件匹配包,则可以使用此命令,该命令执行不太严格的检查

yarn install --pure-lockfile --ignore-engines --ignore-platform  --ignore-scripts --link-duplicates

Yarn 存在已知的轻微可重现性问题,它将您的 Node 主要版本和计算机架构嵌入到 tarball 中。

移除禁止的条目

NPM 注册表中的预编译二进制文件是 tarball 中的一个严重问题。 您必须确保它们在打包供应商 tarball 之前不会被使用或分发。 移除的最佳时机是在打包供应商 tarball 之前。

一个可以消除大部分预编译二进制文件的示例脚本

#zypper in file findutils moreutils
find . -name '*.node' -print -delete
find . -name '*.jar' -print -delete
find . -name '*.dll' -print -delete
find . -name '*.exe' -print -delete
find . -name '*.dylib' -print -delete
find . -name '*.so' -print -delete
find . -name '*.o' -print -delete
find . -name '*.a' -print -delete
find . -name '*.wasm' -print -delete

#We use sponge to avoid a race condition between find and rm
find . -type f| sponge |\
    xargs -P"$(nproc)" -- sh -c 'file -S "$@" | grep -v '\'': .*script'\'' | grep '\'': .*executable'\'' | tee /dev/stderr | sed '\''s/: .*//'\'' | xargs rm -fv'

应该在此阶段移除任何捆绑的库

仅将node_modules打包到供应商 tarball 中。

某些应用程序可以包含多个 shrinkwrap 文件,例如 vscode

构建

通常,%build 的开头应该如下所示

%build
export CFLAGS="%{optflags}"
export CXXFLAGS="%{optflags}"
export LDFLAGS="%{?build_ldflags}"
export MAKEFLAGS="%{_smp_mflags}"
export ELECTRON_SKIP_BINARY_DOWNLOAD=1
%electron_rebuild

%electron_rebuild 执行node_modules中的任何构建脚本,包括可能针对 Electron 标头构建原生模块。

原生模块

如果您的包包含仅使用稳定 API(napi_*uv_*)的原生模块,您必须在您的 spec 中包含以下内容,以确保它们能够成功加载

Requires: nodejs-electron%{_isa}
…
%check
%electron_check_native

如果使用不稳定的 API,此检查将失败。 在这种情况下,使用以下内容以确保在 Electron 的主要更新上进行重建

#in Requires: section
%electron_req
…
%check
%electron_check_native_unstable

C/C++ 标志

  • 您正在构建一个可加载的插件。 用于可加载插件的正确重定位模型是-fpic -fno-semantic-interposition。 如果您使用更宽松的模型(-fpie或不可重定位),您将收到链接器错误。 如果您使用更严格的模型(-fpic),它将起作用,但会产生次优代码。
  • 许多上游构建脚本由于仅在不支持符号可见性的 Windows 上进行测试,因此会过度导出符号。 添加-fvisibility=hidden以修复此问题。 这不会破坏正确标记其可见性的 Node 所需的导出。

如果所有 C/C++ 代码都由 %electron_rebuild 宏构建,则这些标志将自动注入。 否则,您需要手动添加它们。

Asar

由于可重现性问题和 rpmlint 看不到 asar 的内容,因此应避免使用。 最好以解包的形式分发应用程序。 TODO:记录如何在 electron-builder 中禁用 asar。

特定技术

yarn

%electron_rebuild 宏执行 npm。 不可能使用 yarn 来执行构建脚本。

如果您的构建脚本检查 yarn,您需要将其修补掉(vscode 的示例)或以其他方式欺骗它们。

Rust

通常,Rust 模块仅使用稳定的 API,不需要 Electron 标头来构建。 通用 Rust 指南可以对其进行修改以适用于可加载插件

Rust 没有 gcc 的全局-fvisibility=hidden;您应该检查无用的导出并修补它们(如果可能)。

混合 Rust/C++ 代码库需要单独关注,因为需要解决 rustc 的许多 FFI 错误。

esbuild

这是一个由两部分组成的 javascript 打包器/转译器:用 Go 编写的主程序和 JS 辅助库。 与每个 JS 库一样,它将被分发到您的 node_modules 中。 在处理使用 esbuild 的程序时,您需要了解两件事

  • 版本 0.17 是一个完整的重写,破坏了命令行 API 和 JS 库。 (截至 2024 年,我们没有在野外看到旧版本的 esbuild 客户端库)
  • esbuild 是一个敌对的上游,JS 库包含多个粗糙的 DRM 检查,旨在使发行版构建失败(该程序也有一个 DRM,但我们已经修补了它

仅仅让 %electron_rebuild 成功

%build
#esbuild is not actually used, it is only declared as a transitive dependency of some webpack plugin
export ESBUILD_BINARY_PATH=/bin/true
…
%electron_rebuild
--- a/node_modules/esbuild/install.js
+++ b/node_modules/esbuild/install.js
@@ -85,7 +85,7 @@
     }
     throw err;
   }
-  if (stdout !== versionFromPackageJSON) {
+  if (0) {
     throw new Error(`Expected ${JSON.stringify(versionFromPackageJSON)} but got ${JSON.stringify(stdout)}`);
   }
 }

这足以用于例如 Bitwarden,它没有使用 esbuild(只是将其作为传递依赖项包含在内)。

实际使用 esbuild

BuildRequires: esbuild >= 0.17
%build
export ESBUILD_BINARY_PATH=/usr/bin/esbuild
…
%electron_rebuild

截至版本 0.23,有三个额外的检查

--- a/node_modules/esbuild/install.js
+++ b/node_modules/esbuild/install.js
@@ -27,7 +27,7 @@
 var os = require("os");
 var path = require("path");
 var ESBUILD_BINARY_PATH = process.env.ESBUILD_BINARY_PATH || ESBUILD_BINARY_PATH;
-var isValidBinaryPath = (x) => !!x && x !== "/usr/bin/esbuild";
+var isValidBinaryPath = (x) => !!x
 var knownWindowsPackages = {
   "win32 arm64 LE": "@esbuild/win32-arm64",
   "win32 ia32 LE": "@esbuild/win32-ia32",
--- a/build/node_modules/esbuild/lib/main.js
+++ b/build/node_modules/esbuild/lib/main.js
@@ -710,7 +710,6 @@
       isFirstPacket = false;
       let binaryVersion = String.fromCharCode(...bytes);
       if (binaryVersion !== "0.23.0") {
-        throw new Error(`Cannot start service: Host version "${"0.23.0"}" does not match binary version ${quote(binaryVersion)}`);
       }
       return;
     }
@@ -1734,7 +1733,7 @@
 var os = require("os");
 var path = require("path");
 var ESBUILD_BINARY_PATH = process.env.ESBUILD_BINARY_PATH || ESBUILD_BINARY_PATH;
-var isValidBinaryPath = (x) => !!x && x !== "/usr/bin/esbuild";
+var isValidBinaryPath = (x) => !!x
 var packageDarwin_arm64 = "@esbuild/darwin-arm64";
 var packageDarwin_x64 = "@esbuild/darwin-x64";
 var knownWindowsPackages = {

@electron/fuses

这个库从设计上就是损坏的。 它试图对 Electron 可执行文件进行二进制巫术,这是不可能的,因为系统中只有一个 Electron 副本。 它将(故意)在 openSUSE 的 electron 二进制文件上失败,以确保您阅读此页面而不是分发一个损坏的包。

已知可以安全忽略的 Fuses

  • run_as_node
  • node_options
  • node_cli_inspect
  • embedded_asar_integrity_validation
  • only_load_app_from_asar
  • grant_file_protocol_extra_privileges

cookie_encryption

如果上游使用此 fuse,则使用本地构建的应用程序处理由上游二进制文件生成的用户数据可能会导致用户数据丢失!

已在 Signal 和 Element 中观察到此 fuse(幸运的是,这两个应用程序都不使用 cookie 进行任何操作)。

electron-builder

移除的功能

这些功能在上游 Electron 构建中可用,但 openSUSE 目前没有构建它们,因为它们未被使用

  • PPAPI
  • PDF
  • 打印
  • WebExtensions
  • WebGPU

已知错误和注意事项

app.isPackaged

始终返回 false。 欢迎提供补丁。

process.execPath

此 API 从设计上是损坏的。 大多数 Electron 应用程序需要一个 /usr/bin 启动脚本,并且应用程序不可能知道它的名称。 process.execPath 的每次使用都必须进行修补,否则将打开“关于 Electron”对话框而不是您的应用程序。 vscode 的示例

app.getPath('exe')

替代方法。 请参阅 此 bitwarden 错误 以获取示例。

app.relaunch

由于与上述相同的原因(以及相同的症状),此 API 从设计上是损坏的。 提供一个合适的包装脚本作为附加参数,vscode 的示例

ELECTRON_RUN_AS_NODE