Skip to content

Commit e6ff03e

Browse files
committed
Add PM2 daemon management commands to CLI
1 parent e879c4e commit e6ff03e

File tree

10 files changed

+502
-3
lines changed

10 files changed

+502
-3
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ vendor/*
105105
# Include the vendored copy of wp-now in our repo
106106
!vendor/wp-now
107107

108+
# CLI npm artifacts
109+
cli/vendor/
110+
cli/package-lock.json
111+
108112
# Build output
109113
dist
110114

cli/commands/pm2/list.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import { __, _n, sprintf } from '@wordpress/i18n';
2+
import Table from 'cli-table3';
3+
import { PreviewCommandLoggerAction as LoggerAction } from 'common/logger-actions';
4+
import { ensureDaemonRunning, listProcesses } from 'cli/lib/pm2-manager';
5+
import { Logger, LoggerError } from 'cli/logger';
6+
import { StudioArgv } from 'cli/types';
7+
8+
export async function runCommand( format: 'table' | 'json' ): Promise< void > {
9+
const logger = new Logger< LoggerAction >();
10+
11+
try {
12+
logger.reportStart( LoggerAction.LOAD, __( 'Loading PM2 processes…' ) );
13+
await ensureDaemonRunning();
14+
const processes = await listProcesses();
15+
16+
if ( processes.length === 0 ) {
17+
logger.reportSuccess( __( 'No processes found' ) );
18+
return;
19+
}
20+
21+
const message = sprintf(
22+
_n( 'Found %d process', 'Found %d processes', processes.length ),
23+
processes.length
24+
);
25+
logger.reportSuccess( message );
26+
27+
if ( format === 'table' ) {
28+
const table = new Table( {
29+
head: [ __( 'Name' ), __( 'PID' ), __( 'Status' ), __( 'Uptime' ) ],
30+
style: {
31+
head: [ 'cyan' ],
32+
border: [ 'grey' ],
33+
},
34+
wordWrap: true,
35+
wrapOnWordBoundary: false,
36+
} );
37+
38+
processes.forEach( ( proc ) => {
39+
const uptimeSeconds = Math.floor( proc.pm2_env.uptime / 1000 );
40+
const uptimeMinutes = Math.floor( uptimeSeconds / 60 );
41+
const uptimeHours = Math.floor( uptimeMinutes / 60 );
42+
const uptimeDisplay =
43+
uptimeHours > 0
44+
? sprintf( '%dh %dm', uptimeHours, uptimeMinutes % 60 )
45+
: sprintf( '%dm %ds', uptimeMinutes, uptimeSeconds % 60 );
46+
47+
table.push( [ proc.name, proc.pid.toString(), proc.status, uptimeDisplay ] );
48+
} );
49+
50+
console.log( table.toString() );
51+
} else {
52+
const jsonOutput = processes.map( ( proc ) => ( {
53+
name: proc.name,
54+
pid: proc.pid,
55+
pm_id: proc.pm_id,
56+
status: proc.status,
57+
uptime: proc.pm2_env.uptime,
58+
} ) );
59+
console.log( JSON.stringify( jsonOutput, null, 2 ) );
60+
}
61+
} catch ( error ) {
62+
if ( error instanceof LoggerError ) {
63+
logger.reportError( error );
64+
} else {
65+
const loggerError = new LoggerError( __( 'Failed to list PM2 processes' ), error );
66+
logger.reportError( loggerError );
67+
}
68+
}
69+
}
70+
71+
export const registerCommand = ( yargs: StudioArgv ) => {
72+
return yargs.command( {
73+
command: 'list',
74+
describe: __( 'List all PM2 processes' ),
75+
builder: ( yargs ) => {
76+
return yargs.option( 'format', {
77+
type: 'string',
78+
choices: [ 'table', 'json' ],
79+
default: 'table',
80+
description: __( 'Output format' ),
81+
} );
82+
},
83+
handler: async ( argv ) => {
84+
await runCommand( argv.format as 'table' | 'json' );
85+
},
86+
} );
87+
};
88+

cli/commands/pm2/start.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { __ } from '@wordpress/i18n';
2+
import { PreviewCommandLoggerAction as LoggerAction } from 'common/logger-actions';
3+
import { startDaemon } from 'cli/lib/pm2-manager';
4+
import { Logger, LoggerError } from 'cli/logger';
5+
import { StudioArgv } from 'cli/types';
6+
7+
export async function runCommand(): Promise< void > {
8+
const logger = new Logger< LoggerAction >();
9+
10+
try {
11+
logger.reportStart( LoggerAction.LOAD, __( 'Starting PM2 daemon…' ) );
12+
await startDaemon();
13+
logger.reportSuccess( __( 'PM2 daemon started' ) );
14+
} catch ( error ) {
15+
if ( error instanceof LoggerError ) {
16+
logger.reportError( error );
17+
} else {
18+
const loggerError = new LoggerError( __( 'Failed to start PM2 daemon' ), error );
19+
logger.reportError( loggerError );
20+
}
21+
}
22+
}
23+
24+
export const registerCommand = ( yargs: StudioArgv ) => {
25+
return yargs.command( {
26+
command: 'start',
27+
describe: __( 'Start the PM2 daemon' ),
28+
handler: async () => {
29+
await runCommand();
30+
},
31+
} );
32+
};

cli/commands/pm2/status.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { __, _n, sprintf } from '@wordpress/i18n';
2+
import { PreviewCommandLoggerAction as LoggerAction } from 'common/logger-actions';
3+
import { isDaemonRunning, listProcesses } from 'cli/lib/pm2-manager';
4+
import { Logger, LoggerError } from 'cli/logger';
5+
import { StudioArgv } from 'cli/types';
6+
7+
export async function runCommand(): Promise< void > {
8+
const logger = new Logger< LoggerAction >();
9+
10+
try {
11+
logger.reportStart( LoggerAction.LOAD, __( 'Checking PM2 daemon status…' ) );
12+
13+
if ( ! isDaemonRunning() ) {
14+
logger.reportSuccess( __( 'PM2 daemon is not running' ) );
15+
return;
16+
}
17+
18+
const processes = await listProcesses( false );
19+
20+
const message = sprintf(
21+
_n( 'PM2 daemon is running (%d process)', 'PM2 daemon is running (%d processes)', processes.length ),
22+
processes.length
23+
);
24+
logger.reportSuccess( message );
25+
26+
if ( processes.length > 0 ) {
27+
processes.forEach( ( proc ) => {
28+
logger.reportKeyValuePair( proc.name, sprintf( 'pid: %d, status: %s', proc.pid, proc.status ) );
29+
} );
30+
}
31+
} catch ( error ) {
32+
if ( error instanceof LoggerError ) {
33+
logger.reportError( error );
34+
} else {
35+
const loggerError = new LoggerError( __( 'Failed to check PM2 daemon status' ), error );
36+
logger.reportError( loggerError );
37+
}
38+
}
39+
}
40+
41+
export const registerCommand = ( yargs: StudioArgv ) => {
42+
return yargs.command( {
43+
command: 'status',
44+
describe: __( 'Check PM2 daemon status and list processes' ),
45+
handler: async () => {
46+
await runCommand();
47+
},
48+
} );
49+
};
50+

cli/commands/pm2/stop.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { __ } from '@wordpress/i18n';
2+
import { PreviewCommandLoggerAction as LoggerAction } from 'common/logger-actions';
3+
import { stopDaemon } from 'cli/lib/pm2-manager';
4+
import { Logger, LoggerError } from 'cli/logger';
5+
import { StudioArgv } from 'cli/types';
6+
7+
export async function runCommand(): Promise< void > {
8+
const logger = new Logger< LoggerAction >();
9+
10+
try {
11+
logger.reportStart( LoggerAction.LOAD, __( 'Stopping PM2 daemon…' ) );
12+
await stopDaemon();
13+
logger.reportSuccess( __( 'PM2 daemon stopped' ) );
14+
} catch ( error ) {
15+
if ( error instanceof LoggerError ) {
16+
logger.reportError( error );
17+
} else {
18+
const loggerError = new LoggerError( __( 'Failed to stop PM2 daemon' ), error );
19+
logger.reportError( loggerError );
20+
}
21+
}
22+
}
23+
24+
export const registerCommand = ( yargs: StudioArgv ) => {
25+
return yargs.command( {
26+
command: 'stop',
27+
describe: __( 'Stop the PM2 daemon' ),
28+
handler: async () => {
29+
await runCommand();
30+
},
31+
} );
32+
};
33+

cli/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { registerCommand as registerCreateCommand } from 'cli/commands/preview/c
88
import { registerCommand as registerDeleteCommand } from 'cli/commands/preview/delete';
99
import { registerCommand as registerListCommand } from 'cli/commands/preview/list';
1010
import { registerCommand as registerUpdateCommand } from 'cli/commands/preview/update';
11+
import { registerCommand as registerPm2ListCommand } from 'cli/commands/pm2/list';
12+
import { registerCommand as registerPm2StartCommand } from 'cli/commands/pm2/start';
13+
import { registerCommand as registerPm2StatusCommand } from 'cli/commands/pm2/status';
14+
import { registerCommand as registerPm2StopCommand } from 'cli/commands/pm2/stop';
1115
import { registerCommand as registerSiteListCommand } from 'cli/commands/site/list';
1216
import { readAppdata } from 'cli/lib/appdata';
1317
import { loadTranslations } from 'cli/lib/i18n';
@@ -52,6 +56,13 @@ async function main() {
5256
registerUpdateCommand( previewYargs );
5357
previewYargs.demandCommand( 1, __( 'You must provide a valid command' ) );
5458
} )
59+
.command( 'pm2', __( 'Manage PM2 daemon' ), ( pm2Yargs ) => {
60+
registerPm2StartCommand( pm2Yargs );
61+
registerPm2StopCommand( pm2Yargs );
62+
registerPm2StatusCommand( pm2Yargs );
63+
registerPm2ListCommand( pm2Yargs );
64+
pm2Yargs.demandCommand( 1, __( 'You must provide a valid command' ) );
65+
} )
5566
.demandCommand( 1, __( 'You must provide a valid command' ) )
5667
.strict();
5768

0 commit comments

Comments
 (0)