refactor(client): unify web and desktop release workflow
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const frontendDir = fileURLToPath(new URL("../", import.meta.url));
|
||||
const paths = {
|
||||
packageJson: new URL("../package.json", import.meta.url),
|
||||
packageLock: new URL("../package-lock.json", import.meta.url),
|
||||
tauriConfig: new URL("../src-tauri/tauri.conf.json", import.meta.url),
|
||||
cargoToml: new URL("../src-tauri/Cargo.toml", import.meta.url),
|
||||
cargoLock: new URL("../src-tauri/Cargo.lock", import.meta.url),
|
||||
};
|
||||
const SEMVER_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
||||
|
||||
const readJson = async (path) => JSON.parse(await readFile(path, "utf8"));
|
||||
const writeJson = async (path, value) => writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
||||
|
||||
const readCargoPackageVersion = async () => {
|
||||
const cargo = await readFile(paths.cargoToml, "utf8");
|
||||
const packageSection = cargo.match(/\[package\]([\s\S]*?)(?=\n\[|$)/)?.[1];
|
||||
const version = packageSection?.match(/^version\s*=\s*"([^"]+)"/m)?.[1];
|
||||
if (!version) throw new Error("Cannot find [package].version in src-tauri/Cargo.toml");
|
||||
return version;
|
||||
};
|
||||
|
||||
const readCargoLockPackageVersion = async () => {
|
||||
const cargoLock = await readFile(paths.cargoLock, "utf8");
|
||||
const packageSection = cargoLock
|
||||
.match(/\[\[package\]\]([\s\S]*?)(?=\n\[\[package\]\]|$)/g)
|
||||
?.find((section) => /^name\s*=\s*"ctms-desktop"$/m.test(section));
|
||||
const version = packageSection?.match(/^version\s*=\s*"([^"]+)"/m)?.[1];
|
||||
if (!version) throw new Error("Cannot find ctms-desktop version in src-tauri/Cargo.lock");
|
||||
return version;
|
||||
};
|
||||
|
||||
const readVersions = async () => {
|
||||
const [packageJson, packageLock, tauriConfig, cargoVersion, cargoLockVersion] = await Promise.all([
|
||||
readJson(paths.packageJson),
|
||||
readJson(paths.packageLock),
|
||||
readJson(paths.tauriConfig),
|
||||
readCargoPackageVersion(),
|
||||
readCargoLockPackageVersion(),
|
||||
]);
|
||||
return {
|
||||
"package.json": packageJson.version,
|
||||
"package-lock.json": packageLock.version,
|
||||
"package-lock.json root": packageLock.packages?.[""]?.version,
|
||||
"tauri.conf.json": tauriConfig.version,
|
||||
"Cargo.toml": cargoVersion,
|
||||
"Cargo.lock": cargoLockVersion,
|
||||
};
|
||||
};
|
||||
|
||||
const assertVersionsMatch = async () => {
|
||||
const versions = await readVersions();
|
||||
const uniqueVersions = new Set(Object.values(versions));
|
||||
if (uniqueVersions.size !== 1 || uniqueVersions.has(undefined)) {
|
||||
const details = Object.entries(versions)
|
||||
.map(([file, version]) => ` ${file}: ${version ?? "<missing>"}`)
|
||||
.join("\n");
|
||||
throw new Error(`Client versions are not synchronized:\n${details}`);
|
||||
}
|
||||
const [version] = uniqueVersions;
|
||||
console.log(`Client version ${version} is synchronized.`);
|
||||
};
|
||||
|
||||
const setVersion = async (version) => {
|
||||
if (!SEMVER_PATTERN.test(version)) {
|
||||
throw new Error(`Invalid semantic version: ${version}`);
|
||||
}
|
||||
|
||||
const [packageJson, packageLock, tauriConfig, tauriConfigSource, cargo, cargoLock] = await Promise.all([
|
||||
readJson(paths.packageJson),
|
||||
readJson(paths.packageLock),
|
||||
readJson(paths.tauriConfig),
|
||||
readFile(paths.tauriConfig, "utf8"),
|
||||
readFile(paths.cargoToml, "utf8"),
|
||||
readFile(paths.cargoLock, "utf8"),
|
||||
]);
|
||||
|
||||
packageJson.version = version;
|
||||
packageLock.version = version;
|
||||
packageLock.packages[""].version = version;
|
||||
|
||||
const tauriVersionPattern = /("version"\s*:\s*")[^"]+(")/;
|
||||
const packageSectionPattern = /(\[package\][\s\S]*?^version\s*=\s*")[^"]+(")/m;
|
||||
const lockPackagePattern =
|
||||
/(\[\[package\]\]\nname\s*=\s*"ctms-desktop"\nversion\s*=\s*")[^"]+(")/m;
|
||||
if (typeof tauriConfig.version !== "string" || !tauriVersionPattern.test(tauriConfigSource)) {
|
||||
throw new Error("Cannot update version in src-tauri/tauri.conf.json");
|
||||
}
|
||||
if (!packageSectionPattern.test(cargo)) {
|
||||
throw new Error("Cannot update [package].version in src-tauri/Cargo.toml");
|
||||
}
|
||||
if (!lockPackagePattern.test(cargoLock)) {
|
||||
throw new Error("Cannot update ctms-desktop version in src-tauri/Cargo.lock");
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
writeJson(paths.packageJson, packageJson),
|
||||
writeJson(paths.packageLock, packageLock),
|
||||
writeFile(paths.tauriConfig, tauriConfigSource.replace(tauriVersionPattern, `$1${version}$2`)),
|
||||
writeFile(paths.cargoToml, cargo.replace(packageSectionPattern, `$1${version}$2`)),
|
||||
writeFile(paths.cargoLock, cargoLock.replace(lockPackagePattern, `$1${version}$2`)),
|
||||
]);
|
||||
console.log(`Updated CTMS Web and Desktop client version to ${version} in ${frontendDir}`);
|
||||
await assertVersionsMatch();
|
||||
};
|
||||
|
||||
const [, , command = "--check", value] = process.argv;
|
||||
|
||||
try {
|
||||
if (command === "--check") {
|
||||
await assertVersionsMatch();
|
||||
} else if (command === "--set" && value) {
|
||||
await setVersion(value);
|
||||
} else {
|
||||
throw new Error("Usage: node scripts/client-version.mjs [--check | --set <semver>]");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : error);
|
||||
process.exitCode = 1;
|
||||
}
|
||||
Reference in New Issue
Block a user