diff --git a/packages/cli/package.json b/packages/cli/package.json index e8cc2dbef..d08f5c857 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -56,7 +56,7 @@ "@pgpmjs/server-utils": "workspace:^", "@pgpmjs/types": "workspace:^", "find-and-require-package-json": "^0.8.2", - "inquirerer": "^4.1.2", + "inquirerer": "^4.2.0", "js-yaml": "^4.1.0", "minimist": "^1.2.8", "pg-cache": "workspace:^", diff --git a/packages/csv-to-pg/package.json b/packages/csv-to-pg/package.json index b6a495991..2220facdc 100644 --- a/packages/csv-to-pg/package.json +++ b/packages/csv-to-pg/package.json @@ -46,7 +46,7 @@ "@pgsql/types": "^17.6.2", "@pgsql/utils": "^17.8.4", "csv-parser": "^2.3.3", - "inquirerer": "^4.1.2", + "inquirerer": "^4.2.0", "js-yaml": "^3.14.0", "pgsql-deparser": "^17.15.0" } diff --git a/pgpm/cli/__tests__/upgrade-modules.test.ts b/pgpm/cli/__tests__/upgrade.test.ts similarity index 75% rename from pgpm/cli/__tests__/upgrade-modules.test.ts rename to pgpm/cli/__tests__/upgrade.test.ts index 70bd2a91a..c1a8e016a 100644 --- a/pgpm/cli/__tests__/upgrade-modules.test.ts +++ b/pgpm/cli/__tests__/upgrade.test.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import { TestFixture } from '../test-utils'; -describe('cmds:upgrade-modules - with initialized workspace and module', () => { +describe('cmds:upgrade - with initialized workspace and module', () => { let fixture: TestFixture; let workspaceDir: string; let moduleDir: string; @@ -45,7 +45,7 @@ describe('cmds:upgrade-modules - with initialized workspace and module', () => { describe('when no modules are installed', () => { it('reports no modules installed', async () => { await fixture.runCmd({ - _: ['upgrade-modules'], + _: ['upgrade'], cwd: moduleDir, }); @@ -65,10 +65,10 @@ describe('cmds:upgrade-modules - with initialized workspace and module', () => { }); it('reports modules are up to date', async () => { + // Default behavior now upgrades all (no --all flag needed) await fixture.runCmd({ - _: ['upgrade-modules'], + _: ['upgrade'], cwd: moduleDir, - all: true, }); const pkgJson = JSON.parse( @@ -88,7 +88,7 @@ describe('cmds:upgrade-modules - with initialized workspace and module', () => { it('dry run does not modify package.json', async () => { await fixture.runCmd({ - _: ['upgrade-modules'], + _: ['upgrade'], cwd: moduleDir, 'dry-run': true, }); @@ -99,16 +99,16 @@ describe('cmds:upgrade-modules - with initialized workspace and module', () => { expect(pkgJson.dependencies['@pgpm-testing/base32']).toBe('1.1.0'); }); - it('--all flag upgrades from 1.1.0 to 1.2.0', async () => { + it('default behavior upgrades all from 1.1.0 to 1.2.0', async () => { let pkgJson = JSON.parse( fs.readFileSync(path.join(moduleDir, 'package.json'), 'utf-8') ); expect(pkgJson.dependencies['@pgpm-testing/base32']).toBe('1.1.0'); + // Default behavior now upgrades all (like pnpm upgrade) await fixture.runCmd({ - _: ['upgrade-modules'], + _: ['upgrade'], cwd: moduleDir, - all: true, }); pkgJson = JSON.parse( @@ -117,17 +117,34 @@ describe('cmds:upgrade-modules - with initialized workspace and module', () => { expect(pkgJson.dependencies['@pgpm-testing/base32']).toBe('1.2.0'); }); - it('--modules flag filters to specific modules', async () => { + it('positional args filter to specific modules', async () => { let pkgJson = JSON.parse( fs.readFileSync(path.join(moduleDir, 'package.json'), 'utf-8') ); expect(pkgJson.dependencies['@pgpm-testing/base32']).toBe('1.1.0'); + // Positional args now used instead of --modules flag (like pnpm upgrade @pkg/name) await fixture.runCmd({ - _: ['upgrade-modules'], + _: ['upgrade', '@pgpm-testing/base32'], + cwd: moduleDir, + }); + + pkgJson = JSON.parse( + fs.readFileSync(path.join(moduleDir, 'package.json'), 'utf-8') + ); + expect(pkgJson.dependencies['@pgpm-testing/base32']).toBe('1.2.0'); + }); + + it('up alias works the same as upgrade', async () => { + let pkgJson = JSON.parse( + fs.readFileSync(path.join(moduleDir, 'package.json'), 'utf-8') + ); + expect(pkgJson.dependencies['@pgpm-testing/base32']).toBe('1.1.0'); + + // Test the 'up' alias + await fixture.runCmd({ + _: ['up'], cwd: moduleDir, - modules: '@pgpm-testing/base32', - all: true, }); pkgJson = JSON.parse( diff --git a/pgpm/cli/package.json b/pgpm/cli/package.json index e739ea3c1..803be243a 100644 --- a/pgpm/cli/package.json +++ b/pgpm/cli/package.json @@ -53,8 +53,8 @@ "@pgpmjs/types": "workspace:^", "appstash": "^0.2.6", "find-and-require-package-json": "^0.8.2", - "genomic": "^5.1.0", - "inquirerer": "^4.1.2", + "genomic": "^5.2.0", + "inquirerer": "^4.2.0", "js-yaml": "^4.1.0", "minimist": "^1.2.8", "pg-cache": "workspace:^", diff --git a/pgpm/cli/src/commands.ts b/pgpm/cli/src/commands.ts index 3922be102..73f0b2bf8 100644 --- a/pgpm/cli/src/commands.ts +++ b/pgpm/cli/src/commands.ts @@ -21,7 +21,7 @@ import migrate from './commands/migrate'; import _package from './commands/package'; import plan from './commands/plan'; import updateCmd from './commands/update'; -import upgradeModules from './commands/upgrade-modules'; +import upgrade from './commands/upgrade'; import remove from './commands/remove'; import renameCmd from './commands/rename'; import revert from './commands/revert'; @@ -64,7 +64,8 @@ export const createPgpmCommandMap = (skipPgTeardown: boolean = false): Record Working directory (default: current directory) - --all Upgrade all modules without prompting + -i, --interactive Show outdated modules and select which ones to upgrade --dry-run Show what would be upgraded without making changes - --modules Comma-separated list of specific modules to upgrade --workspace Upgrade modules across all packages in the workspace Examples: - pgpm upgrade-modules Interactive selection of modules to upgrade - pgpm upgrade-modules --all Upgrade all installed modules - pgpm upgrade-modules --dry-run Preview available upgrades - pgpm upgrade-modules --modules @pgpm/base32,@pgpm/faker Upgrade specific modules - pgpm upgrade-modules --workspace --all Upgrade all modules across the entire workspace + pgpm upgrade Upgrade all installed modules + pgpm upgrade -i Interactive selection of modules to upgrade + pgpm upgrade @pgpm/base32 Upgrade specific module + pgpm upgrade @pgpm/base32 @pgpm/uuid Upgrade multiple specific modules + pgpm upgrade --dry-run Preview available upgrades + pgpm upgrade --workspace Upgrade all modules across the entire workspace + pgpm up Alias for upgrade `; interface ModuleUpdateInfo { @@ -62,7 +64,7 @@ async function upgradeModulesForProject( argv: Partial, prompter: Inquirerer, dryRun: boolean, - upgradeAll: boolean, + interactive: boolean, specificModules: string[] | undefined, moduleName?: string ): Promise { @@ -78,10 +80,15 @@ async function upgradeModulesForProject( } const prefix = moduleName ? `[${moduleName}] ` : ''; - log.info(`${prefix}Found ${installed.length} installed module(s). Checking for updates...`); - + + // Use spinner while checking for updates + const spinner = createSpinner(`${prefix}Checking ${installed.length} installed module(s) for updates...`); + spinner.start(); + const moduleVersions = await fetchModuleVersions(installedVersions); const modulesWithUpdates = moduleVersions.filter(m => m.hasUpdate); + + spinner.succeed(`${prefix}Found ${modulesWithUpdates.length} module(s) with updates available`); if (modulesWithUpdates.length === 0) { log.success(`${prefix}All modules are already up to date.`); @@ -101,9 +108,8 @@ async function upgradeModulesForProject( let modulesToUpgrade: string[]; - if (upgradeAll) { - modulesToUpgrade = modulesWithUpdates.map(m => m.name); - } else if (specificModules) { + if (specificModules && specificModules.length > 0) { + // Specific modules provided as positional arguments modulesToUpgrade = modulesWithUpdates .filter(m => specificModules.includes(m.name)) .map(m => m.name); @@ -112,36 +118,26 @@ async function upgradeModulesForProject( log.warn(`${prefix}None of the specified modules have updates available.`); return false; } - } else { - const options = modulesWithUpdates.map(mod => ({ + } else if (interactive) { + // Interactive mode: use pnpm-style upgrade UI + const packages: PackageInfo[] = modulesWithUpdates.map(mod => ({ name: mod.name, - value: mod.name, - message: `${mod.name} (${mod.currentVersion} -> ${mod.latestVersion})` + current: mod.currentVersion, + latest: mod.latestVersion!, + type: 'dependencies' as const })); - const questions: Question[] = [ - { - name: 'selectedModules', - message: `${prefix}Select modules to upgrade:`, - type: 'checkbox', - options: options.map(o => o.message), - default: options.map(o => o.message) - } - ]; - - const answers = await prompter.prompt(argv, questions); - const selectedOptions = (answers.selectedModules as OptionValue[]) - .filter(opt => opt.selected) - .map(opt => opt.name); - - modulesToUpgrade = modulesWithUpdates - .filter(mod => selectedOptions.includes(`${mod.name} (${mod.currentVersion} -> ${mod.latestVersion})`)) - .map(m => m.name); + const result = await upgradePrompt(packages, 10); - if (modulesToUpgrade.length === 0) { + if (result.updates.length === 0) { log.info(`${prefix}No modules selected for upgrade.`); return false; } + + modulesToUpgrade = result.updates.map(u => u.name); + } else { + // Default behavior: upgrade all modules with updates (like pnpm upgrade) + modulesToUpgrade = modulesWithUpdates.map(m => m.name); } log.info(`\n${prefix}Upgrading ${modulesToUpgrade.length} module(s)...`); @@ -158,16 +154,18 @@ export default async ( _options: CLIOptions ) => { if (argv.help || argv.h) { - console.log(upgradeModulesUsageText); + console.log(upgradeUsageText); process.exit(0); } const { cwd = process.cwd() } = argv; const dryRun = Boolean(argv['dry-run']); - const upgradeAll = Boolean(argv.all); + const interactive = Boolean(argv.i || argv.interactive); const workspaceMode = Boolean(argv.workspace); - const specificModules = argv.modules - ? String(argv.modules).split(',').map(m => m.trim()) + + // Get specific modules from positional arguments (argv._) + const specificModules = argv._ && argv._.length > 0 + ? argv._.map((m: string) => String(m).trim()) : undefined; const project = new PgpmPackage(cwd); @@ -194,7 +192,7 @@ export default async ( argv, prompter, dryRun, - upgradeAll, + interactive, specificModules, moduleName ); @@ -212,6 +210,6 @@ export default async ( throw new Error('You must run this command inside a PGPM module. Use --workspace to upgrade all modules in the workspace.'); } - await upgradeModulesForProject(project, argv, prompter, dryRun, upgradeAll, specificModules); + await upgradeModulesForProject(project, argv, prompter, dryRun, interactive, specificModules); } }; diff --git a/pgpm/cli/src/utils/display.ts b/pgpm/cli/src/utils/display.ts index 5aef77059..f63298cee 100644 --- a/pgpm/cli/src/utils/display.ts +++ b/pgpm/cli/src/utils/display.ts @@ -15,7 +15,7 @@ export const usageText = ` export Export database migrations from existing databases update Update pgpm to the latest version cache Manage cached templates (clean) - upgrade-modules Upgrade installed pgpm modules to latest versions + upgrade Upgrade installed pgpm modules to latest versions (alias: up) Database Administration: kill Terminate database connections and optionally drop databases diff --git a/pgpm/core/package.json b/pgpm/core/package.json index c505fc6df..e8a47a4ec 100644 --- a/pgpm/core/package.json +++ b/pgpm/core/package.json @@ -44,7 +44,7 @@ "@pgsql/types": "^17.6.2", "@types/pg": "^8.16.0", "copyfiles": "^2.4.1", - "inquirerer": "^4.1.2", + "inquirerer": "^4.2.0", "makage": "^0.1.9" }, "dependencies": { @@ -53,7 +53,7 @@ "@pgpmjs/server-utils": "workspace:^", "@pgpmjs/types": "workspace:^", "csv-to-pg": "workspace:^", - "genomic": "^5.1.0", + "genomic": "^5.2.0", "glob": "^13.0.0", "komoji": "^0.7.11", "parse-package-name": "^1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d508e09b2..99972abc0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1243,8 +1243,8 @@ importers: specifier: ^0.8.2 version: 0.8.3 inquirerer: - specifier: ^4.1.2 - version: 4.1.2 + specifier: ^4.2.0 + version: 4.2.1 js-yaml: specifier: ^4.1.0 version: 4.1.1 @@ -1325,8 +1325,8 @@ importers: specifier: ^2.3.3 version: 2.3.5 inquirerer: - specifier: ^4.1.2 - version: 4.1.2 + specifier: ^4.2.0 + version: 4.2.1 js-yaml: specifier: ^3.14.0 version: 3.14.2 @@ -1436,11 +1436,11 @@ importers: specifier: ^0.8.2 version: 0.8.3 genomic: - specifier: ^5.1.0 - version: 5.1.0 + specifier: ^5.2.0 + version: 5.2.1 inquirerer: - specifier: ^4.1.2 - version: 4.1.2 + specifier: ^4.2.0 + version: 4.2.1 js-yaml: specifier: ^4.1.0 version: 4.1.1 @@ -1516,8 +1516,8 @@ importers: specifier: workspace:^ version: link:../../packages/csv-to-pg/dist genomic: - specifier: ^5.1.0 - version: 5.1.0 + specifier: ^5.2.0 + version: 5.2.1 glob: specifier: ^13.0.0 version: 13.0.0 @@ -1556,8 +1556,8 @@ importers: specifier: ^2.4.1 version: 2.4.1 inquirerer: - specifier: ^4.1.2 - version: 4.1.2 + specifier: ^4.2.0 + version: 4.2.1 makage: specifier: ^0.1.9 version: 0.1.9 @@ -5223,6 +5223,9 @@ packages: find-and-require-package-json@0.8.4: resolution: {integrity: sha512-08+BvFbBNU1pvxSMzZnZ01bRGxRyOUAbQG/ttfFrt6G5cFyitmZbtPVKPPz75L/3etyccRq/xxQ1GXUdYB+BkQ==} + find-and-require-package-json@0.8.5: + resolution: {integrity: sha512-I15lQmpt0rxbmDpIgdn1IEWJU/smOLuKrPZFQMnktnz6kn25XgjGNn1ouKLFGsXuDW1Ssd47YPdSPwhSIGuvnA==} + find-up@2.1.0: resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} engines: {node: '>=4'} @@ -5323,8 +5326,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - genomic@5.1.0: - resolution: {integrity: sha512-MRg8bh6iz628qKdQ1TwfO4o1P5khDA+qnqQzYSArvYz5JwcgHUm+/7b6e/8beMr93SD9ZgASHJ1mTIsOttCNkw==} + genomic@5.2.1: + resolution: {integrity: sha512-7+6g0ib9katy7OFUeaPXKiBPFZZSmeDdc3BNUvBvgCASCrseLU9r7a4sBo6319zbX81IBniM1EdIRsWzYKz9Qg==} gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} @@ -5706,8 +5709,8 @@ packages: inquirerer@4.1.1: resolution: {integrity: sha512-OC8UJ7GRxVEFvrT7G5IDIBttjMrqREuax2J2/1ZdmbpJJIaxgydjHYsSmjoyg21XMv+0IU9DEsvBOyJqV1e9dQ==} - inquirerer@4.1.2: - resolution: {integrity: sha512-77alDagsj6hPYadiDalAJDWeR8UqgqjlS7t0IERpDVWx9pmhCPEBAPV59VE1rAlgg4adS1AJoQhNeJP0OVtugg==} + inquirerer@4.2.1: + resolution: {integrity: sha512-DlJzCWrs/GjuUUifhH1Bu+gjOY7DAWlODqESNBb8a9CE1qeiUDqdPrf9qQGy82clMOXDDqQlB3CLbYSKw3z/lA==} invariant@2.2.4: resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} @@ -8211,6 +8214,9 @@ packages: yanse@0.1.10: resolution: {integrity: sha512-sSfkypRMWbsBzUcP3oe9xIhyTBqQRv7XvjICzSkciEPP4xmw/8i0slDqlvhVBz9WsSB40aaJvkJunaddl9viZA==} + yanse@0.1.11: + resolution: {integrity: sha512-70QlVdqwpLdQH1ZqFk5HSAFk6puptS7Q6y79umsucED+uvmbSSVvKMJVDmc4Yt+89JMkf7n4gaR/Ftz6wUZKeA==} + yanse@0.1.9: resolution: {integrity: sha512-AKKZLY+TWENQOYEuQei07b4QFW4hCS6yyAbUiWBCjHOpkhzWUD8EehNAeI/8kwm5pvJQcmX2ojujPywF+DCheA==} @@ -12452,6 +12458,8 @@ snapshots: find-and-require-package-json@0.8.4: {} + find-and-require-package-json@0.8.5: {} + find-up@2.1.0: dependencies: locate-path: 2.0.0 @@ -12547,10 +12555,10 @@ snapshots: function-bind@1.1.2: {} - genomic@5.1.0: + genomic@5.2.1: dependencies: appstash: 0.2.7 - inquirerer: 4.1.2 + inquirerer: 4.2.1 gensync@1.0.0-beta.2: {} @@ -13027,13 +13035,13 @@ snapshots: minimist: 1.2.8 yanse: 0.1.10 - inquirerer@4.1.2: + inquirerer@4.2.1: dependencies: deepmerge: 4.3.1 - find-and-require-package-json: 0.8.4 + find-and-require-package-json: 0.8.5 js-yaml: 4.1.1 minimist: 1.2.8 - yanse: 0.1.10 + yanse: 0.1.11 invariant@2.2.4: dependencies: @@ -16153,6 +16161,8 @@ snapshots: yanse@0.1.10: {} + yanse@0.1.11: {} + yanse@0.1.9: {} yargs-parser@18.1.3: