首页
/ draw.io Desktop 个人 Fork 构建实战:DRAWIO_UNSIGNED 未签名构建与 electron-builder 全流程

draw.io Desktop 个人 Fork 构建实战:DRAWIO_UNSIGNED 未签名构建与 electron-builder 全流程

2026-09-04 13:34:23作者:江焘钦

draw.io Desktop 对外部贡献关闭,但采用 Apache 2.0 许可——你可以 Fork 它、修改它并为个人用途自行构建。本文基于仓库内 doc/BUILDING_FOR_PERSONAL_USE.md 的完整流程展开,覆盖从递归克隆、修改 Electron 外壳、免打包调试,到 macOS/Windows/Linux 三平台本地构建、GitHub Actions 无人值守构建,以及 Fork 维护与编辑器子模块定制的每一步,并逐一对应到仓库源码(sync.cjssrc/main/electron.jsbuild/sign-trusted.mjs 等)中的真实实现。读完后你能够独立完成一次“签名豁免、自动更新禁用、可维护”的 draw.io 自定义桌面构建。

1. 背景:为什么个人构建是“未签名”的

官方二进制产物由 GitHub Actions 的 release 工作流产出,其中代码签名与公证(notarization)依赖私有基础设施:Apple Developer 证书、Azure Trusted Signing(Windows)、以及私有仓库 drawio-dev(CI 从中获取内建好的编辑器产物)。这些资源对 Fork 全部不可用——因此个人构建必然是**未签名(unsigned)**的。

构建系统为此提供了显式开关。设置环境变量:

DRAWIO_UNSIGNED=true

即可跳过代码签名与公证。从源码看,这个开关在两处生效:

  • build/sign-trusted.mjs#L29-L33:Windows 签名钩子在签名每个文件前先检查该变量,命中则打印 DRAWIO_UNSIGNED=true: skipping code signing 并直接返回;未设置时会抛出含 “Trusted Signing dlib not found” 字样的错误(build/sign-trusted.mjs#L38-L41)——这就是文档中提到的“缺少 DRAWIO_UNSIGNED 会以 Trusted Signing dlib 报错失败”的根源。
  • build/notarize.mjs#L256-L260:macOS 的 afterSign 公证钩子,命中时打印 skipping notarization 并跳过公证。

官方签名体系的更多细节(Azure 资源、Apple 组织级 secrets、失败排查表)可参见 doc/RELEASE_PROCESS.md 第 11 节。

设置该变量即代表你接受两条约定:

  • 操作系统会警告(或拦截)未签名应用——macOS Gatekeeper 与 Windows SmartScreen 有其存在的理由,你是在为自己亲手构建的版本选择绕过它们;
  • 该构建只归你所有:非官方构建不受支持,不要针对修改后的版本提 issue,也不要把自己的构建当作 draw.io 分发给他人。

Linux 产物本身就不签名,因此该变量在 Linux 构建中并非必需(设置也无害)。

2. Fork 与克隆:递归子模块与 Node 版本

在 GitHub 上 Fork 上游 jgraph/drawio-desktop 仓库,然后递归克隆——核心编辑器是一个 git 子模块,缺少它构建会失败:

git clone --recursive https://github.com/<your-username>/drawio-desktop.git
cd drawio-desktop

若已经克隆但漏了 --recursive,补一句 git submodule update --init 即可。

子模块定义见 .gitmodules

[submodule "drawio"]
	path = drawio
	url = https://github.com/jgraph/drawio.git
	branch = dev

为什么子模块不可或缺?sync.cjs#L8-L12 在找不到 drawio/VERSION 时会直接报错退出,提示“Did you clone with --recursive or run git submodule update --init?”——版本号正是从子模块中读取的。

环境要求:Node.js 22.12 或更高版本(package.jsonengines.node 声明为 >=22.12.0),以及 npm;官方构建使用 Node 24。

3. 修改应用外壳:给窗口标题加后缀(完整示例)

文档给出的实战示例是:为所有窗口标题追加一个后缀,让你一眼确认当前运行的是自己的构建。

src/main/electron.js 中找到主窗口创建处(当前仓库位于第 427 行,文档写作“around line 442”):

	let mainWindow = new BrowserWindow(options)
	windowsRegistry.push(mainWindow)

在其后添加:

	mainWindow.on('page-title-updated', (event, title) =>
	{
		event.preventDefault();
		mainWindow.setTitle(title + ' (my build)');
	});

代码风格注意:缩进使用 Tab,开括号独占一行——这与 src/main/electron.js#L429-L431 等处的既有写法一致(如 if (lastWinSize.maximized) 块)。

此修改作用对象是 Electron 外壳(src/main/),与第 8 节的编辑器修改是两条不同的路径。

4. 免打包验证:npm start 与单实例锁

不需要打安装包就能验证改动:

npm install
npm start

package.jsonstart 脚本即 electron .,直接从源码树运行应用。若改动生效,每个窗口标题都应以 (my build) 结尾。设置 DRAWIO_ENV=dev(PowerShell 写法:$env:DRAWIO_ENV="dev")可顺带打开 DevTools——对应 src/main/electron.js#L188const __DEV__ = process.env.DRAWIO_ENV === 'dev'

一个必须知道的坑:draw.io 每个用户只允许运行单实例。src/main/electron.js#L1292 通过 app.requestSingleInstanceLock() 实现——如果官方应用已在运行,你启动自己这份代码只会往官方应用里开一个新窗口(没有你的改动)。所以先退出所有正在运行的 draw.io。

如果你的日常使用就在开发机上,可以到此为止,长期使用 npm start——打包只是要得到一个“正常安装版”应用时才需要。

5. 本地构建安装程序

5.1 任何平台都要先做:同步版本并禁用自动更新

npm install
npm run sync -- disableUpdate

sync 的底层实现是 sync.cjs:它从 drawio/VERSION 读取版本(校验必须形如 X.Y.Z),写入 package.jsonversion 字段(sync.cjs#L14-L27),并根据命令行参数重新生成 src/main/disableUpdate.js。当前仓库中该文件内容为 export function disableUpdate() { return false;},执行 sync -- disableUpdate 后会被改写为 return truesync.cjs#L28-L29)。

为什么要禁用更新?否则应用会通过 electron-updater 自动升级回官方(未修改的)版本,静默覆盖你的改动。

另注意:npm run release-* 脚本是 CI 用的,除 release-snap--publish never)外均带 --publish always,会尝试把产物发布到 GitHub Releases——本地构建请直接调用 electron-builder,输出位于 dist/ 目录。

5.2 macOS

DRAWIO_UNSIGNED=true CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder --config electron-builder-linux-mac.json --mac dmg --arm64 --publish never
  • Intel 机器改用 --x64
  • 想构建官方完整产物集(x64 + arm64 的 zip,x64 + arm64 + universal 的 dmg,耗时明显更久),把命令中的 --mac dmg --arm64 整体去掉:命令行给出的目标会替换配置文件中的目标列表,只有“裸”命令才会继承 electron-builder-linux-mac.json 的完整目标清单;
  • CSC_IDENTITY_AUTO_DISCOVERY=false 阻止 electron-builder 去 keychain 里搜索签名证书。

产物为 ad-hoc 签名:构建机上可直接打开。若拷贝到另一台 Mac(或从 GitHub Actions 下载,见第 6 节),Gatekeeper 会隔离它并常报 “damaged”——拷入 /Applications 后清除一次隔离标记:

xattr -cr /Applications/draw.io.app

Quick Look 预览扩展会被打包并 ad-hoc 签名,但 macOS 可能拒绝加载未签名应用扩展——个人构建中请视为不支持 Quick Look。

5.3 Windows

PowerShell:

$env:DRAWIO_UNSIGNED="true"
npx electron-builder --config electron-builder-win.json --publish never

cmd.exe:

set DRAWIO_UNSIGNED=true
npx electron-builder --config electron-builder-win.json --publish never

该变量只对当前终端会话有效——新开终端要重新设置(缺失时构建会以 “Trusted Signing dlib” 相关错误失败,见第 1 节的源码出处)。

产物为 NSIS 安装程序与 MSI。SmartScreen 会在运行安装程序时警告(“Windows protected your PC”)——选择 More infoRun anyway。注意 NSIS 安装程序是**整机级(per-machine)**安装的,会替换已有的官方安装。

5.4 Linux

npx electron-builder --config electron-builder-linux-mac.json --linux AppImage deb --x64 --publish never
  • Linux 二进制不签名,DRAWIO_UNSIGNED 不需要(设了也无害);
  • 按需把 deb 换成 rpm--x64 换成 --arm64;构建 rpm 目标要求系统已安装 rpm 包。

一个命名细节:官方 Linux 包(以及第 6 节的 Actions 工作流)会把产品名设为 drawio,而本地“裸”构建使用 draw.io——因此本地 deb/rpm 会与官方 drawio 包并存而非替换。想与官方命名一致,可在构建前向 electron-builder-linux-mac.json 中加入 "productName": "drawio"

6. 替代方案:在你的 Fork 上用 GitHub Actions 构建

没有对应平台的机器(或不想到处装工具链)时,可以在 Fork 上用 .github/workflows/personal-build.yml 工作流构建。它不需要任何 secrets、从不发布任何东西,且仅在你手动触发时运行。工作流文件本身印证了这一点:workflow_dispatch 触发、permissions: contents: read,并在环境变量中固化了 DRAWIO_UNSIGNED: 'true'CSC_IDENTITY_AUTO_DISCOVERY: 'false'

操作步骤:

  1. 提交并推送改动到你的 Fork——Actions 构建的是 GitHub 上的代码,不是你本地工作区:

    git add -A
    git commit -m "my change"
    git push
    
  2. 在 Fork 的 GitHub 页面打开 Actions 标签并启用工作流(GitHub 对新 Fork 默认禁用,需手动确认);

  3. 左侧选择 Personal Unsigned Build,点击 Run workflow;在弹窗中确认 Use workflow from 分支就是你推送改动的那个分支,选择目标平台(linux / macos / windows),运行;

  4. 运行结束后,从运行页面的 Artifacts 区下载安装包。

从工作流源码还可以看到几个实用细节(.github/workflows/personal-build.yml):

  • 按所选平台选择 macos-latest / windows-latest / ubuntu-latest runner,Node 固定为 24;
  • Linux 分支会安装打包工具(icnsutils、graphicsmagick、xz-utils、rpm),并用 sedproductName 置为 drawio 以对齐官方 Linux 构建——这解释了为什么 Actions 产出的 Linux 包与官方同名、而本地裸构建默认不同名;
  • 产物以 drawio-<platform>-unsigned 为前缀上传,路径覆盖 dist/ 下的 dmg、zip、exe、msi、AppImage、deb、rpm。

同样的未签名注意事项依然适用;尤其从 GitHub 下载的 macOS .dmg 一定处于隔离状态,请预留 xattr -cr 这一步。

另外:tag 触发的 release 工作流(.github/workflows/electron-builder.yml.github/workflows/electron-builder-win.yml)在 Fork 上无法工作——它们依赖私有 drawio-dev 仓库与签名 secrets,且仅在你推送 v* 标签时才会运行。因此:不要打 v* 标签(或直接从 Fork 中删掉这两个工作流)。

7. 保持 Fork 更新

先提交自己的改动,然后丢弃构建过程重写过、git 拒绝合并的文件——npm run sync 会再生成 package.json 的 version 与 src/main/disableUpdate.js

git checkout -- package.json src/main/disableUpdate.js
git remote add upstream https://github.com/jgraph/drawio-desktop.git
git fetch upstream
git merge upstream/dev
git submodule update --init

最后一步把 drawio 子模块移动到桌面应用所期望的提交点。更新完成后需要重新构建。

两个版本上的小坑:

  • 你的构建版本号来自公开子模块中的 drawio/VERSION,它可能略滞后于由内部仓库构建的官方桌面版本(CI 会先将内部产出的编辑器与 VERSION 拷入子模块目录再跑 npm run sync,见 CLAUDE.md 中关于 CI override 的说明);
  • 由于自动更新已被禁用,更新永远是:拉取、重新构建、重装。

8. 修改编辑器而非外壳

至此的全部内容都在改 Electron 外壳(src/main/)。而图表编辑器本身位于 drawio 子模块中。关键区别在于两种构建路径取子模块的方式不同:

  • 本地构建:electron-builder 打包的是你子模块工作区里的现状——所以本地构建时可以直接编辑 drawio/src/main/webapp/ 下的文件。例如 js/PreConfig.js 在编辑器加载前执行,是官方支持的配置覆写位置;
  • GitHub Actions 构建:子模块会从 jgraph/drawio 全新检出(actions/checkoutsubmodules: true),你本地的子模块编辑不生效。此时需要把 drawio 也 Fork 一份,把编辑器改动推送到那里,再把桌面 Fork 的子模块指向它——编辑 .gitmodules 并提交新的子模块引用。

9. 速查表

目标 命令 / 操作
克隆(含子模块) git clone --recursive <fork-url>;补救:git submodule update --init
免打包运行 + DevTools npm install && npm startDRAWIO_ENV=dev
同步版本 + 禁用自动更新 npm run sync -- disableUpdate
macOS 本地构建 DRAWIO_UNSIGNED=true CSC_IDENTITY_AUTO_DISCOVERY=false npx electron-builder --config electron-builder-linux-mac.json --mac dmg --arm64 --publish never
Windows 本地构建 设置 DRAWIO_UNSIGNED=truenpx electron-builder --config electron-builder-win.json --publish never
Linux 本地构建 npx electron-builder --config electron-builder-linux-mac.json --linux AppImage deb --x64 --publish never
跨 Mac 传播未签名 app xattr -cr /Applications/draw.io.app
无本地机器 Fork Actions 中手动运行 Personal Unsigned Build,下载 Artifacts
同步上游 git checkout -- package.json src/main/disableUpdate.js → fetch/merge upstream/devgit submodule update --init

核心要点回顾:DRAWIO_UNSIGNED=true 是签名/公证的显式豁免(build/sign-trusted.mjsbuild/notarize.mjs),sync -- disableUpdate 防止自定义版被官方更新覆盖(sync.cjs),--publish never 与直接调用 electron-builder 保证构建只落盘在 dist/、绝不外发。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
902
1.82 K
docsdocs
暂无描述
Markdown
888
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.51 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341