Docker Build and Push / build-image (push) Successful in 1m5s
- check-version: 分组校验 (前端 package.json / 外壳 tauri.conf+Cargo), 两组不再要求相等; 分支前缀校验只针对前端版本 - build-ota: build_id 改为 YYYYMMDDHHmmss (北京时间, 与前端 BUILD_ID 同格式); sequence 保持毫秒仅用于判新 - 我的页: 当前版本恢复 v0.2.0 · build <原始BUILD_ID>; 新版本附 OTA build_id; 底部替换为外壳版本 (Tauri getVersion, Web 端 —) - README: 合并遗留 readme 全部内容并按决议重写版本管理章节; 新增 CHANGELOG.md (PATCH 仅明确提出时自增)
121 lines
5.1 KiB
JavaScript
121 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
// CI 专用: 校验版本号分组一致 + 前端版本与分支名对应
|
|
//
|
|
// 背景 (2026-10-25 调整: 外壳与前端版本号解绑):
|
|
// 前端 (Web/OTA) 与外壳 (Android/Windows 壳) 独立维护、独立发版,
|
|
// 两组版本号不再要求相等, 只要求**组内**一致:
|
|
//
|
|
// 组1 前端: package.json .version
|
|
// ├─ 前端运行时 APP_VERSION = <version> (裸, 无后缀)
|
|
// └─ OTA 热更包 version = <version>-ota (打包脚本追加)
|
|
//
|
|
// 组2 外壳: src-tauri/tauri.conf.json .version (= APK versionName)
|
|
// ├─ src-tauri/Cargo.toml [package].version
|
|
// └─ src-tauri/Cargo.lock (iboard 条目)
|
|
// 外壳随 android-*/桌面发布独立演进, 与 package.json 无关。
|
|
//
|
|
// 另: package.json 的 version 不允许带 "-ota" (OTA 后缀由打包脚本追加)。
|
|
//
|
|
// 校验分两段:
|
|
// A. 组内一致性 (任何分支都查)
|
|
// B. 分支对应 (仅 dev-<版本> 分支, 针对前端组):
|
|
// dev-<major>.<minor> → version 必须以 <major>.<minor> 开头
|
|
// dev-<major> → version 必须以 <major>. 开头
|
|
// main / master / 其它 → 跳过
|
|
//
|
|
// 环境变量:
|
|
// GITHUB_REF_NAME - Gitea Actions 提供的分支名 (本地运行可用 BRANCH 覆盖)
|
|
//
|
|
// 退出码: 0 通过 / 1 不一致
|
|
import fs from 'node:fs'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const ROOT = path.resolve(__dirname, '..', '..')
|
|
|
|
const branch = process.env.GITHUB_REF_NAME || process.env.BRANCH || ''
|
|
|
|
function readJson(rel) {
|
|
return JSON.parse(fs.readFileSync(path.join(ROOT, rel), 'utf8'))
|
|
}
|
|
|
|
// Cargo.toml 的 [package].version 位于文件开头, 取第一个行首 version = "x"
|
|
function readCargoTomlVersion() {
|
|
const text = fs.readFileSync(path.join(ROOT, 'src-tauri/Cargo.toml'), 'utf8')
|
|
const m = /^version\s*=\s*"([^"]+)"/m.exec(text)
|
|
return m ? m[1] : null
|
|
}
|
|
|
|
// Cargo.lock 里挑出 name = "iboard" 那个包的 version
|
|
function readCargoLockVersion() {
|
|
const text = fs.readFileSync(path.join(ROOT, 'src-tauri/Cargo.lock'), 'utf8')
|
|
const blocks = text.split(/^\[\[package\]\]$/m)
|
|
for (const b of blocks) {
|
|
if (/^name\s*=\s*"iboard"$/m.test(b)) {
|
|
const m = /^version\s*=\s*"([^"]+)"/m.exec(b)
|
|
if (m) return m[1]
|
|
}
|
|
}
|
|
return null
|
|
}
|
|
|
|
// 组1 前端 (唯一手动管理点 = package.json)
|
|
const webVersion = String(readJson('package.json').version || '')
|
|
const webSources = [{ file: 'package.json', version: webVersion }]
|
|
const otaVersion = webVersion + '-ota'
|
|
|
|
// 组2 外壳 (独立维护; 三处必须组内一致)
|
|
const shellSources = [
|
|
{ file: 'src-tauri/tauri.conf.json', version: String(readJson('src-tauri/tauri.conf.json').version || '') },
|
|
{ file: 'src-tauri/Cargo.toml', version: readCargoTomlVersion() },
|
|
{ file: 'src-tauri/Cargo.lock (iboard)', version: readCargoLockVersion() },
|
|
]
|
|
|
|
console.log('[check-version] 分支: ', branch || '(未知)')
|
|
for (const s of webSources) console.log(`[check-version] [前端] ${s.file.padEnd(28)} ${s.version || '(缺失)'}`)
|
|
for (const s of shellSources) console.log(`[check-version] [外壳] ${s.file.padEnd(28)} ${s.version || '(缺失)'}`)
|
|
console.log('[check-version] 产物版本: OTA =', otaVersion, '/ 外壳(APK versionName) =', shellSources[0].version)
|
|
|
|
let failed = false
|
|
|
|
// ---- A1. 前端组: 裸版本号 ----
|
|
if (/-ota$/i.test(webVersion)) {
|
|
console.error('')
|
|
console.error(`[check-version] package.json 的 version 不能带 "-ota": ${webVersion}`)
|
|
console.error('[check-version] OTA 后缀由 scripts/build-ota.mjs 自动追加, 请只写裸版本号 (如 0.2.0)')
|
|
failed = true
|
|
}
|
|
|
|
// ---- A2. 外壳组内一致 ----
|
|
const shellBroken = shellSources.filter((s) => s.version !== shellSources[0].version || !s.version)
|
|
if (shellBroken.length) {
|
|
console.error('')
|
|
console.error('============================================================')
|
|
console.error('[check-version] 外壳版本号不一致 (tauri.conf.json ↔ Cargo.toml ↔ Cargo.lock), 构建中止')
|
|
console.error('============================================================')
|
|
for (const s of shellBroken) {
|
|
console.error(` ${s.file}: ${s.version || '(缺失)'} ≠ tauri.conf.json: ${shellSources[0].version}`)
|
|
}
|
|
failed = true
|
|
}
|
|
|
|
// ---- B. 分支对应 (仅前端组; 外壳在 android-* 分支独立演进, 不参与) ----
|
|
const m = /^dev-(\d+)(?:\.(\d+))?$/.exec(branch)
|
|
if (!m) {
|
|
console.log('[check-version] 非 dev-<版本> 分支, 跳过分支对应校验')
|
|
} else {
|
|
const want = m[2] === undefined ? `${m[1]}.` : `${m[1]}.${m[2]}`
|
|
if (!webVersion.startsWith(want)) {
|
|
console.error('')
|
|
console.error(`[check-version] 版本与分支不一致: 分支 "${branch}" 要求前端版本以 ${want} 开头, 实际是 ${webVersion}`)
|
|
console.error('[check-version] 请修改 package.json 的 version')
|
|
failed = true
|
|
} else {
|
|
console.log(`[check-version] 分支一致 (${branch} ↔ ${webVersion})`)
|
|
}
|
|
}
|
|
|
|
if (failed) process.exit(1)
|
|
console.log('[check-version] 全部通过')
|