Skip to content

Commit a90303f

Browse files
committed
fix: the problem when developing on arm64 and container environments, update Electron version to 29.4.0 and add network interfaces fallback
- Updated Electron dependency from 19.1.9 to 29.4.0 in package.json and pnpm-lock.yaml. - Added a new utility for handling network interfaces fallback in os-network-fallback.cjs and os-network-fallback.js. - Updated README.md with instructions for Linux arm64 environments and additional notes on phantomjs configuration. - Modified .npmrc to include a comment regarding phantomjs_skip_download. - Refactored background.js to import the new network fallback utility.
1 parent 38ff846 commit a90303f

File tree

8 files changed

+219
-118
lines changed

8 files changed

+219
-118
lines changed

.npmrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
# Enable it if phantomjs-prebuilt postinstall fails on some environments.
2+
# phantomjs_skip_download=true
13
shamefully-hoist=true

README.md

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,10 @@
136136

137137
### 2.2、开启前 vs 开启后
138138

139-
| | 开启前 | 开启后 |
140-
| -------- | ------------------------------ | ------------------------------------------------- |
141-
| 头像 | ![](./doc/avatar2.png) | ![](./doc/avatar1.png) |
142-
| clone | ![](./doc/clone-before.png) | ![](./doc/clone.png) |
139+
| | 开启前 | 开启后 |
140+
|----------|--------------------------------|--------------------------------------------------|
141+
| 头像 | ![](./doc/avatar2.png) | ![](./doc/avatar1.png) |
142+
| clone | ![](./doc/clone-before.png) | ![](./doc/clone.png) |
143143
| zip 下载 | ![](./doc/download-before.png) | ![](./doc/download.png)秒下的,实在截不到速度的图 |
144144

145145
## 三、模式说明
@@ -386,6 +386,70 @@ npm install -g pnpm --registry=https://registry.npmmirror.com
386386

387387
```
388388

389+
#### 3)Linux arm64 环境(Electron 安装提示)
390+
391+
如果你在 Linux arm64 上为 GUI 安装依赖遇到 Electron `postinstall` 崩溃(`double free or corruption`)或无法下载二进制,可按下面流程处理:
392+
393+
常见错误提示:
394+
- `phantomjs-prebuilt`: Unexpected platform or architecture: linux/arm64,postinstall 失败。
395+
- `electron` postinstall: double free or corruption (out) / Aborted (core dumped)。
396+
397+
1. 使用与 Electron 安装脚本兼容的 Node 版本(建议 LTS 18):
398+
399+
```shell
400+
nvm install 18.20.8
401+
nvm use 18.20.8
402+
corepack enable
403+
```
404+
405+
2. 正常安装 GUI 依赖(脚本需要开启):
406+
407+
```shell
408+
pnpm install --filter @docmirror/dev-sidecar-gui
409+
```
410+
411+
3. 若依旧在下载阶段崩溃,可手动下载并解压 Electron 二进制:
412+
413+
```shell
414+
electron_dir="$(pwd)/node_modules/.pnpm/[email protected]/node_modules/electron"
415+
rm -rf "$electron_dir/dist"
416+
curl -L --retry 3 --retry-delay 2 -o /tmp/electron-v29.4.6-linux-arm64.zip \
417+
https://github.com/electron/electron/releases/download/v29.4.6/electron-v29.4.6-linux-arm64.zip
418+
unzip -q /tmp/electron-v29.4.6-linux-arm64.zip -d "$electron_dir/dist"
419+
if [ -f "$electron_dir/dist/electron.d.ts" ]; then mv "$electron_dir/dist/electron.d.ts" "$electron_dir/electron.d.ts"; fi
420+
printf 'electron' > "$electron_dir/path.txt"
421+
```
422+
423+
4. 首次运行若提示 `chrome-sandbox` 权限问题,可在本地开发时关闭沙箱,或设置 SUID 权限:
424+
425+
```shell
426+
# 开发/调试禁用沙箱(无需 root)
427+
ELECTRON_NO_SANDBOX=1 pnpm -F @docmirror/dev-sidecar-gui run electron:serve
428+
429+
# 或为 chrome-sandbox 设置 SUID(需要 root)
430+
sudo chown root:root "$electron_dir/dist/chrome-sandbox"
431+
sudo chmod 4755 "$electron_dir/dist/chrome-sandbox"
432+
```
433+
434+
> `.npmrc` 建议
435+
> - `phantomjs_skip_download=true`:避免旧版 `phantomjs-prebuilt` 在 arm64/容器环境拉取二进制导致安装失败,建议保留。
436+
437+
#### 4)Termux/Android ARM64 运行 GUI(无 root)
438+
439+
- 预加载网络接口兜底、强制关闭 Electron 沙箱(无需 root,使用 HOME 路径):
440+
441+
```bash
442+
cd $HOME/dev-sidecar/packages/gui
443+
NODE_OPTIONS="--require ${HOME:-/data/data/com.termux/files/home}/dev-sidecar/packages/gui/src/utils/os-network-fallback.cjs" \
444+
ELECTRON_FORCE_NO_SANDBOX=1 ELECTRON_NO_SANDBOX=1 ELECTRON_DISABLE_SANDBOX=1 \
445+
npm run electron
446+
```
447+
448+
- 说明:
449+
- Termux 缺少 setuid 能力,必须关闭沙箱,否则会提示 chrome-sandbox 权限错误。
450+
- `os-network-fallback.cjs` 会在 `uv_interface_addresses` 权限受限时返回回环接口,避免 node-ipc 崩溃。
451+
- 若仍需沙箱,只能在支持 setuid 的环境或 root 下运行。
452+
389453
### 8.2、开发调试模式启动
390454

391455
运行如下命令即可开发模式启动

packages/gui/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"@vue/babel-preset-jsx": "^1.4.0",
4646
"@vue/cli-plugin-babel": "^5.0.8",
4747
"@vue/cli-service": "^5.0.8",
48-
"electron": "^19.1.9",
48+
"electron": "^29.4.0",
4949
"electron-builder": "^25.1.8",
5050
"electron-icon-builder": "^2.0.1",
5151
"json5-loader": "^4.0.1",
@@ -56,4 +56,4 @@
5656
"last 2 versions",
5757
"not dead"
5858
]
59-
}
59+
}

packages/gui/src/background.js

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict'
22
/* global __static */
3+
import './utils/os-network-fallback'
34
import path from 'node:path'
45
import DevSidecar from '@docmirror/dev-sidecar'
56
import { app, BrowserWindow, dialog, globalShortcut, ipcMain, Menu, nativeImage, nativeTheme, powerMonitor, protocol, Tray } from 'electron'
@@ -47,7 +48,7 @@ protocol.registerSchemesAsPrivileged([
4748
{ scheme: 'app', privileges: { secure: true, standard: true } },
4849
])
4950

50-
function openDevTools () {
51+
function openDevTools() {
5152
try {
5253
log.debug('尝试打开 `开发者工具`')
5354
win.webContents.openDevTools()
@@ -57,7 +58,7 @@ function openDevTools () {
5758
}
5859
}
5960

60-
function closeDevTools () {
61+
function closeDevTools() {
6162
try {
6263
log.debug('尝试关闭 `开发者工具`')
6364
win.webContents.closeDevTools()
@@ -67,7 +68,7 @@ function closeDevTools () {
6768
}
6869
}
6970

70-
function switchDevTools () {
71+
function switchDevTools() {
7172
if (!win || !win.webContents) {
7273
return
7374
}
@@ -79,7 +80,7 @@ function switchDevTools () {
7980
}
8081

8182
// 隐藏主窗口,并创建托盘,绑定关闭事件
82-
function setTray () {
83+
function setTray() {
8384
// const topMenu = Menu.buildFromTemplate({})
8485
// Menu.setApplicationMenu(topMenu)
8586
// 用一个 Tray 来表示一个图标,这个图标处于正在运行的系统的通知区
@@ -148,7 +149,7 @@ function setTray () {
148149
return appTray
149150
}
150151

151-
function checkHideWin () {
152+
function checkHideWin() {
152153
const config = DevSidecar.api.config.get()
153154

154155
// 配置为false时,不需要校验
@@ -170,7 +171,7 @@ function checkHideWin () {
170171
return true
171172
}
172173

173-
function hideWin (reason = '', needCheck = false) {
174+
function hideWin(reason = '', needCheck = false) {
174175
if (win) {
175176
if (needCheck && !checkHideWin()) {
176177
return
@@ -186,7 +187,7 @@ function hideWin (reason = '', needCheck = false) {
186187
}
187188
}
188189

189-
function showWin () {
190+
function showWin() {
190191
if (win) {
191192
win.show()
192193
} else {
@@ -198,13 +199,13 @@ function showWin () {
198199
winIsHidden = false
199200
}
200201

201-
function changeAppConfig (config) {
202+
function changeAppConfig(config) {
202203
if (config.hideDockWhenWinClose != null) {
203204
hideDockWhenWinClose = config.hideDockWhenWinClose
204205
}
205206
}
206207

207-
function createWindow (startHideWindow, autoQuitIfError = true) {
208+
function createWindow(startHideWindow, autoQuitIfError = true) {
208209
// Create the browser window.
209210
const windowSize = DevSidecar.api.config.get().app.windowSize || {}
210211

@@ -361,11 +362,11 @@ function createWindow (startHideWindow, autoQuitIfError = true) {
361362
return true
362363
}
363364

364-
async function beforeQuit () {
365+
async function beforeQuit() {
365366
log.info('before quit')
366367
return DevSidecar.api.shutdown()
367368
}
368-
async function quit (reason) {
369+
async function quit(reason) {
369370
log.info('app quit:', reason)
370371

371372
if (tray) {
@@ -376,11 +377,11 @@ async function quit (reason) {
376377
app.quit()
377378
}
378379

379-
function hasShortcut (showHideShortcut) {
380+
function hasShortcut(showHideShortcut) {
380381
return showHideShortcut && showHideShortcut.length > 1
381382
}
382383

383-
function registerShowHideShortcut (showHideShortcut) {
384+
function registerShowHideShortcut(showHideShortcut) {
384385
globalShortcut.unregisterAll()
385386
if (hasShortcut(showHideShortcut)) {
386387
try {
@@ -407,7 +408,7 @@ function registerShowHideShortcut (showHideShortcut) {
407408
}
408409
}
409410

410-
function initApp () {
411+
function initApp() {
411412
if (isMac) {
412413
app.whenReady().then(() => {
413414
app.dock.setIcon(path.join(__dirname, '../extra/icons/512x512-2.png'))
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict'
2+
3+
const os = require('node:os')
4+
5+
function installNetworkInterfacesFallback(logger = console) {
6+
const logWarn = (logger && logger.warn) ? logger.warn.bind(logger) : console.warn
7+
8+
// Avoid double-patching
9+
if (os.networkInterfaces && os.networkInterfaces.__dsPatched) {
10+
return os.networkInterfaces
11+
}
12+
13+
const originalNetworkInterfaces = os.networkInterfaces
14+
15+
const safeNetworkInterfaces = function networkInterfacesSafe() {
16+
try {
17+
return originalNetworkInterfaces.call(os)
18+
} catch (err) {
19+
logWarn('os.networkInterfaces failed, using fallback loopback stub:', err)
20+
return {
21+
lo: [
22+
{
23+
address: '127.0.0.1',
24+
netmask: '255.0.0.0',
25+
family: 'IPv4',
26+
mac: '00:00:00:00:00:00',
27+
internal: true,
28+
cidr: '127.0.0.1/8',
29+
},
30+
],
31+
}
32+
}
33+
}
34+
35+
safeNetworkInterfaces.__dsPatched = true
36+
os.networkInterfaces = safeNetworkInterfaces
37+
return safeNetworkInterfaces
38+
}
39+
40+
// Install immediately when required (for NODE_OPTIONS preload)
41+
installNetworkInterfacesFallback()
42+
43+
module.exports = { installNetworkInterfacesFallback }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict'
2+
3+
import { createRequire } from 'node:module'
4+
import log from './util.log.gui'
5+
6+
const require = createRequire(import.meta.url)
7+
const { installNetworkInterfacesFallback } = require('./os-network-fallback.cjs')
8+
9+
installNetworkInterfacesFallback(log)

packages/mitmproxy/src/index.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const { fireError, fireStatus } = require('./utils/util.process')
88
let servers = []
99

1010
const api = {
11-
async start (config) {
11+
async start(config) {
1212
const proxyOptions = ProxyOptions(config)
1313
const setting = config.setting
1414
if (setting) {
@@ -47,7 +47,7 @@ const api = {
4747

4848
registerProcessListener()
4949
},
50-
async close () {
50+
async close() {
5151
return new Promise((resolve, reject) => {
5252
if (servers && servers.length > 0) {
5353
for (const server of servers) {
@@ -77,7 +77,7 @@ const api = {
7777
},
7878
}
7979

80-
function registerProcessListener () {
80+
function registerProcessListener() {
8181
process.on('message', (msg) => {
8282
log.info('child get msg:', JSON.stringify(msg))
8383
if (msg.type === 'action') {
@@ -111,9 +111,10 @@ function registerProcessListener () {
111111
process.on('exit', (code, signal) => {
112112
log.info('代理服务进程被关闭:', code, signal)
113113
})
114-
process.on('beforeExit', (code, signal) => {
115-
log.info('Process beforeExit event with code: ', code, signal)
116-
})
114+
// Aviod keeping print log before exit
115+
// process.on('beforeExit', (code, signal) => {
116+
// log.info('Process beforeExit event with code: ', code, signal)
117+
// })
117118
process.on('SIGPIPE', (code, signal) => {
118119
log.warn('sub Process SIGPIPE', code, signal)
119120
})

0 commit comments

Comments
 (0)