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
7 changes: 5 additions & 2 deletions cli/commands/site/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,10 @@ export async function runCommand(
: __( 'Starting WordPress site...' );
logger.reportStart( LoggerAction.START_SITE, startMessage );
try {
await startWordPressServer( siteDetails, { wpVersion: options.wpVersion, blueprint } );
await startWordPressServer( siteDetails, logger, {
wpVersion: options.wpVersion,
blueprint,
} );
logger.reportSuccess( __( 'WordPress site started' ) );

logSiteDetails( siteDetails );
Expand All @@ -216,7 +219,7 @@ export async function runCommand(

logger.reportStart( LoggerAction.START_SITE, __( 'Applying blueprint...' ) );
try {
await runBlueprint( siteDetails, { wpVersion: options.wpVersion, blueprint } );
await runBlueprint( siteDetails, logger, { wpVersion: options.wpVersion, blueprint } );
logger.reportSuccess( __( 'Blueprint applied successfully' ) );
} catch ( error ) {
throw new LoggerError( __( 'Failed to apply blueprint' ), error );
Expand Down
147 changes: 102 additions & 45 deletions cli/commands/site/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,30 @@ import { SiteCommandLoggerAction as LoggerAction } from 'common/logger-actions';
import { getSiteUrl, readAppdata, type SiteData } from 'cli/lib/appdata';
import { connect, disconnect } from 'cli/lib/pm2-manager';
import { getColumnWidths, getPrettyPath } from 'cli/lib/utils';
import { isServerRunning } from 'cli/lib/wordpress-server-manager';
import { isServerRunning, subscribeSiteEvents } from 'cli/lib/wordpress-server-manager';
import { Logger, LoggerError } from 'cli/logger';
import { StudioArgv } from 'cli/types';

interface SiteListEntry {
id: string;
status: string;
name: string;
path: string;
url: string;
}

async function getSiteListData( sites: SiteData[] ) {
async function getSiteListData( sites: SiteData[] ): Promise< SiteListEntry[] > {
const result: SiteListEntry[] = [];

for await ( const site of sites ) {
const isOnline = await isServerRunning( site.id );
const status = isOnline ? `🟢 ${ __( 'Online' ) }` : `🔴 ${ __( 'Offline' ) }`;
const processInfo = await isServerRunning( site.id );
const isReady =
processInfo && site.latestCliPid !== undefined && processInfo.pid === site.latestCliPid;
const status = isReady ? `🟢 ${ __( 'Online' ) }` : `🔴 ${ __( 'Offline' ) }`;
const url = getSiteUrl( site );

result.push( {
id: site.id,
status,
name: site.name,
path: getPrettyPath( site.path ),
Expand All @@ -34,60 +38,107 @@ async function getSiteListData( sites: SiteData[] ) {
return result;
}

function displaySiteList( sitesData: SiteListEntry[], format: 'table' | 'json' ): void {
if ( format === 'table' ) {
const colWidths = getColumnWidths( [ 0.1, 0.2, 0.3, 0.4 ] );

const table = new Table( {
head: [ __( 'Status' ), __( 'Name' ), __( 'Path' ), __( 'URL' ) ],
wordWrap: true,
wrapOnWordBoundary: false,
colWidths,
style: {
head: [],
border: [],
},
} );

table.push(
...sitesData.map( ( site ) => [
site.status,
site.name,
site.path,
{ href: new URL( site.url ).toString(), content: site.url },
] )
);

console.log( table.toString() );
} else {
console.log( JSON.stringify( sitesData, null, 2 ) );
}
}

const logger = new Logger< LoggerAction >();

export async function runCommand( format: 'table' | 'json' ): Promise< void > {
export async function runCommand( format: 'table' | 'json', watch: boolean ): Promise< void > {
const handleTermination = () => {
disconnect();
process.exit( 0 );
};
process.on( 'SIGTERM', handleTermination );
process.on( 'SIGHUP', handleTermination );
process.on( 'disconnect', handleTermination );

try {
logger.reportStart( LoggerAction.LOAD_SITES, __( 'Loading sites…' ) );
const appdata = await readAppdata();

if ( appdata.sites.length === 0 ) {
logger.reportSuccess( __( 'No sites found' ) );
return;
if ( ! watch ) {
return;
}
} else {
const sitesMessage = sprintf(
_n( 'Found %d site', 'Found %d sites', appdata.sites.length ),
appdata.sites.length
);
logger.reportSuccess( sitesMessage );
}

const sitesMessage = sprintf(
_n( 'Found %d site', 'Found %d sites', appdata.sites.length ),
appdata.sites.length
);

logger.reportSuccess( sitesMessage );

logger.reportStart( LoggerAction.START_DAEMON, __( 'Connecting to process daemon...' ) );
await connect();
logger.reportSuccess( __( 'Connected to process daemon' ) );

const sitesData = await getSiteListData( appdata.sites );
displaySiteList( sitesData, format );

if ( watch ) {
for ( const site of sitesData ) {
const isOnline = site.status.includes( 'Online' );
const payload = {
siteId: site.id,
status: isOnline ? 'running' : 'stopped',
url: site.url,
};
logger.reportKeyValuePair( 'site-status', JSON.stringify( payload ) );
}

if ( format === 'table' ) {
const colWidths = getColumnWidths( [ 0.1, 0.2, 0.3, 0.4 ] );

const table = new Table( {
head: [ __( 'Status' ), __( 'Name' ), __( 'Path' ), __( 'URL' ) ],
wordWrap: true,
wrapOnWordBoundary: false,
colWidths,
style: {
head: [],
border: [],
await subscribeSiteEvents(
async ( { siteId } ) => {
console.clear();
const freshAppdata = await readAppdata();
const freshSitesData = await getSiteListData( freshAppdata.sites );
displaySiteList( freshSitesData, format );

const site = freshSitesData.find( ( s ) => s.id === siteId );
if ( site ) {
const isOnline = site.status.includes( 'Online' );
const payload = {
siteId,
status: isOnline ? 'running' : 'stopped',
url: site.url,
};
logger.reportKeyValuePair( 'site-status', JSON.stringify( payload ) );
}
},
} );

table.push(
...sitesData.map( ( site ) => [
site.status,
site.name,
site.path,
{ href: new URL( site.url ).toString(), content: site.url },
] )
{ debounceMs: 500 }
);

console.log( table.toString() );
} else {
console.log( JSON.stringify( sitesData, null, 2 ) );
}
} finally {
disconnect();
if ( ! watch ) {
disconnect();
}
}
}

Expand All @@ -96,16 +147,22 @@ export const registerCommand = ( yargs: StudioArgv ) => {
command: 'list',
describe: __( 'List local sites' ),
builder: ( yargs ) => {
return yargs.option( 'format', {
type: 'string',
choices: [ 'table', 'json' ],
default: 'table',
description: __( 'Output format' ),
} );
return yargs
.option( 'format', {
type: 'string',
choices: [ 'table', 'json' ] as const,
default: 'table' as const,
description: __( 'Output format' ),
} )
.option( 'watch', {
type: 'boolean',
default: false,
description: __( 'Watch for site status changes and update the list in real-time' ),
} );
},
handler: async ( argv ) => {
try {
await runCommand( argv.format as 'table' | 'json' );
await runCommand( argv.format, argv.watch );
} catch ( error ) {
if ( error instanceof LoggerError ) {
logger.reportError( error );
Expand Down
2 changes: 1 addition & 1 deletion cli/commands/site/set-domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export async function runCommand( sitePath: string, domainName: string ): Promis
await stopWordPressServer( site.id );
await setupCustomDomain( site, logger );
logger.reportStart( LoggerAction.START_SITE, __( 'Restarting site...' ) );
await startWordPressServer( site );
await startWordPressServer( site, logger );
logger.reportSuccess( __( 'Site restarted' ) );
}
} finally {
Expand Down
2 changes: 1 addition & 1 deletion cli/commands/site/set-https.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export async function runCommand( sitePath: string, enableHttps: boolean ): Prom
if ( runningProcess ) {
logger.reportStart( LoggerAction.START_SITE, __( 'Restarting site...' ) );
await stopWordPressServer( site.id );
await startWordPressServer( site );
await startWordPressServer( site, logger );
logger.reportSuccess( __( 'Site restarted' ) );
}
} finally {
Expand Down
2 changes: 1 addition & 1 deletion cli/commands/site/set-php-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export async function runCommand(
if ( runningProcess ) {
logger.reportStart( LoggerAction.START_SITE, __( 'Restarting site...' ) );
await stopWordPressServer( site.id );
await startWordPressServer( site );
await startWordPressServer( site, logger );
logger.reportSuccess( __( 'Site restarted' ) );
}
} finally {
Expand Down
2 changes: 1 addition & 1 deletion cli/commands/site/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export async function runCommand( sitePath: string, skipBrowser = false ): Promi

logger.reportStart( LoggerAction.START_SITE, __( 'Starting WordPress site...' ) );
try {
const processDesc = await startWordPressServer( site );
const processDesc = await startWordPressServer( site, logger );

logger.reportSuccess( __( 'WordPress site started' ) );
if ( processDesc.pid ) {
Expand Down
4 changes: 4 additions & 0 deletions cli/commands/site/tests/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { connect, disconnect } from 'cli/lib/pm2-manager';
import { logSiteDetails, openSiteInBrowser, setupCustomDomain } from 'cli/lib/site-utils';
import { isSqliteIntegrationAvailable, installSqliteIntegration } from 'cli/lib/sqlite-integration';
import { runBlueprint, startWordPressServer } from 'cli/lib/wordpress-server-manager';
import { Logger } from 'cli/logger';

jest.mock( 'common/lib/fs-utils' );
jest.mock( 'common/lib/port-finder', () => ( {
Expand Down Expand Up @@ -295,6 +296,7 @@ describe( 'CLI: studio site create', () => {
);
expect( startWordPressServer ).toHaveBeenCalledWith(
expect.anything(),
expect.any( Logger ),
expect.objectContaining( {
blueprint: expect.objectContaining( {
steps: expect.arrayContaining( [
Expand Down Expand Up @@ -487,6 +489,7 @@ describe( 'CLI: studio site create', () => {
expect( validateBlueprintData ).toHaveBeenCalled();
expect( startWordPressServer ).toHaveBeenCalledWith(
expect.anything(),
expect.any( Logger ),
expect.objectContaining( {
blueprint: expect.any( Object ),
} )
Expand All @@ -507,6 +510,7 @@ describe( 'CLI: studio site create', () => {

expect( startWordPressServer ).toHaveBeenCalledWith(
expect.anything(),
expect.any( Logger ),
expect.objectContaining( {
blueprint: expect.objectContaining( {
steps: expect.arrayContaining( [
Expand Down
12 changes: 7 additions & 5 deletions cli/commands/site/tests/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe( 'CLI: studio site list', () => {

const { runCommand } = await import( '../list' );

await expect( runCommand( 'table' ) ).rejects.toThrow( 'Failed to read appdata' );
await expect( runCommand( 'table', false ) ).rejects.toThrow( 'Failed to read appdata' );
expect( disconnect ).toHaveBeenCalled();
} );
} );
Expand All @@ -65,7 +65,7 @@ describe( 'CLI: studio site list', () => {
const consoleSpy = jest.spyOn( console, 'log' ).mockImplementation();
const { runCommand } = await import( '../list' );

await runCommand( 'table' );
await runCommand( 'table', false );

expect( readAppdata ).toHaveBeenCalled();
expect( consoleSpy ).toHaveBeenCalled();
Expand All @@ -78,18 +78,20 @@ describe( 'CLI: studio site list', () => {
const consoleSpy = jest.spyOn( console, 'log' ).mockImplementation();
const { runCommand } = await import( '../list' );

await runCommand( 'json' );
await runCommand( 'json', false );

expect( consoleSpy ).toHaveBeenCalledWith(
JSON.stringify(
[
{
id: 'site-1',
status: '🔴 Offline',
name: 'Test Site 1',
path: '/path/to/site1',
url: 'http://localhost:8080',
},
{
id: 'site-2',
status: '🔴 Offline',
name: 'Test Site 2',
path: '/path/to/site2',
Expand All @@ -110,7 +112,7 @@ describe( 'CLI: studio site list', () => {

const { runCommand } = await import( '../list' );

await runCommand( 'table' );
await runCommand( 'table', false );

expect( readAppdata ).toHaveBeenCalled();
expect( disconnect ).toHaveBeenCalled();
Expand All @@ -120,7 +122,7 @@ describe( 'CLI: studio site list', () => {
const consoleSpy = jest.spyOn( console, 'log' ).mockImplementation();
const { runCommand } = await import( '../list' );

await runCommand( 'json' );
await runCommand( 'json', false );

expect( consoleSpy ).toHaveBeenCalledWith( expect.stringContaining( 'my-site.wp.local' ) );
expect( disconnect ).toHaveBeenCalled();
Expand Down
5 changes: 3 additions & 2 deletions cli/commands/site/tests/set-domain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
startWordPressServer,
stopWordPressServer,
} from 'cli/lib/wordpress-server-manager';
import { Logger } from 'cli/logger';

jest.mock( 'cli/lib/appdata', () => ( {
...jest.requireActual( 'cli/lib/appdata' ),
Expand Down Expand Up @@ -172,8 +173,8 @@ describe( 'CLI: studio site set-domain', () => {

expect( isServerRunning ).toHaveBeenCalledWith( testSite.id );
expect( stopWordPressServer ).toHaveBeenCalledWith( testSite.id );
expect( setupCustomDomain ).toHaveBeenCalledWith( testSite, expect.any( Object ) );
expect( startWordPressServer ).toHaveBeenCalledWith( testSite );
expect( setupCustomDomain ).toHaveBeenCalledWith( testSite, expect.any( Logger ) );
expect( startWordPressServer ).toHaveBeenCalledWith( testSite, expect.any( Logger ) );
expect( disconnect ).toHaveBeenCalled();
} );

Expand Down
6 changes: 5 additions & 1 deletion cli/commands/site/tests/set-https.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
startWordPressServer,
stopWordPressServer,
} from 'cli/lib/wordpress-server-manager';
import { Logger } from 'cli/logger';

jest.mock( 'cli/lib/appdata', () => ( {
...jest.requireActual( 'cli/lib/appdata' ),
Expand Down Expand Up @@ -194,7 +195,10 @@ describe( 'CLI: studio site set-https', () => {

expect( isServerRunning ).toHaveBeenCalledWith( mockSiteData.id );
expect( stopWordPressServer ).toHaveBeenCalledWith( mockSiteData.id );
expect( startWordPressServer ).toHaveBeenCalledWith( expect.any( Object ) );
expect( startWordPressServer ).toHaveBeenCalledWith(
expect.any( Object ),
expect.any( Logger )
);
expect( disconnect ).toHaveBeenCalled();
} );

Expand Down
Loading
Loading