Add wp-env as an alternative local development environment#2
Open
Add wp-env as an alternative local development environment#2
Conversation
Introduces @wordpress/env alongside the existing Docker setup, providing a simpler way to run the multi-network WordCamp.org environment locally. Key changes: - Create .wp-env.json with multisite config, plugin slugs, and mappings - Add afterStart lifecycle script to seed networks and sites via WP-CLI - Make sunrise files detect network from HTTP_HOST instead of static constants, enabling wp-env (which uses a single wp-config.php) to serve all three networks correctly - Update phpunit-bootstrap.php to detect wp-env paths - Move wordpress.org plugins/themes from composer to wp-env management - Add convenience yarn scripts for wp-env operations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The sunrise.php change to use HTTP_HOST-based network detection calls get_domain_network_id() which relies on get_top_level_domain(), and that function references the WORDCAMP_ENVIRONMENT constant. This constant was not defined in the test bootstrap, causing failures. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove all 8 CampTix payment gateway plugins from composer.json (both require-dev entries and custom SVN package definitions) and add them to .wp-env.json using their wordpress.org plugin slugs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Supportflow is explicitly deactivated network-wide in wcorg-network-plugin-control.php and is not actively used. Remove it entirely rather than migrating to wp-env. Move tagregator from Composer to wp-env plugin slug. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
PHP 8.5 is fully supported by the official WordPress Docker images and has beta support in WordPress 6.9+. This matches the latest PHP version tested in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the manual MySQL service, SVN, install-wp-tests.sh setup with wp-env. The PHP version matrix is handled via .wp-env.override.json which overrides the phpVersion per matrix entry. This eliminates the need for the MySQL service container, SVN installation, and the custom WP test suite installer script. wp-env provides WordPress, the PHPUnit test suite, and MySQL out of the box. The linter workflow is unchanged since it does not run WordPress. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
These payment gateway plugins are not needed for local development. Code references to them can remain for production use. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Install bbpress from the latest wordpress.org release instead of pinning to the 2.6 SVN branch via a custom Composer package. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The wporg-profiles-wp-activity-notifier package is installed from meta.svn.wordpress.org and requires SVN to be available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wp-env requires full download URLs for wordpress.org plugins and themes, not bare slugs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
96242e6 to
dc61918
Compare
The p2 theme returns a 404 from downloads.wordpress.org. It is no longer distributed as a standard theme download. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wp-env runs wp core install before wp config set, so custom constants like WORDCAMP_ENVIRONMENT are not defined when mu-plugins first load during installation. Add defined() checks to prevent fatal errors during wp-env startup. Also add file_exists() check for pub-sync/loader.php which may not exist in all environments (e.g. wp-env where mu-plugins-private is not mapped). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wp-env runs wp core install before wp config set, so custom constants are not available when mu-plugins first load during installation. This early-loading mu-plugin provides safe defaults that prevent fatal errors during the initial WordPress bootstrap. In production and Docker environments where constants are already defined in wp-config.php, this file is a no-op. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wp-env starts a full WordPress instance that loads mu-plugins, which require built JS/CSS assets (blocks, virtual-embeds). Add a build step before starting wp-env to generate these artifacts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
During wp-env initial install, WordPress loads without multisite since those constants are not yet in wp-config.php. This means multisite functions like get_site_meta() are undefined. Adding MULTISITE and SUBDOMAIN_INSTALL as fallback constants ensures WordPress loads the multisite function stack during initial bootstrap. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wp-env runs wp core install before wp config set, and MULTISITE must be in wp-config.php before wp-settings.php processes the multisite check. Since mu-plugins load after that check, fallback constants in a mu-plugin cannot make multisite functions available during the initial WordPress installation. The original CI approach (MySQL service + install-wp-tests.sh) avoids this entirely because PHPUnit loads WordPress via its own bootstrap where all constants are pre-defined. Also remove MULTISITE/SUBDOMAIN_INSTALL from the fallback constants since they have no effect as a mu-plugin (too late in load order). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This reverts commit b05cd73.
5 tasks
Instead of a fallback constants mu-plugin (0-aaa-constants.php), guard individual files against missing constants during wp-env initial bootstrap (wp core install runs before wp config set): - sunrise.php: return early if WORDCAMP_ENVIRONMENT is not defined - cron.php, service-worker-caching.php: add defined() checks - load-other-mu-plugins.php: add file_exists() for pub-sync/loader.php and defined() checks for network constants Move WORDCAMP_ENVIRONMENT to last in .wp-env.json config so all other constants are set in wp-config.php first. Switch CI from manual MySQL + install-wp-tests.sh to wp-env. PHP version matrix handled via .wp-env.override.json. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Since sunrise loads before mu-plugins, defining WORDCAMP_ENVIRONMENT there is sufficient. All other constants are already in wp-config.php because WORDCAMP_ENVIRONMENT is set last in the wp-env config. Also disable plugin loading during initial setup via filters, and revert the defensive defined() checks in mu-plugins since they are no longer needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wp-env runs wp core install before wp config set, so custom constants aren't in wp-config.php when mu-plugins first load. Instead of adding defensive checks to production code, map a wp-env-only mu-plugin from .wp-env/0-early-mu-plugin.php that defines safe defaults. The file sorts first alphabetically (0-aaa-*) so all constants are available before other mu-plugins load. In production, this file does not exist. Simplify the sunrise.php early return to just define WORDCAMP_ENVIRONMENT (the only constant sunrise itself needs) since the mu-plugin handles the rest. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move mu-plugin file mapping after directory mapping in .wp-env.json so the directory mount doesn't overwrite the individual file. - Add file_exists guard for mu-plugins-private/wporg-mu-plugins/pub-sync which doesn't exist in wp-env environments. - Fix PHPCS doc comment capitalization in 0-early-mu-plugin.php. - Add retry to wp-env start for transient 429 rate limiting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change require_once to include_once for pub-sync/loader.php so missing private repo files emit warnings instead of fatals. - Add wp_installing() guard to wcorg_enforce_public_blog_option() to prevent calling get_site_meta() during initial install when multisite functions are not available yet. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Jetpack Custom CSS module references JETPACK__VERSION which is not available during wp core install since plugins load after mu-plugins. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
During wp core install, mu-plugins load but many multisite functions are not yet available. Skip loading all sub-folder mu-plugins during install to prevent cascading fatal errors from init hooks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The tests wp-env instance installs WordPress at localhost, not wordcamp.test. Override DOMAIN_CURRENT_SITE and blog IDs to match the default multisite install state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SUBDOMAIN_INSTALL=true is incompatible with localhost domain. Override to false for the tests environment. Also fix retry logic to destroy the wp-env instance before retrying to avoid EACCES permission errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The tests environment uses localhost which does not support subdomain multisite. Move SUBDOMAIN_INSTALL=true to the development env config so it only applies to dev, not tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Ensure both environments default to path-based multisite. The dev environment overrides this to true for subdomain support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
wp-env multisite install is incompatible with localhost domain when SUBDOMAIN_INSTALL is true. Since the WordPress PHPUnit test suite handles multisite via WP_TESTS_MULTISITE, disable wp-env multisite for the tests environment entirely. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The top-level `multisite: true` was bleeding MULTISITE constant into the tests environment, causing database table errors during wp-env start. Moving multisite-specific settings (SUNRISE, SUBDOMAIN_INSTALL, DOMAIN_CURRENT_SITE, etc.) to env.development ensures the tests env gets a clean single-site install. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The tests env runs as single-site (with WP_TESTS_MULTISITE for PHPUnit). During wp-env wp config set commands, WordPress loads in single-site mode where multisite functions like get_site_meta() are unavailable. - Add is_multisite() check to load-other-mu-plugins.php - Add is_multisite() check to wcorg_enforce_public_blog_option() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- setup.mjs: catch wp site list error in non-multisite environments - CI: fix wp-env destroy confirmation (pipe yes instead of --yes flag) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Keep multisite:true and all config at top level for local dev. CI overrides multisite:false at top level - the multisite config constants in wp-config.php are harmless without MULTISITE defined. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WordPress's is_multisite() returns true if SUNRISE or SUBDOMAIN_INSTALL are defined (even as false), regardless of the MULTISITE constant. Having these in .wp-env.json config means they get added to wp-config.php for ALL environments via wp config set, making both dev and tests think they're multisite even when multisite:false is set via override. Moving these to the lifecycle script means they're only set when multisite is actually available (the script exits early for single-site). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The vendor/ mapping and mu-plugins/vendor/ point to the same Docker mount. Using different paths causes require_once to load the autoloader twice. Also guard constant definitions to avoid warnings when wp-env has already set them via wp-config.php. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The vendor mapping created two paths to the same autoloader files, causing Composer's autoloader class to be declared twice. Instead of mapping vendor separately, invoke phpunit from its actual location in mu-plugins/vendor/bin/. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The mu-plugins-private directory is not available in wp-env. Guard the require_once with file_exists to prevent fatal errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Inside the wp-env container, content is at /var/www/html/wp-content/ not /var/www/html/public_html/wp-content/. Remove the public_html/ prefix from all test directory paths. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…fixes - Skip bootstrap constants in wp-env (let wp-config.php set correct values) - Override WP_TESTS_DOMAIN/WP_SITEURL/WP_HOME to prevent wp-env port append - Add file_exists() guard in autoloader for missing private mu-plugins - Use dynamic table prefix in SubRoles test instead of hardcoded 'wptests_' - Set HTTP_HOST in sunrise-events test for get_redirect_url() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
@wordpress/env(wp-env) as a simpler alternative to the existing Docker Compose setup for local development. Both environments coexist — the.docker/setup is unchanged.What this PR does
.wp-env.json) — Multisite on port 80, plugins/themes from wordpress.org download URLs, directory mappings for mu-plugins/plugins/themes/sunrise files, and a lifecycle script for database seeding.wp-env/setup.mjs) — IdempotentafterStartlifecycle script that creates 3 WordPress networks (WordCamp, Events, Campus) with specific blog IDs matching productionload_network_sunrise(),sunrise-events.php, andsunrise-wordcamp.phpnow detect the network fromHTTP_HOSTinstead of staticSITE_ID_CURRENT_SITE/WORDCAMP_NETWORK_IDconstants. This is backward-compatible sinceget_domain_network_id()already handles both.testand.orgdomains/var/www/html) and adjusts paths for plugins, vendor autoload, and WP test suite locationunit-phpGitHub Actions job now uses wp-env instead of manual MySQL service +install-wp-tests.sh. PHP version matrix is handled via.wp-env.override.jsonSites available after
npx wp-env starthttp://central.wordcamp.test/http://narnia.wordcamp.test/2026/http://events.wordpress.test/http://events.wordpress.test/rome/2024/training/Login:
admin/passwordPrerequisites (one-time)
Add to
/etc/hosts:Quick start
wp-env quirks and workarounds
This section documents the non-obvious wp-env behaviors discovered and the workarounds used. These are worth knowing for anyone maintaining or extending the wp-env setup.
1.
WP_TESTS_DOMAINgets the test port appendedwp-env automatically appends
testsPorttoWP_TESTS_DOMAIN,WP_SITEURL, andWP_HOMEviaappendPortToWPConfigs()in its post-processing. WithtestsPort: 8889, the test domain becomeslocalhost:8889instead oflocalhost.Only ports 80 and 443 are excluded from appending (see
node_modules/@wordpress/env/lib/config/add-or-replace-port.js).Impact: Tests that check domain names (e.g. Let's Encrypt
test_get_domains) gotlocalhost:8889instead of a clean domain.Workaround:
phpunit-bootstrap.phpdefinesWP_TESTS_DOMAIN,WP_SITEURL, andWP_HOMEearly (before wp-env's wp-tests-config.php loads) to override the port-appended values. We tried setting these tonullin.wp-env.json's tests config, but wp-env validates config values and rejectsnull— onlystring | number | booleanare accepted.2.
wp-tests-config.phpis generated fromwp-config.phpwp-env generates
wp-tests-config.phpby copyingwp-config.phpand stripping therequire wp-settings.phpline (viased). This means:$table_prefixiswp_, notwptests_as in standard WP test setups.wp-env.json) are present in the test configphpunit-bootstrap.phpbefore the WP test suite loads take precedence over wp-tests-config.php values (firstdefine()wins)Impact: The subroles test hardcoded
wptests_capabilitiesas the user meta key, which didn't match wp-env'swp_prefix. Also,phpunit-bootstrap.phpwas definingWORDCAMP_ROOT_BLOG_ID=5before wp-tests-config.php could set it to1, causing tests that depend on blog IDs to fail.Workaround:
$wpdb->get_blog_prefix() . 'capabilities'instead of a hardcoded keyphpunit-bootstrap.phpskips defining bootstrap constants when running in wp-env (they come from wp-config.php with correct values)3. No
mu-plugins-private/directory in wp-envThe
2-autoloader.phpautoloader doesrequire_oncefor files inmu-plugins-private/wporg-mu-plugins/pub-sync/utilities/. These private files don't exist in wp-env or CI.Impact: Any test that triggers
class_exists()for aWordPressdotorg\MU_Plugins\Utilitiesclass would hit a fatalrequire_onceerror, even if the test hadmarkTestSkipped()logic (the autoloader runs before the skip check).Workaround: Added a
file_exists()guard in2-autoloader.phpbeforerequire_oncefor the private path. This is safe for production since the file will always exist there.4. Constants unavailable during mu-plugin loading
wp-env loads mu-plugins before
wp-config.phpconstants are fully available in certain bootstrap phases (e.g. duringwp-env startinstall). Several mu-plugins reference constants likeWORDCAMP_ENVIRONMENTunconditionally.Workaround: Created
.wp-env/0-early-mu-plugin.phpmapped towp-content/mu-plugins/0-aaa-wp-env-constants.php(alphabetically first) that defines fallback values for required constants. Addeddefined()guards in mu-plugins that run early.5. Multisite subdomain install + localhost = problems
wp-env's multisite uses
localhostas the domain. Subdomain install with localhost doesn't work because*.localhostdoesn't resolve. The actual multisite setup with custom domains (wordcamp.test, events.wordpress.test) requires thesunrise.phpdrop-in and/etc/hostsentries.Workaround: The
.wp-env/setup.mjslifecycle script runs afterwp-env startand creates the multi-network topology with proper custom domains via WP-CLI. The sunrise files handle routing requests to the correct network based onHTTP_HOST.6. CI doesn't have
/etc/hostsentriesIn CI, there are no custom domain entries. Tests that rely on
$_SERVER['HTTP_HOST']being set to a specific domain need to set it themselves.Workaround: Tests like
test-sunrise-events.phpexplicitly set$_SERVER['HTTP_HOST']before calling functions that depend on it. The test environment config usesBLOG_ID_CURRENT_SITE=1andWORDCAMP_ROOT_BLOG_ID=1(overriding the dev defaults of 5) so tests don't depend on specific blog IDs that require the full multi-network setup.Test plan
npx wp-env startcompletes without errorscurl -I http://central.wordcamp.test/returns 200curl -I http://narnia.wordcamp.test/2026/returns 200curl -I http://events.wordpress.test/returns 200npx wp-env run cli wp site listshows all 4+ sitesnpx wp-env run cli wp network listshows 3 networksyarn wp-env:test:phppasses.docker/)🤖 Generated with Claude Code