Files
WRNexusJS/packages/cli/src/update-notifier.ts
T

84 lines
2.5 KiB
TypeScript

import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { homedir } from "node:os";
import { dirname, join } from "node:path";
import { spawnSync } from "node:child_process";
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
interface UpdateCache {
checkedAt: number;
latest: string;
}
export function currentCliVersion(): string {
try {
return JSON.parse(readFileSync(join(import.meta.dir, "..", "package.json"), "utf8")).version;
} catch {
return "0.0.0";
}
}
export function compareVersions(a: string, b: string): number {
const left = a.split("-")[0]!.split(".").map(Number);
const right = b.split("-")[0]!.split(".").map(Number);
for (let i = 0; i < 3; i++) {
const difference = (left[i] || 0) - (right[i] || 0);
if (difference) return difference > 0 ? 1 : -1;
}
return 0;
}
function cachePath(): string {
return process.env.WRNEXUS_UPDATE_CACHE ?? join(homedir(), ".wrnexus", "update-check.json");
}
function cachedLatest(now: number): string | null {
try {
const cached = JSON.parse(readFileSync(cachePath(), "utf8")) as UpdateCache;
return now - cached.checkedAt < CHECK_INTERVAL_MS ? cached.latest : null;
} catch {
return null;
}
}
function registryLatest(): string | null {
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
const result = spawnSync(npm, ["view", "@wrnexus/cli", "version"], {
encoding: "utf8",
timeout: 5000,
windowsHide: true,
});
const version = (result.stdout ?? "").trim();
return result.status === 0 && /^\d+\.\d+\.\d+/.test(version) ? version : null;
}
/** Check at most once per day and print a non-blocking upgrade suggestion. */
export function notifyIfUpdateAvailable(current: string, command?: string): void {
if (
process.env.WRNEXUS_NO_UPDATE_CHECK === "1" ||
command === "update" ||
command === "upgrade"
) {
return;
}
const now = Date.now();
let latest = cachedLatest(now);
if (!latest) {
latest = registryLatest();
if (!latest) return;
try {
const file = cachePath();
if (!existsSync(dirname(file))) mkdirSync(dirname(file), { recursive: true });
writeFileSync(file, JSON.stringify({ checkedAt: now, latest }) + "\n", "utf8");
} catch {
// A read-only home directory must never break the CLI.
}
}
if (compareVersions(latest, current) > 0) {
console.log(
`\n Update available: WRNexusJS ${current}${latest}\n` +
` Run: wrnexus update --latest\n`,
);
}
}