需求:让积分榜能在飞腾 D2000(ARM64)的 Linux 上跑起来,**同时不破坏 Windows**。
## 一、Go 服务端:交叉编译即可(**已实测**)
- SQLite 用的是 `modernc.org/sqlite`(纯 Go 驱动),全项目**没有一处 `import "C"`** → `CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build` 直接出;
- 产物是**静态链接**(`ELF 64-bit LSB executable, ARM aarch64, statically linked`)→ **不依赖 glibc 版本**,对国产系统友好;
- 本机(arm64)**实际启动验证**:数据目录、局域网地址探测、数据库迁移(0001/0002/0003)全部正常;
- 产物:`debug/服务端-arm64/scoreboard`(29 MB,`-s -w`)。
## 二、Console(Electron 宿主):平台差异收口到一个新文件
- **新增 `console/main/platform.js`**:平台差异集中在这 45 行里,其余代码不写平台判断。
⚠️ **`platform` 参数缺省按 Windows 解析** —— 漏传参数走的就是改动前那条老路(这比"改了再小心"更保守:**默认行为不变,新行为必须显式传参**)。
- `userpaths.js` / `datadir.js`:Windows 分支 `%LOCALAPPDATA%\ScoreboardConsole\` **逐字保留**;Linux 分支按 XDG(`$XDG_DATA_HOME`,缺省 `~/.local/share`)。两平台各自的"拿不到用户目录"提示文案分开写(免得现场去设一个本平台不存在的环境变量)。
- `config.js`:服务端文件名按平台取(Windows `scoreboard.exe` / Linux `scoreboard`)。
- **新增 `console/main/autostart.js`**:Linux 用 `~/.config/autostart/*.desktop`(开=写文件、关=删文件;Linux 没有"已注册但未启用"这个中间态)。
- 托盘与窗口行为:Linux 的通知区多数不派发 double-click,因此按平台分支成单击。
## 三、怎么保证没弄坏 Windows
1. **设计上**:平台差异集中 + 缺省值就是 Windows(见上);
2. **逐行核对 diff**:Windows 分支那几行原样未动;
3. **测试**:Console 由 **68 → 96 例**,新增全是平台分支用例,**既有断言一条没改**;
4. **真机验证**:构建机重打 Windows 包并跑(这一道才算收口)。
## 四、自检
全量自检两轮**全绿、`EXIT=0`**:服务端 14 包 / 前端 **944 例** / Console **96 例**。
## 版本
`dev_0.5.9` → **`dev_0.5.10`**;安装器素材同步重生成(照例只动 `brand.bmp` 两张)。
108 lines
4.9 KiB
JavaScript
108 lines
4.9 KiB
JavaScript
'use strict'
|
||
|
||
const assert = require('node:assert/strict')
|
||
const fs = require('node:fs')
|
||
const os = require('node:os')
|
||
const path = require('node:path')
|
||
const test = require('node:test')
|
||
|
||
const { AUTOSTART_FILE, resolveAutostartFile, isAutostartEnabled, setAutostart } = require('./autostart')
|
||
|
||
// 用绝对路径字符串拼测试目录,避免依赖运行平台的盘符写法
|
||
const HOME = path.resolve('/home/operator')
|
||
|
||
function makeRoot(t) {
|
||
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'scoreboard-autostart-'))
|
||
t.after(() => fs.rmSync(root, { recursive: true, force: true }))
|
||
return root
|
||
}
|
||
|
||
test('自启文件在 $XDG_CONFIG_HOME/autostart 下,缺省用 ~/.config/autostart', () => {
|
||
const xdg = path.resolve('/xdg/config')
|
||
assert.equal(
|
||
resolveAutostartFile({ platform: 'linux', env: { XDG_CONFIG_HOME: xdg } }),
|
||
path.join(xdg, 'autostart', AUTOSTART_FILE)
|
||
)
|
||
assert.equal(
|
||
resolveAutostartFile({ platform: 'linux', env: { HOME } }),
|
||
path.join(HOME, '.config', 'autostart', AUTOSTART_FILE)
|
||
)
|
||
})
|
||
|
||
test('非 Linux 平台没有这个文件(Windows/macOS 走系统登录项)', () => {
|
||
assert.equal(resolveAutostartFile({ platform: 'win32', env: { HOME } }), null)
|
||
assert.equal(resolveAutostartFile({ platform: 'darwin', env: { HOME } }), null)
|
||
assert.throws(
|
||
() => setAutostart({ platform: 'win32', env: { HOME }, exePath: '/x/Scoreboard_Console', enabled: true }),
|
||
/Linux/
|
||
)
|
||
})
|
||
|
||
test('开:写出 .desktop,Exec 指向 Console 自己;关:把文件删掉', (t) => {
|
||
const root = makeRoot(t)
|
||
const env = { XDG_CONFIG_HOME: root }
|
||
const exePath = path.join(root, 'install', 'Scoreboard_Console')
|
||
const file = resolveAutostartFile({ platform: 'linux', env })
|
||
|
||
assert.equal(isAutostartEnabled({ platform: 'linux', env }), false)
|
||
assert.equal(fs.existsSync(file), false)
|
||
|
||
setAutostart({ platform: 'linux', env, exePath, enabled: true })
|
||
assert.equal(isAutostartEnabled({ platform: 'linux', env }), true)
|
||
const text = fs.readFileSync(file, 'utf8')
|
||
assert.match(text, /^\[Desktop Entry\]$/m)
|
||
assert.match(text, /^Type=Application$/m)
|
||
assert.match(text, /^Exec=.*Scoreboard_Console.*$/m)
|
||
assert.match(text, /^X-GNOME-Autostart-enabled=true$/m)
|
||
|
||
// 重复开一次是幂等的(每次启动 Console 都会按配置重写一遍)
|
||
setAutostart({ platform: 'linux', env, exePath, enabled: true })
|
||
assert.equal(fs.readFileSync(file, 'utf8'), text)
|
||
assert.equal(isAutostartEnabled({ platform: 'linux', env }), true)
|
||
|
||
setAutostart({ platform: 'linux', env, exePath, enabled: false })
|
||
assert.equal(fs.existsSync(file), false)
|
||
assert.equal(isAutostartEnabled({ platform: 'linux', env }), false)
|
||
// 关两次不报错:现场连点开关、或文件本来就不在(目录也一并没了)
|
||
setAutostart({ platform: 'linux', env, exePath, enabled: false })
|
||
})
|
||
|
||
test('安装路径带空格时 Exec 加引号,否则自启会被拆成两个参数', (t) => {
|
||
const root = makeRoot(t)
|
||
const env = { XDG_CONFIG_HOME: root }
|
||
const exePath = path.join(root, 'Scoreboard Console', 'Scoreboard_Console')
|
||
|
||
setAutostart({ platform: 'linux', env, exePath, enabled: true })
|
||
const text = fs.readFileSync(resolveAutostartFile({ platform: 'linux', env }), 'utf8')
|
||
assert.match(text, new RegExp(`^Exec="${exePath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}"$`, 'm'))
|
||
})
|
||
|
||
test('文件被标了 Hidden=true(别的桌面环境关闭自启时的写法)视为关闭', (t) => {
|
||
const root = makeRoot(t)
|
||
const env = { XDG_CONFIG_HOME: root }
|
||
const file = resolveAutostartFile({ platform: 'linux', env })
|
||
|
||
setAutostart({ platform: 'linux', env, exePath: '/x/Scoreboard_Console', enabled: true })
|
||
assert.equal(isAutostartEnabled({ platform: 'linux', env }), true)
|
||
|
||
// 现场从桌面环境的「启动应用程序」里关掉自启时,各家的写法不一致:
|
||
// XDG 规范里 Hidden=true 就是「忽略这个文件」,必须认出来
|
||
fs.writeFileSync(file, fs.readFileSync(file, 'utf8').replace('Hidden=false', 'Hidden=true'), 'utf8')
|
||
assert.equal(isAutostartEnabled({ platform: 'linux', env }), false)
|
||
})
|
||
|
||
test('拿不到 $XDG_CONFIG_HOME 也拿不到 $HOME 时开关都报错,不静默当成功', () => {
|
||
const env = {}
|
||
assert.equal(resolveAutostartFile({ platform: 'linux', env }), null)
|
||
assert.equal(isAutostartEnabled({ platform: 'linux', env }), false)
|
||
assert.throws(() => setAutostart({ platform: 'linux', env, exePath: '/x/Scoreboard_Console', enabled: true }), /XDG_CONFIG_HOME/)
|
||
assert.throws(() => setAutostart({ platform: 'linux', env, exePath: '/x/Scoreboard_Console', enabled: false }), /XDG_CONFIG_HOME/)
|
||
})
|
||
|
||
test('拿不到 Console 自身路径时不写一个空 Exec 出去', (t) => {
|
||
const root = makeRoot(t)
|
||
const env = { XDG_CONFIG_HOME: root }
|
||
assert.throws(() => setAutostart({ platform: 'linux', env, exePath: '', enabled: true }), /路径/)
|
||
assert.equal(isAutostartEnabled({ platform: 'linux', env }), false)
|
||
})
|