diff --git a/packages/playground/blueprints/src/lib/steps/login.ts b/packages/playground/blueprints/src/lib/steps/login.ts index 2e6bd1632a8..7a1cdf20530 100644 --- a/packages/playground/blueprints/src/lib/steps/login.ts +++ b/packages/playground/blueprints/src/lib/steps/login.ts @@ -28,9 +28,12 @@ export type LoginStep = { /** * Logs in to Playground. * Under the hood, this function sets the `PLAYGROUND_AUTO_LOGIN_AS_USER` constant. - * The `0-auto-login.php` mu-plugin uses that constant to log in the user on the first load. + * The `1-auto-login.php` mu-plugin uses that constant to log in the user on the first load. * This step depends on the `@wp-playground/wordpress` package because * the plugin is located in and loaded automatically by the `@wp-playground/wordpress` package. + * + * In the CLI, the `--login` flag injects this constant at boot time via + * `mergeDefinedConstants` so every worker has it before the server starts. */ export const login: StepHandler = async ( playground, @@ -39,6 +42,5 @@ export const login: StepHandler = async ( ) => { progress?.tracker.setCaption(progress?.initialCaption || 'Logging in'); - // TODO: Make defineConstant apply to all workers playground.defineConstant('PLAYGROUND_AUTO_LOGIN_AS_USER', username); }; diff --git a/packages/playground/cli/src/blueprints-v1/blueprints-v1-handler.ts b/packages/playground/cli/src/blueprints-v1/blueprints-v1-handler.ts index b03fb70ef06..d81a0a267fe 100644 --- a/packages/playground/cli/src/blueprints-v1/blueprints-v1-handler.ts +++ b/packages/playground/cli/src/blueprints-v1/blueprints-v1-handler.ts @@ -195,6 +195,7 @@ export class BlueprintsV1Handler { withXdebug: !!this.args.xdebug, nativeInternalDirPath, pathAliases: this.args.pathAliases, + constants: mergeDefinedConstants(this.args), }); await playground.isReady(); return playground; @@ -237,7 +238,7 @@ export class BlueprintsV1Handler { return isBlueprintBundle(resolvedBlueprint) ? resolvedBlueprint : { - login: this.args.login, + login: this.getEffectiveLogin(), ...(resolvedBlueprint || {}), preferredVersions: { php: @@ -252,4 +253,20 @@ export class BlueprintsV1Handler { }, }; } + + /** + * When the user explicitly sets PLAYGROUND_AUTO_LOGIN_AS_USER via + * --define, skip the blueprint login step — the boot-time constant + * from mergeDefinedConstants already handles auto-login and the + * blueprint step would overwrite the custom username with 'admin'. + */ + private getEffectiveLogin(): boolean { + if (!this.args.login) { + return false; + } + if (this.args.define?.['PLAYGROUND_AUTO_LOGIN_AS_USER']) { + return false; + } + return true; + } } diff --git a/packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts b/packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts index c28cd4a159e..e1ceb238a26 100644 --- a/packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts +++ b/packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts @@ -52,6 +52,7 @@ interface WorkerBootRequestHandlerOptions { withMemcached?: boolean; withXdebug?: boolean; pathAliases?: PathAlias[]; + constants?: Record; } /** @@ -162,6 +163,7 @@ export class PlaygroundCliBlueprintV1Worker extends PHPWorker { const requestHandler = await bootRequestHandler({ siteUrl: options.siteUrl, maxPhpInstances: 1, + constants: options.constants, createPhpRuntime: createPhpRuntimeFactory( options, this.fileLockManager! diff --git a/packages/playground/cli/src/defines.ts b/packages/playground/cli/src/defines.ts index 790b6daa2dd..7ec41880774 100644 --- a/packages/playground/cli/src/defines.ts +++ b/packages/playground/cli/src/defines.ts @@ -172,6 +172,10 @@ function mergeConstants( /** * Merge all constants from CLI arguments. * + * When `login` is true, `PLAYGROUND_AUTO_LOGIN_AS_USER` is injected so that + * every PHP worker defines the constant at boot time — not just the single + * worker that would run the blueprint `login` step via the pool proxy. + * * @param args - CLI arguments * @returns Merged constants */ @@ -179,10 +183,17 @@ export function mergeDefinedConstants(args: { define?: Record; 'define-bool'?: Record; 'define-number'?: Record; + login?: boolean; }): Record { - return mergeConstants( + const merged = mergeConstants( args['define'], args['define-bool'], args['define-number'] ); + + if (args.login && !Object.hasOwn(merged, 'PLAYGROUND_AUTO_LOGIN_AS_USER')) { + merged['PLAYGROUND_AUTO_LOGIN_AS_USER'] = 'admin'; + } + + return merged; } diff --git a/packages/playground/cli/tests/merge-defined-constants.spec.ts b/packages/playground/cli/tests/merge-defined-constants.spec.ts new file mode 100644 index 00000000000..2f91963f6d1 --- /dev/null +++ b/packages/playground/cli/tests/merge-defined-constants.spec.ts @@ -0,0 +1,61 @@ +import { mergeDefinedConstants } from '../src/defines'; + +describe('mergeDefinedConstants', () => { + it('should merge string, boolean, and number constants', () => { + const result = mergeDefinedConstants({ + define: { API_KEY: 'secret' }, + 'define-bool': { WP_DEBUG: true }, + 'define-number': { LIMIT: 100 }, + }); + expect(result).toEqual({ + API_KEY: 'secret', + WP_DEBUG: true, + LIMIT: 100, + }); + }); + + it('should return empty object when no constants are provided', () => { + const result = mergeDefinedConstants({}); + expect(result).toEqual({}); + }); + + it('should inject PLAYGROUND_AUTO_LOGIN_AS_USER when login is true', () => { + const result = mergeDefinedConstants({ login: true }); + expect(result).toEqual({ + PLAYGROUND_AUTO_LOGIN_AS_USER: 'admin', + }); + }); + + it('should not inject login constant when login is false', () => { + const result = mergeDefinedConstants({ login: false }); + expect(result).toEqual({}); + }); + + it('should not inject login constant when login is undefined', () => { + const result = mergeDefinedConstants({}); + expect(result).toEqual({}); + }); + + it('should not override explicit PLAYGROUND_AUTO_LOGIN_AS_USER from --define', () => { + const result = mergeDefinedConstants({ + define: { PLAYGROUND_AUTO_LOGIN_AS_USER: 'editor' }, + login: true, + }); + expect(result).toEqual({ + PLAYGROUND_AUTO_LOGIN_AS_USER: 'editor', + }); + }); + + it('should preserve other constants alongside the injected login constant', () => { + const result = mergeDefinedConstants({ + define: { API_KEY: 'secret' }, + 'define-bool': { WP_DEBUG: true }, + login: true, + }); + expect(result).toEqual({ + API_KEY: 'secret', + WP_DEBUG: true, + PLAYGROUND_AUTO_LOGIN_AS_USER: 'admin', + }); + }); +});