import * as plugins from "./mod.plugins.js"; import { commitinfo } from "../00_commitinfo_data.js"; import type { ICliMode } from "../helpers.climode.js"; import { getCliMode, printJson } from "../helpers.climode.js"; import { PackageManagerUtil, type IInstalledPackage, type IPackageUpdateInfo, } from "./classes.packagemanager.js"; export const GITZONE_PACKAGES = [ "@git.zone/cli", "@git.zone/tsdoc", "@git.zone/tsbuild", "@git.zone/tstest", "@git.zone/tspublish", "@git.zone/tsbundle", "@git.zone/tsdocker", "@git.zone/tsview", "@git.zone/tswatch", "@git.zone/tsrust", ]; export const run = async (argvArg: any = {}): Promise => { const mode = await getCliMode(argvArg); const command = argvArg._?.[1] || "help"; if (mode.help || command === "help") { showHelp(mode); return; } switch (command) { case "update": await runUpdate(argvArg, mode); break; case "install": await runInstall(argvArg, mode); break; default: showHelp(mode); break; } }; async function runUpdate(argvArg: any, mode: ICliMode): Promise { const verbose = Boolean(argvArg.v || argvArg.verbose); const pmUtil = new PackageManagerUtil(); console.log("Scanning for installed @git.zone packages...\n"); const pnpmInfo = await pmUtil.getPnpmVersionInfo(); if (!pnpmInfo.available) { console.log("pnpm is required for gitzone tools update, but it was not found."); return; } console.log("Package manager:\n"); console.log(" Name Current Latest Status"); console.log(" ----------------------------------------------"); const latestPnpm = (pnpmInfo.latestVersion || "unknown").padEnd(12); const pnpmStatus = pnpmInfo.latestVersion === null ? "? Version unknown" : pnpmInfo.needsUpdate ? "Update available" : "Up to date"; console.log(` ${"pnpm".padEnd(9)}${pnpmInfo.currentVersion.padEnd(12)}${latestPnpm}${pnpmStatus}`); console.log(""); if (verbose) { console.log("Using pnpm as the supported global package manager.\n"); } const selfUpdated = await handleSelfUpdate(pmUtil, mode); if (selfUpdated) { return; } const installedPackages = await pmUtil.getInstalledPackages(); const packageInfos = await getPackageUpdateInfos(pmUtil, installedPackages); if (packageInfos.length === 0) { console.log("No managed @git.zone packages found installed globally."); return; } console.log("Installed @git.zone packages:\n"); console.log(" Package Current Latest Status"); console.log(" ------------------------------------------------------------"); for (const packageInfo of packageInfos) { const status = packageInfo.latestVersion === "unknown" ? "? Version unknown" : packageInfo.needsUpdate ? "Update available" : "Up to date"; console.log( ` ${packageInfo.name.padEnd(28)}${packageInfo.currentVersion.padEnd(12)}${packageInfo.latestVersion.padEnd(12)}${status}`, ); } console.log(""); await printMissingPackages(pmUtil, installedPackages); const packagesToUpdate = packageInfos.filter((packageInfo) => packageInfo.needsUpdate); if (packagesToUpdate.length === 0) { console.log("All managed packages are up to date."); return; } console.log(`Found ${packagesToUpdate.length} package(s) with available updates.\n`); if (!mode.yes && !mode.interactive) { console.log("Run gitzone tools update -y to update without prompts."); return; } let shouldUpdate = mode.yes; if (!shouldUpdate) { const interactInstance = new plugins.smartinteract.SmartInteract(); const answer = await interactInstance.askQuestion({ type: "confirm", name: "confirmUpdate", message: "Do you want to update these packages?", default: true, }); shouldUpdate = answer.value === true; } if (!shouldUpdate) { console.log("Update cancelled."); return; } await installPackages(pmUtil, packagesToUpdate.map((packageInfo) => packageInfo.name), "updated"); } async function runInstall(argvArg: any, mode: ICliMode): Promise { const verbose = Boolean(argvArg.v || argvArg.verbose); const pmUtil = new PackageManagerUtil(); console.log("Scanning for missing @git.zone packages...\n"); const pnpmAvailable = await pmUtil.detectPnpm(); if (!pnpmAvailable) { console.log("pnpm is required for gitzone tools install, but it was not found."); return; } if (verbose) { console.log("Using pnpm as the supported global package manager.\n"); } const installedPackages = await pmUtil.getInstalledPackages(); const installedNames = new Set(installedPackages.map((packageInfo) => packageInfo.name)); const missingPackages = GITZONE_PACKAGES.filter((packageName) => !installedNames.has(packageName)); if (missingPackages.length === 0) { console.log("All managed @git.zone packages are already installed."); return; } console.log(`Found ${missingPackages.length} missing package(s).\n`); if (!mode.yes && !mode.interactive) { await printPackageListWithLatest(pmUtil, missingPackages); console.log("Run gitzone tools install -y to install all missing packages without prompts."); return; } let selectedPackages = missingPackages; if (!mode.yes) { const choicesWithVersions: Array<{ name: string; value: string }> = []; for (const packageName of missingPackages) { const latest = await pmUtil.getLatestVersion(packageName); choicesWithVersions.push({ name: `${packageName}${latest ? `@${latest}` : ""}`, value: packageName, }); } const interactInstance = new plugins.smartinteract.SmartInteract(); const answer = await interactInstance.askQuestion({ type: "checkbox", name: "packages", message: "Select packages to install:", default: missingPackages, choices: choicesWithVersions, }); selectedPackages = answer.value as string[]; if (selectedPackages.length === 0) { console.log("No packages selected. Nothing to install."); return; } } await installPackages(pmUtil, selectedPackages, "installed"); } async function handleSelfUpdate( pmUtil: PackageManagerUtil, mode: ICliMode, ): Promise { console.log("Checking for gitzone self-update...\n"); const currentVersion = commitinfo.version; const latestVersion = await pmUtil.getLatestVersion("@git.zone/cli"); if (!latestVersion || !pmUtil.isNewerVersion(currentVersion, latestVersion)) { console.log(` @git.zone/cli ${currentVersion} Up to date\n`); return false; } console.log(` @git.zone/cli ${currentVersion} -> ${latestVersion} Update available\n`); if (!mode.yes && !mode.interactive) { console.log("Run gitzone tools update -y to update gitzone first."); return true; } let shouldUpdate = mode.yes; if (!shouldUpdate) { const interactInstance = new plugins.smartinteract.SmartInteract(); const answer = await interactInstance.askQuestion({ type: "confirm", name: "confirmSelfUpdate", message: "Do you want to update gitzone itself first?", default: true, }); shouldUpdate = answer.value === true; } if (!shouldUpdate) { console.log("Skipping gitzone self-update.\n"); return false; } const success = await pmUtil.installLatest("@git.zone/cli"); if (!success) { console.log("\ngitzone self-update failed. Continuing with the current version.\n"); return false; } console.log("\ngitzone has been updated. Re-run gitzone tools update to check remaining packages."); return true; } async function getPackageUpdateInfos( pmUtil: PackageManagerUtil, installedPackages: IInstalledPackage[], ): Promise { const packageInfos: IPackageUpdateInfo[] = []; for (const installedPackage of installedPackages) { if (!GITZONE_PACKAGES.includes(installedPackage.name)) { continue; } const latestVersion = await pmUtil.getLatestVersion(installedPackage.name); packageInfos.push({ name: installedPackage.name, currentVersion: installedPackage.version, latestVersion: latestVersion || "unknown", needsUpdate: latestVersion ? pmUtil.isNewerVersion(installedPackage.version, latestVersion) : false, }); } return packageInfos; } async function printMissingPackages( pmUtil: PackageManagerUtil, installedPackages: IInstalledPackage[], ): Promise { const installedNames = new Set(installedPackages.map((packageInfo) => packageInfo.name)); const missingPackages = GITZONE_PACKAGES.filter((packageName) => !installedNames.has(packageName)); if (missingPackages.length === 0) { return; } console.log("Not installed (managed @git.zone packages):\n"); await printPackageListWithLatest(pmUtil, missingPackages); console.log("Run gitzone tools install to install missing packages.\n"); } async function printPackageListWithLatest( pmUtil: PackageManagerUtil, packageNames: string[], ): Promise { console.log(" Package Latest"); console.log(" ----------------------------------------"); for (const packageName of packageNames) { const latest = await pmUtil.getLatestVersion(packageName); console.log(` ${packageName.padEnd(28)} ${latest || "unknown"}`); } console.log(""); } async function installPackages( pmUtil: PackageManagerUtil, packageNames: string[], action: "installed" | "updated", ): Promise { let successCount = 0; let failCount = 0; for (const packageName of packageNames) { const success = await pmUtil.installLatest(packageName); if (success) { console.log(` ${packageName} ${action} successfully`); successCount++; } else { console.log(` ${packageName} failed`); failCount++; } } console.log(""); if (failCount === 0) { console.log(`All ${successCount} package(s) ${action} successfully.`); } else { console.log(`${successCount} package(s) ${action}, ${failCount} failed.`); } } export function showHelp(mode?: ICliMode): void { if (mode?.json) { printJson({ name: "gitzone tools", usage: "gitzone tools [options]", commands: [ { name: "update", description: "Check and update globally installed @git.zone packages" }, { name: "install", description: "Install missing managed @git.zone packages" }, ], flags: [ { flag: "-y, --yes", description: "Run without confirmation prompts" }, { flag: "-v, --verbose", description: "Show package manager diagnostics" }, ], packageManager: "pnpm", managedPackages: GITZONE_PACKAGES, }); return; } console.log(""); console.log("Usage: gitzone tools [options]"); console.log(""); console.log("Commands:"); console.log(" update Check and update globally installed @git.zone packages"); console.log(" install Install missing managed @git.zone packages"); console.log(""); console.log("Options:"); console.log(" -y, --yes Run without confirmation prompts"); console.log(" -v, --verbose Show package manager diagnostics"); console.log(""); console.log("Examples:"); console.log(" gitzone tools update"); console.log(" gitzone tools update -y"); console.log(" gitzone tools install"); console.log(""); }