mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-28 07:14:40 +08:00
* chore: notice i18n * feat: add script to clean up unused i18n keys * chore: cleanup i18n keys * refactor(i18n/proxies): migrate proxies UI to structured locale keys * chore: i18n for rule module * chore: i18n for profile module * chore: i18n for connections module * chore: i18n for settings module * chore: i18n for verge settings * chore: i18n for theme settings * chore: i18n for theme * chore(i18n): components.home.* * chore(i18n): remove unused i18n keys * chore(i18n): components.profile.* * chore(i18n): components.connection * chore(i18n): pages.logs.* * chore(i18n): pages.*.provider * chore(i18n): components.settings.externalCors.* * chore(i18n): components.settings.clash.* * chore(i18n): components.settings.liteMode.* * chore(i18n): components.settings.backup.* * chore(i18n): components.settings.clash.port.* * chore(i18n): components.settings.misc.* * chore(i18n): components.settings.update.* * chore(i18n): components.settings.sysproxy.* * chore(i18n): components.settings.sysproxy.* * chore(i18n): pages.profiles.notices/components.providers.notices * refactor(notice): unify showNotice usage * refactor(notice): add typed showNotice shortcuts, centralize defaults, and simplify subscriptions * refactor: unify showNotice usage * refactor(notice): unify showNotice API * refactor(notice): unify showNotice usage * chore(i18n): components.test.* * chore(i18n): components.settings.dns.* * chore(i18n): components.home.clashInfo.* * chore(i18n): components.home.systemInfo.* * chore(i18n): components.home.ipInfo/traffic.* * chore(i18n): navigation.* * refactor(i18n): remove pages.* namespace and migrate route texts under module-level page keys * chore(i18n): common.* * chore(i18n): common.* * fix: change error handling in patch_profiles_config to return false when a switch is in progress * fix: improve error handling in patch_profiles_config to prevent requests during profile switching * fix: change error handling in patch_profiles_config to return false when a switch is in progress fix: ensure CURRENT_SWITCHING_PROFILE is reset after config updates in perform_config_update and patch_profiles_config * chore(i18n): restructure root-level locale keys into namespaces * chore(i18n): add missing i18n keys * docs: i18n guide * chore: adjust i18n * refactor(i18n): align UI actions and status labels with common keys * refactor(i18n): unify two-name locale namespaces * refactor(i18n/components): unify locale keys and update component references * chore(i18n): add shared and entities namespaces to all locale files * refactor(i18n): consolidate shared and entity namespaces across features * chore(deps): update npm dependencies to ^7.3.5 (#5310) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * refactor(i18n): migrate shared editor modes and consolidate entities namespaces * tmp * refactor(i18n): flatten locales and move theme/validation strings * docs: CONTRIBUTING_i18n.md * refactor(i18n): restructure feedback and profile namespaces for better organization * refactor(i18n): unify settings locale structure and update references * refactor(i18n): reorganize locale keys for home, proxies, rules, connections, logs, unlock, and tests * refactor(i18n/feedback/layout): unify shared toasts & normalize layout namespace * refactor(i18n): centralize common UI strings in shared * refactor(i18n): flatten headers and unify locale schema * refactor(i18n): consolidate duplicate per-feature translations into shared namespace * refactor(i18n): split locales into per-namespace files * style: lint * refactor(i18n): unify unlock UI translations under tests namespace * feat(i18n): add type-checked translation keys * style: eslint import order * feat(i18n): replace ad-hoc loader with rust-i18n backend bundles * chore(prebuild): remove locale-copy step * fix(i18n, notice): propagate runtime params and update cleanup script path * fix(i18n,notice): make locale formatting idempotent and guard early notice translations * fix(i18n): resolve locale aliases and match OS codes correctly * fix(unlock): use i18next-compatible double-brace interpolation in failure notice * fix(i18n): route unlock error notices through translation keys * fix(i18n): i18n types * feat(i18n): localize upgrade notice for Clash core viewer * fix(notice): ensure runtime overrides apply to prefix translations * chore(i18n): replace literal notices with translation keys * chore(i18n): types * chore(i18n): regen typings before formatting to keep keys in sync * chore(i18n): simply labels * chore(i18n): adjust translation * chore: remove eslint-plugin-i18next * chore(i18n): add/refine Korean translations across frontend scopes and Rust backend (#5341) * chore(i18n): translate settings.json (missed in previous pass) (#5343) * chore(i18n): add/refine Korean translations across frontend scopes and Rust backend * chore(i18n): add/refine Korean translations across frontend scopes and Rust backend * fix(i18n-tauri): quote placeholder-leading value in ko.yml to prevent rust_i18n parse panic * chore(i18n): translate settings.json (forgot to include previously) --------- Co-authored-by: rozan <34974262+thelojan@users.noreply.github.com>
316 lines
9.1 KiB
JavaScript
316 lines
9.1 KiB
JavaScript
/**
|
||
* CLI tool to update version numbers in package.json, src-tauri/Cargo.toml, and src-tauri/tauri.conf.json.
|
||
*
|
||
* Usage:
|
||
* pnpm release-version <version>
|
||
*
|
||
* <version> can be:
|
||
* - A full semver version (e.g., 1.2.3, v1.2.3, 1.2.3-beta, v1.2.3+build)
|
||
* - A tag: "alpha", "beta", "rc", "autobuild", "autobuild-latest", or "deploytest"
|
||
* - "alpha", "beta", "rc": Appends the tag to the current base version (e.g., 1.2.3-beta)
|
||
* - "autobuild": Appends a timestamped autobuild tag (e.g., 1.2.3+autobuild.2406101530)
|
||
* - "autobuild-latest": Appends an autobuild tag with latest Tauri commit (e.g., 1.2.3+autobuild.0614.a1b2c3d)
|
||
* - "deploytest": Appends a timestamped deploytest tag (e.g., 1.2.3+deploytest.2406101530)
|
||
*
|
||
* Examples:
|
||
* pnpm release-version 1.2.3
|
||
* pnpm release-version v1.2.3-beta
|
||
* pnpm release-version beta
|
||
* pnpm release-version autobuild
|
||
* pnpm release-version autobuild-latest
|
||
* pnpm release-version deploytest
|
||
*
|
||
* The script will:
|
||
* - Validate and normalize the version argument
|
||
* - Update the version field in package.json
|
||
* - Update the version field in src-tauri/Cargo.toml
|
||
* - Update the version field in src-tauri/tauri.conf.json
|
||
*
|
||
* Errors are logged and the process exits with code 1 on failure.
|
||
*/
|
||
|
||
import { execSync } from "child_process";
|
||
import fs from "fs/promises";
|
||
import path from "path";
|
||
|
||
import { program } from "commander";
|
||
|
||
/**
|
||
* 获取当前 git 短 commit hash
|
||
* @returns {string}
|
||
*/
|
||
function getGitShortCommit() {
|
||
try {
|
||
return execSync("git rev-parse --short HEAD").toString().trim();
|
||
} catch {
|
||
console.warn("[WARN]: Failed to get git short commit, fallback to 'nogit'");
|
||
return "nogit";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取最新 Tauri 相关提交的短 hash
|
||
* @returns {string}
|
||
*/
|
||
function getLatestTauriCommit() {
|
||
try {
|
||
const fullHash = execSync(
|
||
"bash ./scripts-workflow/get_latest_tauri_commit.bash",
|
||
)
|
||
.toString()
|
||
.trim();
|
||
const shortHash = execSync(`git rev-parse --short ${fullHash}`)
|
||
.toString()
|
||
.trim();
|
||
console.log(`[INFO]: Latest Tauri-related commit: ${shortHash}`);
|
||
return shortHash;
|
||
} catch (error) {
|
||
console.warn(
|
||
"[WARN]: Failed to get latest Tauri commit, fallback to current git short commit",
|
||
);
|
||
console.warn(`[WARN]: Error details: ${error.message}`);
|
||
return getGitShortCommit();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 生成短时间戳(格式:MMDD)或带 commit(格式:MMDD.cc39b27)
|
||
* 使用 Asia/Shanghai 时区
|
||
* @param {boolean} withCommit 是否带 commit
|
||
* @param {boolean} useTauriCommit 是否使用 Tauri 相关的 commit(仅当 withCommit 为 true 时有效)
|
||
* @returns {string}
|
||
*/
|
||
function generateShortTimestamp(withCommit = false, useTauriCommit = false) {
|
||
const now = new Date();
|
||
|
||
const formatter = new Intl.DateTimeFormat("en-CA", {
|
||
timeZone: "Asia/Shanghai",
|
||
month: "2-digit",
|
||
day: "2-digit",
|
||
});
|
||
|
||
const parts = formatter.formatToParts(now);
|
||
const month = parts.find((part) => part.type === "month").value;
|
||
const day = parts.find((part) => part.type === "day").value;
|
||
|
||
if (withCommit) {
|
||
const gitShort = useTauriCommit
|
||
? getLatestTauriCommit()
|
||
: getGitShortCommit();
|
||
return `${month}${day}.${gitShort}`;
|
||
}
|
||
return `${month}${day}`;
|
||
}
|
||
|
||
/**
|
||
* 验证版本号格式
|
||
* @param {string} version
|
||
* @returns {boolean}
|
||
*/
|
||
function isValidVersion(version) {
|
||
return /^v?\d+\.\d+\.\d+(-(alpha|beta|rc)(\.\d+)?)?(\+[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*)?$/i.test(
|
||
version,
|
||
);
|
||
}
|
||
|
||
/**
|
||
* 标准化版本号
|
||
* @param {string} version
|
||
* @returns {string}
|
||
*/
|
||
function normalizeVersion(version) {
|
||
return version.startsWith("v") ? version : `v${version}`;
|
||
}
|
||
|
||
/**
|
||
* 提取基础版本号(去掉所有 -tag 和 +build 部分)
|
||
* @param {string} version
|
||
* @returns {string}
|
||
*/
|
||
function getBaseVersion(version) {
|
||
let base = version.replace(/-(alpha|beta|rc)(\.\d+)?/i, "");
|
||
base = base.replace(/\+[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*/g, "");
|
||
return base;
|
||
}
|
||
|
||
/**
|
||
* 更新 package.json 版本号
|
||
* @param {string} newVersion
|
||
*/
|
||
async function updatePackageVersion(newVersion) {
|
||
const _dirname = process.cwd();
|
||
const packageJsonPath = path.join(_dirname, "package.json");
|
||
try {
|
||
const data = await fs.readFile(packageJsonPath, "utf8");
|
||
const packageJson = JSON.parse(data);
|
||
|
||
console.log(
|
||
"[INFO]: Current package.json version is: ",
|
||
packageJson.version,
|
||
);
|
||
packageJson.version = newVersion.startsWith("v")
|
||
? newVersion.slice(1)
|
||
: newVersion;
|
||
await fs.writeFile(
|
||
packageJsonPath,
|
||
JSON.stringify(packageJson, null, 2),
|
||
"utf8",
|
||
);
|
||
console.log(
|
||
`[INFO]: package.json version updated to: ${packageJson.version}`,
|
||
);
|
||
} catch (error) {
|
||
console.error("Error updating package.json version:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新 Cargo.toml 版本号
|
||
* @param {string} newVersion
|
||
*/
|
||
async function updateCargoVersion(newVersion) {
|
||
const _dirname = process.cwd();
|
||
const cargoTomlPath = path.join(_dirname, "src-tauri", "Cargo.toml");
|
||
try {
|
||
const data = await fs.readFile(cargoTomlPath, "utf8");
|
||
const lines = data.split("\n");
|
||
const versionWithoutV = newVersion.startsWith("v")
|
||
? newVersion.slice(1)
|
||
: newVersion;
|
||
|
||
const updatedLines = lines.map((line) => {
|
||
if (line.trim().startsWith("version =")) {
|
||
return line.replace(
|
||
/version\s*=\s*"[^"]+"/,
|
||
`version = "${versionWithoutV}"`,
|
||
);
|
||
}
|
||
return line;
|
||
});
|
||
|
||
await fs.writeFile(cargoTomlPath, updatedLines.join("\n"), "utf8");
|
||
console.log(`[INFO]: Cargo.toml version updated to: ${versionWithoutV}`);
|
||
} catch (error) {
|
||
console.error("Error updating Cargo.toml version:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新 tauri.conf.json 版本号
|
||
* @param {string} newVersion
|
||
*/
|
||
async function updateTauriConfigVersion(newVersion) {
|
||
const _dirname = process.cwd();
|
||
const tauriConfigPath = path.join(_dirname, "src-tauri", "tauri.conf.json");
|
||
try {
|
||
const data = await fs.readFile(tauriConfigPath, "utf8");
|
||
const tauriConfig = JSON.parse(data);
|
||
const versionWithoutV = newVersion.startsWith("v")
|
||
? newVersion.slice(1)
|
||
: newVersion;
|
||
|
||
console.log(
|
||
"[INFO]: Current tauri.conf.json version is: ",
|
||
tauriConfig.version,
|
||
);
|
||
|
||
// 使用完整版本信息,包含build metadata
|
||
tauriConfig.version = versionWithoutV;
|
||
|
||
await fs.writeFile(
|
||
tauriConfigPath,
|
||
JSON.stringify(tauriConfig, null, 2),
|
||
"utf8",
|
||
);
|
||
console.log(
|
||
`[INFO]: tauri.conf.json version updated to: ${versionWithoutV}`,
|
||
);
|
||
} catch (error) {
|
||
console.error("Error updating tauri.conf.json version:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取当前版本号
|
||
*/
|
||
async function getCurrentVersion() {
|
||
const _dirname = process.cwd();
|
||
const packageJsonPath = path.join(_dirname, "package.json");
|
||
try {
|
||
const data = await fs.readFile(packageJsonPath, "utf8");
|
||
const packageJson = JSON.parse(data);
|
||
return packageJson.version;
|
||
} catch (error) {
|
||
console.error("Error getting current version:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 主函数
|
||
*/
|
||
async function main(versionArg) {
|
||
if (!versionArg) {
|
||
console.error("Error: Version argument is required");
|
||
process.exit(1);
|
||
}
|
||
|
||
try {
|
||
let newVersion;
|
||
const validTags = [
|
||
"alpha",
|
||
"beta",
|
||
"rc",
|
||
"autobuild",
|
||
"autobuild-latest",
|
||
"deploytest",
|
||
];
|
||
|
||
if (validTags.includes(versionArg.toLowerCase())) {
|
||
const currentVersion = await getCurrentVersion();
|
||
const baseVersion = getBaseVersion(currentVersion);
|
||
|
||
if (versionArg.toLowerCase() === "autobuild") {
|
||
// 格式: 2.3.0+autobuild.1004.cc39b27
|
||
// 使用 Tauri 相关的最新 commit hash
|
||
newVersion = `${baseVersion}+autobuild.${generateShortTimestamp(true, true)}`;
|
||
} else if (versionArg.toLowerCase() === "autobuild-latest") {
|
||
// 格式: 2.3.0+autobuild.1004.a1b2c3d (使用最新 Tauri 提交)
|
||
const latestTauriCommit = getLatestTauriCommit();
|
||
newVersion = `${baseVersion}+autobuild.${generateShortTimestamp()}.${latestTauriCommit}`;
|
||
} else if (versionArg.toLowerCase() === "deploytest") {
|
||
// 格式: 2.3.0+deploytest.1004.cc39b27
|
||
// 使用 Tauri 相关的最新 commit hash
|
||
newVersion = `${baseVersion}+deploytest.${generateShortTimestamp(true, true)}`;
|
||
} else {
|
||
newVersion = `${baseVersion}-${versionArg.toLowerCase()}`;
|
||
}
|
||
} else {
|
||
if (!isValidVersion(versionArg)) {
|
||
console.error("Error: Invalid version format");
|
||
process.exit(1);
|
||
}
|
||
newVersion = normalizeVersion(versionArg);
|
||
}
|
||
|
||
console.log(`[INFO]: Updating versions to: ${newVersion}`);
|
||
await updatePackageVersion(newVersion);
|
||
await updateCargoVersion(newVersion);
|
||
await updateTauriConfigVersion(newVersion);
|
||
console.log("[SUCCESS]: All version updates completed successfully!");
|
||
} catch (error) {
|
||
console.error("[ERROR]: Failed to update versions:", error);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
program
|
||
.name("pnpm release-version")
|
||
.description("Update project version numbers")
|
||
.argument("<version>", "version tag or full version")
|
||
.action(main)
|
||
.parse(process.argv);
|