Add envlite tool#35
Conversation
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the Unlinked AccountsThe following contributors have not linked their GitHub and WordPress.org accounts: @lucatume, @gemini-code-assist. Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases. Core Committers: Use this line as a base for the props when committing in SVN: To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
| the final state but breaks the "internally consistent at every | ||
| step" invariant. | ||
|
|
||
| Phases 1, 2, 4, 5, 6 are mutually independent and could be run in |
There was a problem hiding this comment.
Adding note/comment as a TODO: I think there are gains to be had here by making the two main HTTP based tools (Composer and npm) run in parallel.
To verify and decide whether the 5s seconds savings is real or not. If real, fine as is, else I would explore parallelization.
There was a problem hiding this comment.
👍
I ran a benchmark to see whether running npm ci and composer install was likely to improve things or not. My machine does should paralell as 1.3 times faster than sequential.
- Keep within the spec's PHP 7.4 floor: replace str_starts_with / str_contains / str_ends_with calls in envlite.php and the tests with strpos/substr equivalents. The preflight gates on PHP_VERSION_ID >= 70400, but the rest of the file used PHP 8.0+ helpers and would fatal before reaching preflight. - Convert phase 5-8 failures into the spec's diagnostic format. Phases throw RuntimeException on SHA256 mismatch, missing placeholders, and I/O errors; init now wraps each phase in envlite_init_phase_guard, which logs `envlite init: phase N: <cause>` and exits 1. - Handle file_get_contents() returning false in phases 5/6/7. Required reads (db.copy, sample configs) raise a structured `phase N: cannot read <path>` failure; optional current-file reads short-circuit cleanly so the owned_drifted branch never hashes null. The observe_ht_sqlite step skips on read failure rather than recording the empty-string hash. - Align preflight npm floor with package.json engines (>= 10.2.3 vs the previous 10.2.0) so preflight catches what `npm ci` would later. Spec text updated to match. - Correct the plan note that claimed no edits to package.json or .gitignore — both are touched in this branch.
| exception. The `--group html-api` subset still passes clean on PHP | ||
| 8.5.5 against the SQLite drop-in. Other groups may surface | ||
| deprecations; that's a per-group fix, not envlite's problem. | ||
| 3. **No `composer.lock`, by upstream design.** Every Phase 4 run |
There was a problem hiding this comment.
Review: should this run with PHP 7.4 platform?
Pro: it would guarantee same dependencies no matter the PHP version used.
Con: it would ignore the PHP version used to run the command.
Not sure what should be the default here. A developer/agent might not be making a conscious decision to use a specific PHP version to run the command, but rather jut run whatever is available.
| 5. **Two distinct config files.** `wp-tests-config.php` (Phase 6) and | ||
| `src/wp-config.php` (Phase 7) are loaded by different bootstrap paths | ||
| and serve different purposes. Both are needed; do not consolidate. | ||
| 6. **Pin the plugin SHA, not the version number.** Plugin version |
There was a problem hiding this comment.
This requires updating this repository to get the latest version of a plugin that is not part of Core WordPress, but just a utility for it.
I think the version should be stable, but latest so that someone running envlite would get the greatest and latest version of the plugin for local development.
|
|
||
| ## What envlite explicitly does NOT do | ||
|
|
||
| - Allocate ports for *external* tooling (database GUIs, Xdebug, etc.) — |
There was a problem hiding this comment.
I think being deterministic about the XDebug port would really help when working on multiple environments at the same time: all calling the same XDebug port would make debugging impossible.
This comment was marked as resolved.
This comment was marked as resolved.
- Keep within the spec's PHP 7.4 floor: replace str_starts_with / str_contains / str_ends_with calls in envlite.php and the tests with strpos/substr equivalents. The preflight gates on PHP_VERSION_ID >= 70400, but the rest of the file used PHP 8.0+ helpers and would fatal before reaching preflight. - Convert phase 5-8 failures into the spec's diagnostic format. Phases throw RuntimeException on SHA256 mismatch, missing placeholders, and I/O errors; init now wraps each phase in envlite_init_phase_guard, which logs `envlite init: phase N: <cause>` and exits 1. - Handle file_get_contents() returning false in phases 5/6/7. Required reads (db.copy, sample configs) raise a structured `phase N: cannot read <path>` failure; optional current-file reads short-circuit cleanly so the owned_drifted branch never hashes null. The observe_ht_sqlite step skips on read failure rather than recording the empty-string hash. - Align preflight npm floor with package.json engines (>= 10.2.3 vs the previous 10.2.0) so preflight catches what `npm ci` would later. Spec text updated to match. - Correct the plan note that claimed no edits to package.json or .gitignore — both are touched in this branch.
This comment was marked as resolved.
This comment was marked as resolved.
Currently `phpunit` and `envlite serve` share `.ht.sqlite`, so the test bootstrap's `install.php` drops the dev site's tables on every test run. Design proposes a single `define( 'DB_FILE', '.ht.test.sqlite' )` in Phase 6's `wp-tests-config.php`, leaving Phase 7 and the live runtime untouched.
Bite-sized tasks for the design at plans/2026-05-09-envlite-test-db- isolation-design.md: pre-implementation verification, tripwire, DB_FILE append, end-to-end manual test, spec update.
Inline code spans render backslashes literally; ['\"] would show as ['\"] not ['"] in the rendered Markdown.
The fixture used 9999, which falls inside the 1..65535 range that envlite_phase1_discover_port treats as a valid cached port — so the function returned 9999 and the assertion that the port came from the 8100..8899 auto-discovery pool would have failed if the suite ever got that far. Use 70000 so the cache-out-of-range path is actually exercised.
ZipArchive::extractTo returns false on a partial or failed extraction (permissions, full disk, malformed entries). The previous code ignored that and unconditionally recorded the plugin directory as envlite-owned; on the next run the db.copy short-circuit would then skip re-downloading and leave a half-extracted, unverified SQLite plugin tree in place. Treat a non-true return as fatal so Phase 5 surfaces the failure instead of pinning broken state into the manifest.
api.wordpress.org/secret-key/1.1/salt/ returns random bytes that can contain \`\$\` and \`\\\`. Passing those bytes as preg_replace's replacement argument means sequences like \`\$1\`, \`\\1\`, or \`\$&\` get interpreted as backreferences and silently corrupt the salts that land in src/wp-config.php. Switch to preg_replace_callback so the salts block is inserted verbatim regardless of what characters it contains. Pinned by a regression test that feeds backreference-shaped bytes through the render path and asserts they survive verbatim.
wp-config-sample.php ships CRLF in tree, so the previous render path mixed the sample's CRLF lines with envlite's LF-only injections (WP_HOME, WP_SITEURL, the fetched salts block). The rendered file is then ugly and — worse — the manifest hash recorded for src/wp-config.php becomes sensitive to whatever EOL conversion the user's git client applied at checkout time, which would spuriously trip envlite's drift prompt on machines with different settings. Strip \\r\\n to \\n once at the top of envlite_phase7_render so output is LF-only regardless of how the sample was checked out. Pinned by a regression test asserting the rendered config contains no \\r\\n.
…able php -S does not honor Apache .ht* deny rules, so a request for /wp-content/database/.ht.sqlite would be served as a static binary by the existing-file passthrough.
…index.php Existing directories such as /wp-admin/ previously fell through to the front controller, returning the front-end response instead of executing src/wp-admin/index.php. Return false for directories with an index.php so php -S serves the directory index. Directories without an index still fall through to the front controller to avoid php -S's default directory listings.
After a reboot or other session change, the cached port in .envlite/port may now be held by another process. Phase 1 returned the cached port without verifying it was free, so the failure only surfaced at bind time in `serve` — after every other init phase had already run. Verify the cached port is free; on conflict fall through to the seeded auto-discovery loop and re-cache the new pick.
Adds
tools/local-env/envlite.php: a single-file PHP setup tool that takes a clean wordpress-develop checkout to a runnable state — WordPress on SQLite served byphp -S, plus a greenphpunit --group html-api— without Docker, MAMP, or system MySQL.Existing local environments (Docker, MAMP) require background services and several minutes of setup. envlite uses only host PHP, node, and composer — tools already required for other dev workflows — and leaves no daemons behind.
Usage
php tools/local-env/envlite.php <subcommand>:init [--port=N] [--no-build]— runs all 9 setup phases (preflight, port discovery,npm ci,npm run build:dev,composer install, SQLite drop-in,wp-tests-config.php,src/wp-config.php,router.php).serve— runsphp -S 127.0.0.1:<cached-port> -t src router.phpin the foreground.clean— removes envlite-managed files (manifest-tracked).--force— disables interactive prompts (for CI).Design invariants
proc_openwith array commands. Nosed/awk/curl/unzip/shasumdependencies..envlite/manifestwith its content hash. Re-runs silently re-stamp envlite-owned files; drift or unowned files prompt before overwriting (--forcebypasses)..tmp, fsync (when available), rename. Manifest never reads the renamed target — it stores the hash that was written."\n", neverPHP_EOL. Content hashes are byte-identical across platforms.Tests
php tools/local-env/tests/run.php— 61 tests across 16 test files. No PHPUnit dependency (envlite bootstraps PHPUnit, so the test suite uses a tiny in-tree harness instead). Pure helpers tested directly; an end-to-end smoke drives Phases 5–8 +cleanagainst a fixture directory.Layout
tools/local-env/envlite.php— the tool (~850 lines)tools/local-env/tests/— test harness + 16 test filesplans/ENVLITE_SPECIFICATION.md— design specdocs/superpowers/plans/2026-05-08-envlite.md— implementation planTrac ticket:
Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Opus 4.7
Used for: Specification writing, implementation plan, and per-task implementation with two-stage review (spec compliance + code quality) per task. Output reviewed and edited by me.
This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.