From 4b6fa69d711ff0618dddce88aa149489439294e7 Mon Sep 17 00:00:00 2001 From: Spenser Hale Date: Thu, 5 Mar 2026 16:51:53 -0800 Subject: [PATCH 1/2] feat: add print option to phpMyAdmin command to output URL to stdout --- __tests__/commands/phpmyadmin.ts | 11 +++++++++++ npm-shrinkwrap.json | 3 --- src/bin/vip-db-phpmyadmin.ts | 31 +++++++++++++++++++++---------- src/commands/phpmyadmin.ts | 12 +++++++++--- 4 files changed, 41 insertions(+), 16 deletions(-) diff --git a/__tests__/commands/phpmyadmin.ts b/__tests__/commands/phpmyadmin.ts index c4beebaa5..08a0fa029 100644 --- a/__tests__/commands/phpmyadmin.ts +++ b/__tests__/commands/phpmyadmin.ts @@ -91,5 +91,16 @@ describe( 'commands/PhpMyAdminCommand', () => { } ); expect( openUrl ).toHaveBeenCalledWith( 'http://test-url.com' ); } ); + + it( 'should print the URL to stdout instead of opening browser when print option is set', async () => { + const printCmd = new PhpMyAdminCommand( app, env, tracker ); + const printOpenUrl = jest.spyOn( printCmd, 'openUrl' ); + printOpenUrl.mockReset(); + const consoleSpy = jest.spyOn( console, 'log' ); + await printCmd.run( { print: true } ); + expect( printOpenUrl ).not.toHaveBeenCalled(); + expect( consoleSpy ).toHaveBeenCalledWith( 'http://test-url.com' ); + consoleSpy.mockRestore(); + } ); } ); } ); diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 0eb6b74e8..d3d6d6cfe 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -11691,7 +11691,6 @@ "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" @@ -11707,7 +11706,6 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" @@ -11717,7 +11715,6 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" diff --git a/src/bin/vip-db-phpmyadmin.ts b/src/bin/vip-db-phpmyadmin.ts index c273190a6..eccac3782 100755 --- a/src/bin/vip-db-phpmyadmin.ts +++ b/src/bin/vip-db-phpmyadmin.ts @@ -18,6 +18,10 @@ const examples = [ description: "Generate access to a read-only phpMyAdmin web interface for the environment's database.", }, + { + usage: 'vip @example-app.develop db phpmyadmin --print', + description: 'Print the phpMyAdmin URL to stdout instead of opening it in a browser.', + }, ]; const appQuery = ` @@ -40,16 +44,23 @@ void command( { requiredArgs: 0, usage: 'vip db phpmyadmin', } ) + .option( 'print', 'Print the phpMyAdmin URL to stdout instead of opening it in a browser.' ) .examples( examples ) - .argv( process.argv, async ( arg: string[], { app, env }: { app: App; env: AppEnvironment } ) => { - const trackerFn = makeCommandTracker( 'phpmyadmin', { - app: app.id, - env: env.uniqueLabel, - } ); - await trackerFn( 'execute' ); + .argv( + process.argv, + async ( + arg: string[], + { app, env, print: printUrl }: { app: App; env: AppEnvironment; print: boolean } + ) => { + const trackerFn = makeCommandTracker( 'phpmyadmin', { + app: app.id, + env: env.uniqueLabel, + } ); + await trackerFn( 'execute' ); - const cmd = new PhpMyAdminCommand( app, env, trackerFn ); - await cmd.run(); + const cmd = new PhpMyAdminCommand( app, env, trackerFn ); + await cmd.run( { print: printUrl } ); - await trackerFn( 'success' ); - } ); + await trackerFn( 'success' ); + } + ); diff --git a/src/commands/phpmyadmin.ts b/src/commands/phpmyadmin.ts index 8b63d2a61..0e71ef67a 100644 --- a/src/commands/phpmyadmin.ts +++ b/src/commands/phpmyadmin.ts @@ -167,7 +167,7 @@ export class PhpMyAdminCommand { } } - public async run( silent = false ): Promise< void > { + public async run( { silent = false, print = false } = {} ): Promise< void > { this.silent = silent; if ( ! this.app.id ) { @@ -229,8 +229,14 @@ export class PhpMyAdminCommand { exit.withError( `Failed to generate PhpMyAdmin URL: ${ error.message }` ); } - void this.openUrl( url ); this.stopProgressTracker(); - this.log( 'PhpMyAdmin is opened in your default browser.' ); + + if ( print ) { + // Output only the URL to stdout for scripting/automation use + console.log( url ); + } else { + void this.openUrl( url ); + this.log( 'PhpMyAdmin is opened in your default browser.' ); + } } } From 07d08c5b2f0685517b7a44c94c2f6d751422c192 Mon Sep 17 00:00:00 2001 From: Volodymyr Kolesnykov Date: Wed, 18 Mar 2026 14:10:18 +0200 Subject: [PATCH 2/2] feat: add `--silent` to `vip db phpmyadmin` --- __tests__/commands/phpmyadmin.ts | 15 +++-- src/bin/vip-db-phpmyadmin.ts | 10 +++- src/commands/phpmyadmin.ts | 94 +++++++++++++++++++++++++------- 3 files changed, 91 insertions(+), 28 deletions(-) diff --git a/__tests__/commands/phpmyadmin.ts b/__tests__/commands/phpmyadmin.ts index 08a0fa029..98383aa19 100644 --- a/__tests__/commands/phpmyadmin.ts +++ b/__tests__/commands/phpmyadmin.ts @@ -63,7 +63,7 @@ describe( 'commands/PhpMyAdminCommand', () => { const app = { id: 123 }; const env = { id: 456, jobs: [] }; const tracker = jest.fn() as CommandTracker; - const cmd = new PhpMyAdminCommand( app, env, tracker ); + const cmd = new PhpMyAdminCommand( app, env, tracker, true ); const openUrl = jest.spyOn( cmd, 'openUrl' ); beforeEach( () => { @@ -93,14 +93,17 @@ describe( 'commands/PhpMyAdminCommand', () => { } ); it( 'should print the URL to stdout instead of opening browser when print option is set', async () => { - const printCmd = new PhpMyAdminCommand( app, env, tracker ); + const printCmd = new PhpMyAdminCommand( app, env, tracker, true ); const printOpenUrl = jest.spyOn( printCmd, 'openUrl' ); printOpenUrl.mockReset(); const consoleSpy = jest.spyOn( console, 'log' ); - await printCmd.run( { print: true } ); - expect( printOpenUrl ).not.toHaveBeenCalled(); - expect( consoleSpy ).toHaveBeenCalledWith( 'http://test-url.com' ); - consoleSpy.mockRestore(); + try { + await printCmd.run( { print: true } ); + expect( printOpenUrl ).not.toHaveBeenCalled(); + expect( consoleSpy ).toHaveBeenCalledWith( 'http://test-url.com' ); + } finally { + consoleSpy.mockRestore(); + } } ); } ); } ); diff --git a/src/bin/vip-db-phpmyadmin.ts b/src/bin/vip-db-phpmyadmin.ts index eccac3782..d52dd9b99 100755 --- a/src/bin/vip-db-phpmyadmin.ts +++ b/src/bin/vip-db-phpmyadmin.ts @@ -45,12 +45,18 @@ void command( { usage: 'vip db phpmyadmin', } ) .option( 'print', 'Print the phpMyAdmin URL to stdout instead of opening it in a browser.' ) + .option( 'silent', 'Do not print any output to the console.' ) .examples( examples ) .argv( process.argv, async ( arg: string[], - { app, env, print: printUrl }: { app: App; env: AppEnvironment; print: boolean } + { + app, + env, + print: printUrl, + silent, + }: { app: App; env: AppEnvironment; print: boolean; silent: boolean } ) => { const trackerFn = makeCommandTracker( 'phpmyadmin', { app: app.id, @@ -58,7 +64,7 @@ void command( { } ); await trackerFn( 'execute' ); - const cmd = new PhpMyAdminCommand( app, env, trackerFn ); + const cmd = new PhpMyAdminCommand( app, env, trackerFn, silent ); await cmd.run( { print: printUrl } ); await trackerFn( 'success' ); diff --git a/src/commands/phpmyadmin.ts b/src/commands/phpmyadmin.ts index 0e71ef67a..e0684f1f7 100644 --- a/src/commands/phpmyadmin.ts +++ b/src/commands/phpmyadmin.ts @@ -4,6 +4,7 @@ import { ApolloClient } from '@apollo/client/core'; import chalk from 'chalk'; import gql from 'graphql-tag'; +import { setTimeout } from 'node:timers/promises'; /** * Internal dependencies @@ -21,7 +22,7 @@ import API, { enableGlobalGraphQLErrorHandling, } from '../lib/api'; import * as exit from '../lib/cli/exit'; -import { ProgressTracker } from '../lib/cli/progress'; +import { ProgressTracker, StepConstructorParam } from '../lib/cli/progress'; import { CommandTracker } from '../lib/tracker'; import { pollUntil } from '../lib/utils'; @@ -116,30 +117,83 @@ async function getPhpMyAdminStatus( appId: number, envId: number ): Promise< str return resp.data?.app?.environments?.[ 0 ]?.phpMyAdminStatus?.status ?? ''; } +class MyProgressTracker extends ProgressTracker { + public constructor( + steps: StepConstructorParam[], + private silent: boolean + ) { + super( steps ); + } + + public setSilent( silent: boolean ): void { + this.silent = silent; + } + + public print(): void { + if ( ! this.silent ) { + super.print(); + } + } + + public startPrinting( prePrintCallback?: () => unknown ): void { + if ( ! this.silent ) { + super.startPrinting( prePrintCallback ); + } + } + + public stopPrinting(): void { + if ( ! this.silent ) { + super.stopPrinting(); + } + } + + public stepRunning( stepId: string, additionalInfo?: string | string[] ): void { + if ( ! this.silent ) { + super.stepRunning( stepId, additionalInfo ); + } + } + + public stepSuccess( stepId: string, additionalInfo?: string | string[] ): void { + if ( ! this.silent ) { + super.stepSuccess( stepId, additionalInfo ); + } + } + + public stepFailed( stepId: string, additionalInfo?: string | string[] ): void { + if ( ! this.silent ) { + super.stepFailed( stepId, additionalInfo ); + } + } +} + export class PhpMyAdminCommand { private silent?: boolean; private readonly steps = { ENABLE: 'enable', GENERATE: 'generate', }; - private readonly progressTracker: ProgressTracker; + private readonly progressTracker: MyProgressTracker; constructor( private readonly app: App, private readonly env: AppEnvironment, - private readonly track: CommandTracker = async () => {} + private readonly track: CommandTracker = async () => {}, + silent = false ) { - this.progressTracker = new ProgressTracker( [ - { id: this.steps.ENABLE, name: 'Enabling PHPMyAdmin for this environment' }, - { id: this.steps.GENERATE, name: 'Generating access link' }, - ] ); + this.silent = silent; + this.progressTracker = new MyProgressTracker( + [ + { id: this.steps.ENABLE, name: 'Enabling phpMyAdmin for this environment' }, + { id: this.steps.GENERATE, name: 'Generating access link' }, + ], + silent + ); } private log( msg: string ): void { - if ( this.silent ) { - return; + if ( ! this.silent ) { + console.log( msg ); } - console.log( msg ); } private stopProgressTracker(): void { @@ -163,13 +217,11 @@ export class PhpMyAdminCommand { await pollUntil( this.getStatus.bind( this ), 1000, ( sts: string ) => sts === 'running' ); // Additional 30s for LB routing to be updated - await new Promise( resolve => setTimeout( resolve, 30000 ) ); + await setTimeout( 30_000 ); } } - public async run( { silent = false, print = false } = {} ): Promise< void > { - this.silent = silent; - + public async run( { print = false } = {} ): Promise< void > { if ( ! this.app.id ) { exit.withError( 'No app was specified' ); } @@ -178,9 +230,11 @@ export class PhpMyAdminCommand { exit.withError( 'No environment was specified' ); } - const message = - 'Note: PHPMyAdmin sessions are read-only. If you run a query that writes to DB, it will fail.'; - console.log( chalk.yellow( message ) ); + if ( ! this.silent ) { + const message = + 'Note: PHPMyAdmin sessions are read-only. If you run a query that writes to DB, it will fail.'; + console.log( chalk.yellow( message ) ); + } this.progressTracker.startPrinting(); try { @@ -206,7 +260,7 @@ export class PhpMyAdminCommand { } exit.withError( - 'Failed to enable PhpMyAdmin. Please try again. If the problem persists, please contact support.' + 'Failed to enable phpMyAdmin. Please try again. If the problem persists, please contact support.' ); } @@ -226,7 +280,7 @@ export class PhpMyAdminCommand { stack: error.stack, } ); this.stopProgressTracker(); - exit.withError( `Failed to generate PhpMyAdmin URL: ${ error.message }` ); + exit.withError( `Failed to generate phpMyAdmin URL: ${ error.message }` ); } this.stopProgressTracker(); @@ -236,7 +290,7 @@ export class PhpMyAdminCommand { console.log( url ); } else { void this.openUrl( url ); - this.log( 'PhpMyAdmin is opened in your default browser.' ); + this.log( 'phpMyAdmin is opened in your default browser.' ); } } }