Skip to content

Studio: Refactor Studio to depend on the CLI for starting sites#2172

Merged
bcotrim merged 19 commits intodev/studio-cli-i2from
stu-1019-studio-refactor-studio-to-depend-on-the-cli-for-starting
Dec 9, 2025
Merged

Studio: Refactor Studio to depend on the CLI for starting sites#2172
bcotrim merged 19 commits intodev/studio-cli-i2from
stu-1019-studio-refactor-studio-to-depend-on-the-cli-for-starting

Conversation

@bcotrim
Copy link
Contributor

@bcotrim bcotrim commented Dec 2, 2025

Related issues

Proposed Changes

  • Add --watch to studio site list
  • Add watcher to Studio to listen to site status changes
  • Implemented createCliServerProcess so Studio can start and stop sites using Studio CLI

Testing Instructions

  • npm install
  • npm start
  • Start a new site using Studio
  • Confirm site is running
  • Stop site using Studio
  • Confirm site is not running
  • Confirm studio site list returns the correct status for the site
  • Start site using Studio CLI
  • Confirm Studio shows the site as running
  • Stop the site using Studio CLI
  • Confirm Studio shows the site as stopped

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

@bcotrim bcotrim self-assigned this Dec 2, 2025
@bcotrim bcotrim force-pushed the stu-1019-studio-refactor-studio-to-depend-on-the-cli-for-starting branch from 034011f to e882445 Compare December 3, 2025 19:25
@bcotrim bcotrim requested a review from a team December 3, 2025 19:28
@bcotrim bcotrim marked this pull request as ready for review December 3, 2025 19:31
@bcotrim bcotrim force-pushed the stu-1019-studio-refactor-studio-to-depend-on-the-cli-for-starting branch from e882445 to d0e4359 Compare December 4, 2025 14:47
Copy link
Contributor

@fredrikekelund fredrikekelund left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, @bcotrim! 👍 Very satisfying to see this come together. It's working nicely in my testing so far.

I left a couple of smaller comments.

I'd also be interested to hear your perspective of this site list --watch approach vs watching the config file for changes and using some mechanism (a version of the studio site status command, perhaps) to check the site status when latestCliPid changes. I don't really have a strong opinion there, but it would be good to have a documented reason for going one way or the other.

Comment on lines +153 to +165
choices: [ 'table', 'json' ],
default: 'table',
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 as 'table' | 'json', argv.watch as boolean );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

					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, argv.watch );

Kind of a nitpick, but that's how we get yargs to provide the correct types for the options.

@@ -391,8 +392,8 @@ async function appBoot() {

app.on( 'quit', () => {
void stopAllServersOnQuit();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will kill all running processes, regardless of whether they were started from the CLI or from the app. We could just accept this behavior, but @nightnei also had a pretty good idea to display a simple modal before quitting the app that says "Do you want to stop the running sites? Yes / Leave them runing"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that idea! I would add a "remember my decision" checkbox. I think that provides enough tools for our users to adjust their experience as it work best for them. We can always revisit in the future.
Should I handle that change in this PR or as part of STU-1078 ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created STU-1112 to tackle this separately

Comment on lines +727 to +731
// CLI-managed sites don't have PHP instance, return cached theme details for Now
// ToDo: Implement logic to fetch theme details using mu-plugin?
if ( ! server.server.php ) {
return server.details.themeDetails;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once we have WP-CLI support, we might also consider using the wp eval command

export function executeCliCommand(
args: string[],
options: ExecuteCliCommandOptions = {}
): CliCommandEventEmitter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
): CliCommandEventEmitter {
): [CliCommandEventEmitter, ChildProcess] {

Optional, but we could return the child process instance from this function, too. It would save us from the dance of having to set the kill function on the EventEmitter

@bcotrim bcotrim changed the base branch from trunk to dev/studio-cli-i2 December 5, 2025 12:25
@bcotrim bcotrim force-pushed the stu-1019-studio-refactor-studio-to-depend-on-the-cli-for-starting branch from 5a0ae52 to dd0994d Compare December 5, 2025 19:04
Copy link
Contributor

@fredrikekelund fredrikekelund left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the e2e/blueprints.test.ts tests are still failing, but I guess that's expected so long as we're in this intermittent state of using the Studio CLI to start sites, but still using Playground CLI to create sites.

Other than that, this looks good! 👍 I left a couple of comments. No huge blockers. The biggest one is probably to remove pm2ProcessMessageSchema in favor of childMessagePm2Schema

cli/logger.ts Outdated
Comment on lines 40 to 46
/**
* Get the underlying ora spinner instance.
* Useful for sharing with other modules that need to update progress.
*/
public get spinner(): Ora {
return this._spinner;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a leftover, right? We could probably change the _spinner prop back to spinner

const bus = await getPm2Bus();

const messageHandler = ( packet: unknown ) => {
const result = pm2ProcessMessageSchema.safeParse( packet );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const result = pm2ProcessMessageSchema.safeParse( packet );
const result = childMessagePm2Schema.safeParse( packet );

Let's reuse childMessagePm2Schema here. We can update the schema definition to also include data.process.name

Comment on lines +106 to +118
// Zod schema for PM2 process messages (IPC messages from child processes)
export const pm2ProcessMessageSchema = z.object( {
process: z.object( {
name: z.string(),
pm_id: z.number(),
} ),
raw: z
.object( {
topic: z.string(),
} )
.passthrough(),
} );
export type Pm2ProcessMessage = z.infer< typeof pm2ProcessMessageSchema >;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Zod schema for PM2 process messages (IPC messages from child processes)
export const pm2ProcessMessageSchema = z.object( {
process: z.object( {
name: z.string(),
pm_id: z.number(),
} ),
raw: z
.object( {
topic: z.string(),
} )
.passthrough(),
} );
export type Pm2ProcessMessage = z.infer< typeof pm2ProcessMessageSchema >;

Let's remove this per my suggestion in cli/lib/pm2-manager.ts

} ),
event: z.string(),
} );
export type Pm2ProcessEvent = z.infer< typeof pm2ProcessEventSchema >;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export type Pm2ProcessEvent = z.infer< typeof pm2ProcessEventSchema >;

We're only using the schema, so let's remove the inferred type.

@@ -391,8 +392,8 @@ async function appBoot() {

app.on( 'quit', () => {
void stopAllServersOnQuit();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created STU-1112 to tackle this separately

};

class CliCommandEventEmitter extends EventEmitter {
export class CliCommandEventEmitter extends EventEmitter {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
export class CliCommandEventEmitter extends EventEmitter {
class CliCommandEventEmitter extends EventEmitter {

No need to export this class, AFAICT

* When true, file watchers should ignore site events to prevent interference
* with the ongoing operation.
*/
hasOngoingOperation = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍

wpVersion?: string;
blueprint?: unknown;
}
logger: Logger< string >,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to address this in the current PR, but the pattern of passing in a logger instance seems to confuse responsibilities. I suggest following up later (maybe after the project launch) by refactoring this file to export a WordPressServerManager class that extends EventEmitter, and emitting the appropriate events (progress, success, error) as part of the startup flow.

Copy link
Contributor

@nightnei nightnei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM and works as expected 👍
I also tested the case when we do something in CLI (when Studio is closed) and then start the Studio, and all sites appears in Studio as started/stopped correctly 👍

@bcotrim
Copy link
Contributor Author

bcotrim commented Dec 9, 2025

@fredrikekelund @nightnei thanks for the reviews!

It looks like the e2e/blueprints.test.ts tests are still failing, but I guess that's expected so long as we're in this intermittent state of using the Studio CLI to start sites, but still using Playground CLI to create sites.

Yes, I think it will be easier to address once we have the full implementation

@bcotrim bcotrim merged commit 8a5bf00 into dev/studio-cli-i2 Dec 9, 2025
3 of 5 checks passed
@bcotrim bcotrim deleted the stu-1019-studio-refactor-studio-to-depend-on-the-cli-for-starting branch December 9, 2025 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants