Skip to content
Merged
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
4 changes: 3 additions & 1 deletion docs/guide/logs-and-debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ All three panes read in the terminal's own monospace font, so the columns of a P

## The debug.log tab

Anything WordPress or your code writes to the PHP error log — `error_log()` calls, notices, warnings, deprecations, `_doing_it_wrong()`, fatals — appears here while the dev server runs. This works because every site is booted with WordPress's debug constants already set. Two of them, `WP_DEBUG` and `SCRIPT_DEBUG`, can be turned off under **Sites** in [Settings](./settings); the rest are not configurable:
Anything WordPress or your code writes to the PHP error log — `error_log()` calls, notices, warnings, deprecations, `_doing_it_wrong()`, fatals — appears here while the dev server runs. This works because every site is booted with WordPress's debug constants already set, along with the two that say what kind of environment it is. Two of them, `WP_DEBUG` and `SCRIPT_DEBUG`, can be turned off under **Sites** in [Settings](./settings); the rest are not configurable:

| Constant | Value | Effect |
| --- | --- | --- |
Expand All @@ -28,6 +28,8 @@ Anything WordPress or your code writes to the PHP error log — `error_log()` ca
| `SCRIPT_DEBUG` | `true` | Core serves unminified JS and CSS. Can be turned off in Settings. |
| `WP_DISABLE_FATAL_ERROR_HANDLER` | `true` | A fatal shows the actual error instead of WordPress's "critical error" recovery screen. |
| `AUTOMATIC_UPDATER_DISABLED` | `true` | Core's automatic updater does not run (and does not fill the log with its own messages). |
| `WP_ENVIRONMENT_TYPE` | `local` | The site runs as a local environment, as Core's own Docker environment does. Application Passwords are available, which on plain HTTP they are only for a `local` site. As on any site that is not `production`, pingbacks and trackbacks are off, and Site Health skips its caching and HTTPS tests. |
| `WP_DEVELOPMENT_MODE` | `core`, or `plugin` on a Gutenberg site | On a Core site, Core looks for its blocks' stylesheets on every page load instead of using a cached list, so a block stylesheet added in `build/` is picked up at once. On a Gutenberg site it changes nothing today; it is set because it describes the site. |

Note that `WP_DEBUG_DISPLAY` has a known cost: a notice fired during a REST or AJAX request is printed into the response and can corrupt the JSON it expects. That trade is made deliberately — seeing the error beats a silent blank page for a newcomer.

Expand Down
55 changes: 45 additions & 10 deletions src/playground-plan.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -66,25 +66,60 @@ function planPlaygroundLaunch(config) {
};
}

// The wp-config constants a strategy needs on top of the shared debug/SMTP set.
// The wp-config constants that depend on what the site is, on top of the shared
// debug/SMTP set.
//
// Only 'plugin-mount' asks for any, and it asks for the two that make the
// mounted directory read-only from inside WordPress. The mount is a read-write
// NODEFS mount of the *source checkout*, not a regenerable build/, so
// Plugins → Delete on the mounted plugin, or the plugin file editor, writes
// straight through to the contributor's working tree, uncommitted work and .git
// included. Core's docroot strategy exposes only build/, which the app rebuilds,
// so it keeps WordPress's defaults.
// Every strategy gets the environment Core's own Docker environment sets up
// (#598): `.env.example` in wordpress-develop defaults WP_ENVIRONMENT_TYPE to
// `local` and WP_DEVELOPMENT_MODE to `core`, and the handbook documents that
// environment. Left undefined, the type falls back to `production`, and Core
// offers Application Passwords only over HTTPS or on a `local` site, so on the
// dev server's plain http://127.0.0.1 a ticket about them could not be tested.
// Any type but `production` also changes what a site does, and a contributor
// can meet it: pingbacks and trackbacks are off, in and out, and Site Health
// skips its page cache and object cache tests, drops the HTTPS test, and rates
// errors shown to visitors as recommended rather than critical. Core's Docker
// environment has all of that too, and a ticket about one of them needs the
// type changed on purpose: Core's own filters, from an mu-plugin in build/, do
// that per ticket better than a setting here would for every site. `local` itself also lets the screen that authorizes an
// application accept a plain-HTTP redirect URL, and would turn WP_DEBUG on by
// default, which the debug set decides anyway. Both values are strings,
// which is what Core compares them with, so they live here rather than in
// wp-debug-constants.js, whose values are all booleans; and the development
// mode follows from the strategy.
//
// The development mode is what the site is: a wordpress-develop checkout is
// Core development, and a Gutenberg checkout mounted into a stock WordPress is
// plugin development. `core` makes Core list its own blocks' stylesheets on
// every load instead of from a cached list kept until the version changes, so a
// block stylesheet added to or removed from build/ is picked up at once.
// `plugin` changes nothing in Core or Gutenberg today; it is set because it is
// the honest answer, and `core` would describe a Core checkout the site does
// not have. Neither value is a setting: there is nothing to choose that the
// site type does not already answer.
//
// 'plugin-mount' also asks for the two constants that make the mounted
// directory read-only from inside WordPress. The mount is a read-write NODEFS
// mount of the *source checkout*, not a regenerable build/, so Plugins → Delete
// on the mounted plugin, or the plugin file editor, writes straight through to
// the contributor's working tree, uncommitted work and .git included. Core's
// docroot strategy exposes only build/, which the app rebuilds, so it keeps
// WordPress's defaults.
//
// The cost is real and deliberate: DISALLOW_FILE_MODS also blocks installing a
// second plugin or theme into the preview. Losing an afternoon of uncommitted
// work is worse than restarting a preview you can restart.
function planServeConstants(config) {
const cfg = config || {};
if (cfg.strategy === 'plugin-mount') {
return { DISALLOW_FILE_MODS: true, DISALLOW_FILE_EDIT: true };
return {
WP_ENVIRONMENT_TYPE: 'local',
WP_DEVELOPMENT_MODE: 'plugin',
DISALLOW_FILE_MODS: true,
DISALLOW_FILE_EDIT: true
};
}
return {};
return { WP_ENVIRONMENT_TYPE: 'local', WP_DEVELOPMENT_MODE: 'core' };
}

module.exports = { planPlaygroundLaunch, planServeConstants, WORDPRESS_VFS_ROOT, PLUGINS_VFS_BASE };
6 changes: 4 additions & 2 deletions src/server-runner.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,10 @@ async function main() {
'WP_MAIL_SMTP_SECURE': process.env.WP_MAIL_SMTP_SECURE || '', // '', 'ssl', or 'tls'
'WP_MAIL_SMTP_USER': process.env.WP_MAIL_SMTP_USER || '',
'WP_MAIL_SMTP_PASS': process.env.WP_MAIL_SMTP_PASS || '',
// Last, so a strategy that has to protect the host directory it
// mounted is not overridden by the shared sets above.
// Last: the environment type and development mode every site
// gets, and, for a strategy that has to protect the host
// directory it mounted, the constants that do, which the shared
// sets above must not override.
...serveConstants
}
}
Expand Down
26 changes: 25 additions & 1 deletion tests/unit/playground-plan.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

const test = require('node:test');
const assert = require('node:assert/strict');
const { planPlaygroundLaunch, PLUGINS_VFS_BASE } = require('../../src/playground-plan.cjs');
const { planPlaygroundLaunch, planServeConstants, PLUGINS_VFS_BASE } = require('../../src/playground-plan.cjs');
const { getProjectType } = require('../../src/project-type.cjs');

test('docroot strategy mounts the build dir as WordPress and skips the download', () => {
Expand Down Expand Up @@ -73,3 +73,27 @@ test('a plugin-mount slug that is not a plain name is refused before it becomes
assert.throws(() => planPlaygroundLaunch({ strategy: 'plugin-mount', pluginDir: '/sites/gb', pluginSlug: bad }), /plain slug/, `expected a refusal for ${JSON.stringify(bad)}`);
}
});

// The environment Core's own Docker environment sets up (#598). Undefined, the
// type falls back to `production`, and Core offers Application Passwords only
// over HTTPS or on a `local` site: on the dev server's plain HTTP the feature
// was gone, and a ticket about it could not be tested.
test('a Core site runs as a local environment in core development mode', () => {
assert.deepEqual(planServeConstants({ strategy: 'docroot', docroot: '/sites/wp/build' }), {
WP_ENVIRONMENT_TYPE: 'local',
WP_DEVELOPMENT_MODE: 'core'
});
// The default strategy is docroot, so it gets the same.
assert.deepEqual(planServeConstants({}), planServeConstants({ strategy: 'docroot' }));
});

// A Gutenberg checkout is a plugin mounted into a stock WordPress: plugin
// development, not Core. The file locks stay beside the environment.
test('a Gutenberg site runs as a local environment in plugin development mode, with file changes locked', () => {
assert.deepEqual(planServeConstants({ strategy: 'plugin-mount', pluginDir: '/sites/gutenberg', pluginSlug: 'gutenberg' }), {
WP_ENVIRONMENT_TYPE: 'local',
WP_DEVELOPMENT_MODE: 'plugin',
DISALLOW_FILE_MODS: true,
DISALLOW_FILE_EDIT: true
});
});
6 changes: 6 additions & 0 deletions tests/unit/runner-wiring.test.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,10 @@ test('a docroot config reaches runCLI as the options a Core site always got', ()
assert.equal('additional-blueprint-steps' in cliOptions, false);
assert.equal(cliOptions.wordpressInstallMode, 'install-from-existing-files-if-needed');
assert.equal(cliOptions.blueprint.constants.DISALLOW_FILE_MODS, undefined, 'a Core docroot keeps WordPress\'s file defaults');
// The environment Core's Docker environment has (#598); without `local`,
// Application Passwords are gone on the dev server's plain HTTP.
assert.equal(cliOptions.blueprint.constants.WP_ENVIRONMENT_TYPE, 'local');
assert.equal(cliOptions.blueprint.constants.WP_DEVELOPMENT_MODE, 'core');
});

test('a plugin-mount config mounts the checkout as a plugin into a stock install, and locks file modifications', () => {
Expand All @@ -232,6 +236,8 @@ test('a plugin-mount config mounts the checkout as a plugin into a stock install
// Plugins > Delete in the served site removes it.
assert.equal(cliOptions.blueprint.constants.DISALLOW_FILE_MODS, true);
assert.equal(cliOptions.blueprint.constants.DISALLOW_FILE_EDIT, true);
assert.equal(cliOptions.blueprint.constants.WP_ENVIRONMENT_TYPE, 'local');
assert.equal(cliOptions.blueprint.constants.WP_DEVELOPMENT_MODE, 'plugin');
// And they are added to the shared constants, not in place of them.
assert.equal(cliOptions.blueprint.constants.WP_MAIL_SMTP_HOST, '127.0.0.1');
});
Expand Down
Loading