Skip to content

CLI: Set PLAYGROUND_AUTO_LOGIN_AS_USER at boot time for all workers when --login is used#3485

Open
ashfame wants to merge 5 commits intotrunkfrom
fix/cli-login-constant-all-workers
Open

CLI: Set PLAYGROUND_AUTO_LOGIN_AS_USER at boot time for all workers when --login is used#3485
ashfame wants to merge 5 commits intotrunkfrom
fix/cli-login-constant-all-workers

Conversation

@ashfame
Copy link
Copy Markdown
Member

@ashfame ashfame commented Apr 13, 2026

Motivation for the change, related issues

When the CLI runs server --login, the blueprint login step calls
playground.defineConstant('PLAYGROUND_AUTO_LOGIN_AS_USER', username) once
through the pool proxy. There was an existing TODO acknowledging a potential
issue with this:

// TODO: Make defineConstant apply to all workers
playground.defineConstant('PLAYGROUND_AUTO_LOGIN_AS_USER', username);

In practice, all CLI workers share a native /internal/ directory
(nativeInternalDirPath), so the consts.json file written by
defineConstant is visible across workers and the login flakiness is hard
to reproduce manually. However, the constant is set during blueprint step
execution
rather than at PHP boot time. Moving it into the boot-time
constants path:

  • Makes the login constant available before the server starts accepting
    requests, rather than relying on the shared filesystem being written to
    during blueprint execution
  • Ensures the constant survives PHP runtime rotation cleanly (every ~400
    requests a new PHP instance is created via createPhp, which applies
    options.constants — the proper boot-time path)
  • Resolves the TODO in login.ts

Additionally, the V1 handler's per-worker bootRequestHandler was not
forwarding --define / --define-bool / --define-number constants at
all. Those constants only reached the single worker that the pool proxy
selected for bootWordPress. This PR fixes that gap so every V1 worker
gets CLI-defined constants at PHP instance creation time.

Implementation details

packages/playground/cli/src/defines.ts

  • mergeDefinedConstants now accepts an optional login boolean
  • When login is true, injects PLAYGROUND_AUTO_LOGIN_AS_USER: 'admin'
    (matching the blueprint login step's default)
  • Respects an explicit --define PLAYGROUND_AUTO_LOGIN_AS_USER override

packages/playground/cli/src/blueprints-v1/blueprints-v1-handler.ts

  • Passes constants: mergeDefinedConstants(this.args) to each worker's
    bootRequestHandler, so every worker gets CLI-defined constants at PHP
    instance creation time

packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts

  • Adds constants to WorkerBootRequestHandlerOptions
  • Forwards constants to the @wp-playground/wordpress bootRequestHandler,
    which applies them in createPhp for every PHP instance (including after
    runtime rotation)

packages/playground/blueprints/src/lib/steps/login.ts

  • Removes the resolved TODO comment
  • Fixes the mu-plugin filename reference from 0-auto-login.php to
    1-auto-login.php

V2 already passes mergeDefinedConstants(this.args) per-worker in
bootWorker, so the defines.ts change is sufficient there.

Testing Instructions (or ideally a Blueprint)

Unit tests (added in this PR):

npx nx test-playground-cli playground-cli --testFile=merge-defined-constants.spec.ts

Covers: login=true injects the constant, login=false/undefined does not,
explicit --define override is preserved, and coexistence with other
constants.

Existing tests (verify no regressions):

npx nx test-playground-cli playground-cli --testFile=parse-define-arguments.spec.ts
npx nx typecheck playground-cli
npx nx lint playground-cli

Code review notes:

The login flakiness from the missing constant has been observed in E2E tests
but is difficult to reproduce manually, because CLI workers share a native
/internal/ directory and the consts.json file written by defineConstant
is visible across all workers. The primary value of this change is
architectural correctness: constants now flow through the proper boot-time
path (bootRequestHandlercreatePhp) rather than relying on a
shared-filesystem side effect of the blueprint login step. This also fixes
--define constant propagation to all V1 workers, which had the same gap.

ashfame added 3 commits April 13, 2026 23:00
…login is set

When the CLI's --login flag is true, inject the PLAYGROUND_AUTO_LOGIN_AS_USER
constant into the merged constants so it flows through the boot-time path
alongside other --define constants. This ensures the auto-login constant is
set before the server starts accepting requests, rather than relying on the
blueprint login step which runs on a single pool-proxied worker.

An explicit --define PLAYGROUND_AUTO_LOGIN_AS_USER override is respected.

Made-with: Cursor
Previously, mergeDefinedConstants was only passed to bootWordPress which
runs on a single pool-proxied worker. While the shared nativeInternalDirPath
means consts.json is visible across workers, feeding constants through
bootRequestHandler ensures they are applied at PHP instance creation time
and survive runtime rotation without relying on file-level sharing.

This also fixes --define/--define-bool/--define-number for V1 workers that
were not the one selected by the pool proxy for bootWordPress.

Made-with: Cursor
Covers: login=true injects the constant, login=false/undefined does not,
explicit --define override is preserved, and coexistence with other constants.

Made-with: Cursor
@ashfame ashfame requested review from a team, Copilot and zaerl April 13, 2026 17:19

This comment was marked as low quality.

@ashfame ashfame self-assigned this Apr 13, 2026
ashfame added 2 commits April 14, 2026 00:11
The TODO to make defineConstant apply to all workers is addressed by
injecting the constant at boot time via mergeDefinedConstants. Also
corrects the mu-plugin filename from 0-auto-login.php to 1-auto-login.php.

Made-with: Cursor
…ia --define

When the user provides a custom auto-login username via --define, the
boot-time constant from mergeDefinedConstants already handles auto-login.
The blueprint login step compiles login:true to username:'admin' and calls
defineConstant which would overwrite the custom value in consts.json.
Skip the step entirely so the user's --define value is respected.

Made-with: Cursor
Copy link
Copy Markdown
Member

@brandonpayton brandonpayton left a comment

Choose a reason for hiding this comment

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

@ashfame I'm not sure what to make of this yet. Do we know for sure this is the cause of login flakiness observed in E2E tests? Do we even have E2E tests for Playground CLI? I don't think so, but sometimes I forget things.

AFAICT, the fact that consts.json is shared across PHP-WASM/node workers should be sufficient to define PLAYTOUN_AUTO_LOGIN_AS_USER because consts.json is read within the auto_prepend_script every time PHP is run:

if (!this.fileExists(AUTO_PREPEND_SCRIPT)) {
this.writeFile(
AUTO_PREPEND_SCRIPT,
`<?php
// Define constants set via defineConstant() calls
if(file_exists('/internal/shared/consts.json')) {
$consts = json_decode(file_get_contents('/internal/shared/consts.json'), true);
foreach ($consts as $const => $value) {
if (!defined($const) && is_scalar($value)) {
define($const, $value);
}
}
}
// Preload all the files from /internal/shared/preload
foreach (glob('/internal/shared/preload/*.php') as $file) {
require_once $file;
}
`
);
}

The same could be said of Playground on the web which has a single Web Worker with multiple PHP-WASM/web instances. In that case, there is a primary PHP-WASM instance that has a MEMFS filesystem that all secondary PHP-WASM instances access via PROXYFS. So those instances should also be getting the constants for free due to the auto_prepend_script.

If there is indeed flaky auto-login in E2E tests, it might be due to something else.

If there is real flakiness with Playground CLI E2E tests, one place I might wonder about is this bit which tries to clear cookies from previous Playground CLI sessions:

// Clear the playground_auto_login_already_happened cookie on the first request.
// Otherwise the first Playground CLI server started on the machine will set it,
// all the subsequent runs will get the stale cookie, and the auto-login will
// assume they don't have to auto-login again.
if (isFirstRequest) {
isFirstRequest = false;
const headers: Record<string, string[]> = {
'Content-Type': ['text/plain'],
'Content-Length': ['0'],
Location: [request.url],
};
if (
request.headers?.['cookie']?.includes(
'playground_auto_login_already_happened'
)
) {
headers['Set-Cookie'] = [
'playground_auto_login_already_happened=1; Max-Age=0; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Path=/',
];
}
return StreamedPHPResponse.fromPHPResponse(
new PHPResponse(302, headers, new Uint8Array())
);
}

On another topic:
Regarding defines.ts, I think it would be good to keep that module as generic as possible, without knowledge of specific constants.

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