Files
dukang/scripts/set-version.mjs
T
2026-09-10 09:56:50 +08:00

184 lines
5.5 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.
/**
* 统一改产品版本号:各端 package.json + 小程序兜底 + API health + HQ 最低版本占位。
*
* 用法:
* node scripts/set-version.mjs 4.0.19
* node scripts/set-version.mjs v4.0.19
* node scripts/set-version.mjs 4.0.19 --dry-run
* pnpm set-version -- 4.0.19
*
* 不改内部库(packages/domain、shared-types、weixin-sdk、client-logging 仍为 0.1.0)。
* 加 --include-libs 才会一并改这些库。
*/
import { readFileSync, writeFileSync, readdirSync, statSync } from 'node:fs';
import { dirname, join, relative } from 'node:path';
import { fileURLToPath } from 'node:url';
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
const LIB_PACKAGES = new Set([
'packages/domain/package.json',
'packages/shared-types/package.json',
'packages/weixin-sdk/package.json',
'packages/client-logging/package.json',
]);
const SOURCE_TARGETS = [
{
file: 'apps/mini-user/src/lib/client-version.ts',
label: '小程序版本兜底',
pattern: /(\|\| ')(\d+\.\d+\.\d+)(')/,
},
{
file: 'server/dukang-api/src/modules/health/health.controller.ts',
label: 'API /health version',
pattern: /(version:\s*')(\d+\.\d+\.\d+)(')/,
},
{
file: 'server/dukang-api/src/common/system-config/system-config.registry.ts',
label: 'HQ 小程序最低版本占位',
pattern: /(placeholder:\s*')(\d+\.\d+\.\d+)(')/,
},
];
function fail(message) {
console.error(message);
process.exit(1);
}
function parseArgs(argv) {
const flags = new Set();
const rest = [];
for (const a of argv) {
if (a === '--dry-run' || a === '-n') flags.add('dryRun');
else if (a === '--include-libs') flags.add('includeLibs');
else if (a === '--help' || a === '-h') flags.add('help');
else if (a.startsWith('-')) {
fail(`未知参数:${a}\n用法:node scripts/set-version.mjs <version> [--dry-run] [--include-libs]`);
} else rest.push(a);
}
return { flags, versionRaw: rest[0] };
}
function normalizeVersion(raw) {
const v = String(raw || '').trim().replace(/^v/i, '');
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(v)) {
fail(`版本号须为 semver,例如 4.0.19 或 v4.0.19,收到:${raw || '(空)'}`);
}
return v;
}
function walkPackageJson(dir, out = []) {
for (const name of readdirSync(dir)) {
if (name === 'node_modules' || name === 'dist' || name === '.git') continue;
const full = join(dir, name);
let st;
try {
st = statSync(full);
} catch {
continue;
}
if (st.isDirectory()) walkPackageJson(full, out);
else if (name === 'package.json') out.push(full);
}
return out;
}
function rel(file) {
return relative(root, file).replaceAll('\\', '/');
}
function printHelp() {
console.log(`统一改产品版本号
用法:
node scripts/set-version.mjs <version> [--dry-run] [--include-libs]
pnpm set-version -- <version>
示例:
node scripts/set-version.mjs 4.0.19
node scripts/set-version.mjs v4.0.19 --dry-run
会改:
- 各端 / API / shared-ui 的 package.json version
- 小程序 client-version.ts 兜底
- API GET /health 的 version
- HQ「小程序最低版本」placeholder
默认不改内部库 0.1.0domain / shared-types / weixin-sdk / client-logging)。`);
}
function main() {
const { flags, versionRaw } = parseArgs(process.argv.slice(2));
if (flags.has('help') || !versionRaw) {
printHelp();
process.exit(versionRaw ? 0 : 1);
}
const version = normalizeVersion(versionRaw);
const dryRun = flags.has('dryRun');
const includeLibs = flags.has('includeLibs');
const changes = [];
for (const file of walkPackageJson(root)) {
const key = rel(file);
if (key === 'package.json') continue;
if (!includeLibs && LIB_PACKAGES.has(key)) continue;
const raw = readFileSync(file, 'utf8');
let pkg;
try {
pkg = JSON.parse(raw);
} catch {
fail(`无法解析 ${key}`);
}
if (typeof pkg.version !== 'string') continue;
if (pkg.version === version) {
changes.push({ file: key, from: pkg.version, to: version, changed: false });
continue;
}
const next = { ...pkg, version };
const text = `${JSON.stringify(next, null, 2)}\n`;
if (!dryRun) writeFileSync(file, text);
changes.push({ file: key, from: pkg.version, to: version, changed: true });
}
for (const target of SOURCE_TARGETS) {
const file = join(root, target.file);
const raw = readFileSync(file, 'utf8');
const match = raw.match(target.pattern);
if (!match) {
changes.push({
file: target.file,
from: '(未匹配到 semver)',
to: version,
changed: false,
note: target.label,
});
continue;
}
const from = match[2];
if (from === version) {
changes.push({ file: target.file, from, to: version, changed: false, note: target.label });
continue;
}
const next = raw.replace(target.pattern, `$1${version}$3`);
if (!dryRun) writeFileSync(file, next);
changes.push({ file: target.file, from, to: version, changed: true, note: target.label });
}
const updated = changes.filter((c) => c.changed);
const skipped = changes.filter((c) => !c.changed);
console.log(`${dryRun ? '[dry-run] ' : ''}目标版本 ${version}`);
for (const c of updated) {
console.log(` 改 ${c.file}${c.note ? ` (${c.note})` : ''} ${c.from}${c.to}`);
}
for (const c of skipped) {
console.log(` 跳过 ${c.file}${c.note ? ` (${c.note})` : ''} 已是 ${c.from}`);
}
console.log(updated.length ? `共 ${updated.length} 处将写入。` : '没有需要修改的文件。');
}
main();