Files
Scoreboard/deploy/prepare-extra.mjs
T
AskaEth ab7662b7f1 docs: 新增《升级说明》(按版本记录改动与升级须知)+ 安装包随包带上升级说明
- docs/升级说明.md:dev_0.1.8 的升级须知——「个人+团体」项目必须补录团体名次
  (旧版没有录入位置、团体分是拿个人名次顶替算的)、单位总分为何会变化、
  奖牌三层开关、即刻重算的动作清单、5 分钟核对表
- README 文档地图与《部署指南》3.4 加入口
- deploy/prepare-extra.mjs:把升级说明一并平铺进安装目录(升级说明.txt)
- debug/使用说明/:同步全部用户文档(含刷新旧的使用说明.txt 快照)
2026-09-11 22:02:00 +08:00

156 lines
8.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 构建前准备三样东西:
// 1) 把 docs/ 下的使用说明与报名名单导入规范暂存到 console/extra/,供 electron-builder 的 extraFiles 平铺到部署包根目录;
// 2) 生成安装器自定义脚本(console/resources/installer-custom.nsh):许可页顶部文案(署名来自构建机本地文件)
// + 记住上次安装目录(重装时沿用,而不是退回系统默认路径);
// 3) 读仓库根 VERSION 文件:生成 deploy/version.gen.bat(供 build.bat 注入 Go 二进制)
// 并同步 console/package.json 的 version(决定安装包/免安装包的文件名)。
// 用 Node 而不是批处理:中文文件名/中文内容在 .bat 里会被 cmd 解析错乱。
import { copyFileSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
import { dirname, join } from 'node:path'
const root = dirname(import.meta.dirname)
// ---- 1. usage docs ----------------------------------------------------------
// 都暂存成 .txt:部署包根目录里给现场双击打开,.md 在记事本里全是符号。
// 源文件优先取 docs/ 下同名 .txt(便于临时替换),否则用仓库里的 .md。
const extraDir = join(root, 'console', 'extra')
mkdirSync(extraDir, { recursive: true })
const docsToStage = [
{ target: '使用说明.txt', sources: ['使用说明.txt', '使用手册.md'], label: 'usage doc' },
{ target: '报名名单导入规范.txt', sources: ['报名名单导入规范.md'], label: 'entry import spec' },
// 升级须知也要随包走:现场覆盖安装前能在安装目录里先看到「这一版改了什么、要补什么」。
{ target: '升级说明.txt', sources: ['升级说明.md'], label: 'upgrade notes' },
]
for (const doc of docsToStage) {
const source = doc.sources.map((name) => join(root, 'docs', name)).find((file) => existsSync(file))
if (!source) {
console.log(` [docs] no ${doc.label}, skip`)
continue
}
copyFileSync(source, join(extraDir, doc.target))
console.log(` ${doc.label} staged from ${source}`)
}
// ---- 2. installer custom script ---------------------------------------------
// electron-builder 的 nsis.include 只收一个文件,所以许可页文案与安装目录记忆写在同一个 .nsh 里。
const signatureFile = join(root, 'deploy', 'signature.local.txt')
const nshDir = join(root, 'console', 'resources')
const nshFile = join(nshDir, 'installer-custom.nsh')
const legacyNshFile = join(nshDir, 'installer-signature.nsh')
mkdirSync(nshDir, { recursive: true })
let topText = '请阅读并接受许可协议'
if (existsSync(signatureFile)) {
const signature = readFileSync(signatureFile, 'utf8').trim()
if (signature !== '') topText = `内部署名:${signature}`
}
// 换行用 CRLF:.gitattributes 里 *.nsh 就是 CRLF,NSIS 在 Windows 上按CRLF 读最稳。
const nsh = [
'!include "LogicLib.nsh"',
'',
'; 安装器自定义脚本:由 deploy/prepare-extra.mjs 生成,不要手改。',
'; 内容只有三块:许可页文案、上次安装目录记忆、卸载钩子。',
'',
'; 自己记的安装目录(per-user 落 HKCU,per-all-users 落 HKLM)。',
'; electron-builder 自带的 ${INSTALL_REGISTRY_KEY} 在卸载时会被整键删掉,',
'; 「先卸载再装新版」或换过安装模式就会退回系统默认路径,所以另存一份不会被卸载器清理的。',
'!define SCOREBOARD_INSTALL_KEY "Software\\Scoreboard Console"',
'',
'!macro customHeader',
` !define MUI_LICENSEPAGE_TEXT_TOP "${topText}"`,
'!macroend',
'',
'!macro customInit',
' ; 在 .onInit 里、initMultiUser 之后执行:此时改 $INSTDIR 还来得及影响后面的目录页。',
' ReadRegStr $R9 HKCU "${SCOREBOARD_INSTALL_KEY}" "InstallLocation"',
' ${If} $R9 != ""',
' StrCpy $INSTDIR $R9',
' ; 安装模式页(per-user / per-all-users)离开时,electron-builder 会用自己那个键',
' ; 重新算一遍 $INSTDIR;卸载后它已经被删了,把记忆值补写回同一个 hive 里。',
' ; 不跨 hive 写:否则会把 per-machine 装的机器悄悄带成默认 per-user 模式(反之亦然)。',
' WriteRegStr HKCU "${INSTALL_REGISTRY_KEY}" "InstallLocation" "$R9"',
' ${Else}',
' ReadRegStr $R9 HKLM "${SCOREBOARD_INSTALL_KEY}" "InstallLocation"',
' ${If} $R9 != ""',
' StrCpy $INSTDIR $R9',
' WriteRegStr HKLM "${INSTALL_REGISTRY_KEY}" "InstallLocation" "$R9"',
' ${Else}',
' ; 没有自己的记忆时,退回 electron-builder 的键(老版本装过、没卸载过的情况)',
' ReadRegStr $R9 HKCU "${INSTALL_REGISTRY_KEY}" "InstallLocation"',
' ${If} $R9 == ""',
' ReadRegStr $R9 HKLM "${INSTALL_REGISTRY_KEY}" "InstallLocation"',
' ${EndIf}',
' ; 只认非空值;旧目录已经被删掉也照样用(这就是「记住我的路径」的语义)。',
' ${If} $R9 != ""',
' StrCpy $INSTDIR $R9',
' ${EndIf}',
' ${EndIf}',
' ${EndIf}',
' ClearErrors',
'!macroend',
'',
'!macro customInstall',
' ; SHELL_CONTEXT 跟随安装模式:per-user → HKCU,per-all-users → HKLM。',
' WriteRegStr SHELL_CONTEXT "${SCOREBOARD_INSTALL_KEY}" "InstallLocation" "$INSTDIR"',
'!macroend',
'',
'!macro customUnInstall',
' ; 故意什么都不删:需求就是「卸载后重装仍沿用上次的目录」。',
' ; 若改成卸载即遗忘,在这里补两行(卸载时 SHELL_CONTEXT 不一定已设定,要写死根键):',
' ; DeleteRegValue HKCU "${SCOREBOARD_INSTALL_KEY}" "InstallLocation"',
' ; DeleteRegValue HKLM "${SCOREBOARD_INSTALL_KEY}" "InstallLocation"',
'!macroend',
'',
].join('\r\n')
writeFileSync(nshFile, nsh, 'utf8')
// 改名前生成的旧脚本已经没人引用,构建机上顺手清掉,免得看起来还在用。
rmSync(legacyNshFile, { force: true })
console.log(` installer custom script ready (${existsSync(signatureFile) ? 'with' : 'without'} internal signature)`)
// ---- 3. version -------------------------------------------------------------
// 版本号只维护仓库根的 VERSION 文件(唯一真相):这里读出来分两路用——
// deploy/version.gen.bat 给 build.bat,编译期用 -X main.version 写进二进制;
// console/package.json electron-builder 据此给安装包/免安装包命名。
// 二进制里的版本在编译那一刻定死,运行期不再读任何文件。
const versionFile = join(root, 'VERSION')
if (!existsSync(versionFile)) {
console.error(' [version] missing VERSION file at repo root')
process.exit(1)
}
const version = (readFileSync(versionFile, 'utf8')
.replace(/^\uFEFF/, '')
.split(/\r?\n/)
.find((line) => line.trim() !== '') ?? '').trim()
if (version === '') {
console.error(' [version] VERSION file is empty')
process.exit(1)
}
// 必须纯 ASCII:cmd.exe 会错解 .bat 里的非 ASCII 文本(见本文件头部说明)。
// 用 CRLF:与 .gitattributes 里 *.bat 的约定一致,cmd 在 Windows 上按 CRLF 读最稳。
// 只 set 不加 setlocal:build.bat 用 call 调它,变量要留在调用方的环境里。
const genBatFile = join(root, 'deploy', 'version.gen.bat')
const genBat = [
'@echo off',
'REM Generated by deploy/prepare-extra.mjs from the VERSION file - do not edit.',
`set "SCOREBOARD_VERSION=${version}"`,
'',
].join('\r\n')
writeFileSync(genBatFile, genBat, 'utf8')
console.log(` version ${version} -> deploy/version.gen.bat`)
// console/package.json 的 version 是 semver,不带 dev_ 前缀(dev_ 只用于二进制版本号)。
// 逐字替换而不是 JSON.parse/stringify:保住原有缩进与末尾换行,改动最小。
const pkgVersion = version.replace(/^dev_/, '')
const pkgFile = join(root, 'console', 'package.json')
const pkgRaw = readFileSync(pkgFile, 'utf8')
const pkgNext = pkgRaw.replace(/^(\s*"version"\s*:\s*)"[^"]*"/m, `$1"${pkgVersion}"`)
if (pkgNext === pkgRaw) {
console.log(` console/package.json version is ${pkgVersion}`)
} else {
writeFileSync(pkgFile, pkgNext, 'utf8')
console.log(` console/package.json version -> ${pkgVersion}`)
}