Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion __tests__/commands/phpmyadmin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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( () => {
Expand Down Expand Up @@ -91,5 +91,19 @@ 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, true );
const printOpenUrl = jest.spyOn( printCmd, 'openUrl' );
printOpenUrl.mockReset();
const consoleSpy = jest.spyOn( console, 'log' );
try {
await printCmd.run( { print: true } );
expect( printOpenUrl ).not.toHaveBeenCalled();
expect( consoleSpy ).toHaveBeenCalledWith( 'http://test-url.com' );
} finally {
consoleSpy.mockRestore();
}
} );
} );
} );
37 changes: 27 additions & 10 deletions src/bin/vip-db-phpmyadmin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `
Expand All @@ -40,16 +44,29 @@ void command( {
requiredArgs: 0,
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 }: { 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,
silent,
}: { app: App; env: AppEnvironment; print: boolean; silent: 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, silent );
await cmd.run( { print: printUrl } );

await trackerFn( 'success' );
} );
await trackerFn( 'success' );
}
);
102 changes: 81 additions & 21 deletions src/commands/phpmyadmin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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';

Expand Down Expand Up @@ -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 {
Expand All @@ -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 ): Promise< void > {
this.silent = silent;

public async run( { print = false } = {} ): Promise< void > {
if ( ! this.app.id ) {
exit.withError( 'No app was specified' );
}
Expand All @@ -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 {
Expand All @@ -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.'
);
}

Expand All @@ -226,11 +280,17 @@ 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 }` );
}

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.' );
}
}
}
Loading