import * as plugins from './mod.plugins.js'; import { PackageManagerUtil, type TPackageManager, type IPackageUpdateInfo, type IPackageManagerInfo } from './classes.packagemanager.js'; import { commitinfo } from '../00_commitinfo_data.js'; // Curated list of known @git.zone CLI tools to track for updates. // This list is intentionally hardcoded to only track official tools. // Add new entries here when new @git.zone packages are published. 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', ]; export interface IUpdateOptions { yes?: boolean; verbose?: boolean; } export const run = async (options: IUpdateOptions = {}): Promise => { const pmUtil = new PackageManagerUtil(); const verbose = options.verbose === true; console.log('Scanning for installed @git.zone packages...\n'); // Check which package managers are available if (verbose) { console.log('Detecting package managers:'); } const detectedPMs: IPackageManagerInfo[] = []; for (const pm of ['npm', 'yarn', 'pnpm'] as TPackageManager[]) { const info = await pmUtil.detectPackageManager(pm, verbose); if (info.available) { detectedPMs.push(info); } } if (verbose) { console.log(''); } if (detectedPMs.length === 0) { console.log('No package managers found (npm, yarn, pnpm).'); console.log('Tried detection via \'which\' command and direct version check.'); return; } // Get version info for each PM and display status table console.log('Package managers:\n'); console.log(' Name Current Latest Status'); console.log(' ──────────────────────────────────────────────'); for (const pmInfo of detectedPMs) { const versionInfo = await pmUtil.getPackageManagerVersion(pmInfo.name); pmInfo.currentVersion = versionInfo.current; pmInfo.latestVersion = versionInfo.latest; pmInfo.needsUpdate = versionInfo.latest ? pmUtil.isNewerVersion(versionInfo.current, versionInfo.latest) : false; const name = pmInfo.name.padEnd(9); const current = versionInfo.current.padEnd(12); const latest = (versionInfo.latest || 'unknown').padEnd(12); const status = versionInfo.latest === null ? '? Version unknown' : pmInfo.needsUpdate ? '⬆️ Update available' : '✓ Up to date'; console.log(` ${name}${current}${latest}${status}`); } console.log(''); // === Self-update check === console.log('Checking for gtools self-update...\n'); const selfVersion = commitinfo.version; const selfLatest = await pmUtil.getLatestVersion('@git.zone/tools'); if (selfLatest && pmUtil.isNewerVersion(selfVersion, selfLatest)) { console.log(` @git.zone/tools ${selfVersion} → ${selfLatest} ⬆️ Update available\n`); // Find which PM has it installed globally let selfPm: TPackageManager | null = null; for (const pmInfo of detectedPMs) { const installed = await pmUtil.getInstalledPackages(pmInfo.name); if (installed.some(p => p.name === '@git.zone/tools')) { selfPm = pmInfo.name; break; } } if (!selfPm) { // Fallback: use first available PM selfPm = detectedPMs[0].name; } let shouldSelfUpdate = options.yes === true; if (!shouldSelfUpdate) { const smartinteractInstance = new plugins.smartinteract.SmartInteract(); const answer = await smartinteractInstance.askQuestion({ type: 'confirm', name: 'confirmSelfUpdate', message: 'Do you want to update gtools itself first?', default: true, }); shouldSelfUpdate = answer.value === true; } if (shouldSelfUpdate) { const success = await pmUtil.executeUpdate(selfPm, '@git.zone/tools'); if (success) { console.log('\ngtools has been updated. Please re-run "gtools update" to check remaining packages.'); process.exit(0); } else { console.log('\ngtools self-update failed. Continuing with current version...\n'); } } } else { console.log(` @git.zone/tools ${selfVersion} ✓ Up to date\n`); } // Collect all installed @git.zone packages from all package managers const allPackages: IPackageUpdateInfo[] = []; for (const pmInfo of detectedPMs) { const installed = await pmUtil.getInstalledPackages(pmInfo.name); for (const pkg of installed) { // Only include packages from our predefined list if (GITZONE_PACKAGES.includes(pkg.name)) { const latestVersion = await pmUtil.getLatestVersion(pkg.name); allPackages.push({ name: pkg.name, currentVersion: pkg.version, latestVersion: latestVersion || 'unknown', packageManager: pmInfo.name, needsUpdate: latestVersion ? pmUtil.isNewerVersion(pkg.version, latestVersion) : false, }); } } } if (allPackages.length === 0) { console.log('No @git.zone packages found installed globally.'); return; } // Display package table console.log('Installed @git.zone packages:\n'); console.log(' Package Current Latest PM Status'); console.log(' ─────────────────────────────────────────────────────────────────────'); for (const pkg of allPackages) { const name = pkg.name.padEnd(28); const current = pkg.currentVersion.padEnd(12); const latest = pkg.latestVersion.padEnd(12); const pm = pkg.packageManager.padEnd(8); const status = pkg.latestVersion === 'unknown' ? '? Version unknown' : pkg.needsUpdate ? '⬆️ Update available' : '✓ Up to date'; console.log(` ${name}${current}${latest}${pm}${status}`); } console.log(''); // Filter packages that need updates const packagesToUpdate = allPackages.filter(p => p.needsUpdate); if (packagesToUpdate.length === 0) { console.log('All packages are up to date!'); return; } console.log(`Found ${packagesToUpdate.length} package(s) with available updates.\n`); // Ask for confirmation unless -y flag is provided let shouldUpdate = options.yes === true; if (!shouldUpdate) { const smartinteractInstance = new plugins.smartinteract.SmartInteract(); const answer = await smartinteractInstance.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; } // Execute updates console.log('\nUpdating packages...\n'); let successCount = 0; let failCount = 0; for (const pkg of packagesToUpdate) { const success = await pmUtil.executeUpdate(pkg.packageManager, pkg.name); if (success) { console.log(` ✓ ${pkg.name} updated successfully`); successCount++; } else { console.log(` ✗ ${pkg.name} update failed`); failCount++; } } console.log(''); if (failCount === 0) { console.log(`All ${successCount} package(s) updated successfully!`); } else { console.log(`Updated ${successCount} package(s), ${failCount} failed.`); } };