Skip to content

Native PHP runtime: blueprint execution via blueprints.phar#3271

Merged
fredrikekelund merged 21 commits intotrunkfrom
claude/vigilant-turing-54f824
Apr 30, 2026
Merged

Native PHP runtime: blueprint execution via blueprints.phar#3271
fredrikekelund merged 21 commits intotrunkfrom
claude/vigilant-turing-54f824

Conversation

@bcotrim
Copy link
Copy Markdown
Contributor

@bcotrim bcotrim commented Apr 28, 2026

Related issues

  • Fixes RSM-1322

How AI was used in this PR

This PR was largely AI-assisted. I reviewed all changes and tested end-to-end locally.

Proposed Changes

Implements blueprint execution for the native PHP runtime (php-server-child.ts), which previously threw "not supported" for the run-blueprint IPC message.

  • scripts/download-wp-server-files.ts: adds blueprints-phar download entry — fetches the latest blueprints.phar from WordPress/php-toolkit GitHub releases and lands it at wp-files/blueprints/blueprints.phar, which Vite auto-copies into dist/cli/wp-files/ at build time
  • apps/cli/lib/dependency-management/paths.ts: adds getBlueprintsPharPath() following the existing phar path helper pattern
  • apps/cli/php-server-child.ts:
    • adds runProcessToCompletion() — spawns a process, streams stdout/stderr, sends { topic: 'activity' } IPC pings on each chunk to keep the parent alive during long installs
    • adds runBlueprint() — writes blueprint JSON to a temp file, calls php blueprints.phar exec --mode=apply-to-existing-site, cleans up on exit
    • hooks runBlueprint() into startServer() so blueprints are applied automatically after WordPress is installed
    • wires the run-blueprint IPC case to call runBlueprint() directly (used by the --no-start path)

SQLite workaround: blueprints.phar detects SQLite by checking wp-content/plugins/sqlite-database-integration/load.php, but Studio installs it in mu-plugins/. A temporary symlink plugins/ → mu-plugins/ is created before the PHAR runs and removed after. Moving SQLite from mu-plugins to plugins is a broader conversation for later.

Testing Instructions

Example blueprint

{
  "steps": [
    {
      "step": "setSiteOptions",
      "options": {
        "blogname": "Blueprint worked!"
      }
    }
  ]
}
  1. npm run download:wp-server-files (or npm install) — confirm wp-files/blueprints/blueprints.phar appears
  2. npm run cli:build
  3. Create a native-PHP site with the included test blueprint:
    STUDIO_RUNTIME=native-php sdev site create --blueprint docs/blueprints/test-set-site-options.json
    
  4. Verify the blueprint applied:
    sdev wp --path ~/Studio/<site-name> option get blogname
    # should return: Blueprint worked!
    

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?

@bcotrim bcotrim marked this pull request as ready for review April 28, 2026 15:55
@bcotrim bcotrim requested a review from fredrikekelund April 28, 2026 15:57
@wpmobilebot
Copy link
Copy Markdown
Collaborator

wpmobilebot commented Apr 28, 2026

📊 Performance Test Results

Comparing 24e019e vs trunk

app-size

Metric trunk 24e019e Diff Change
App Size (Mac) 1454.99 MB 1456.67 MB +1.68 MB 🔴 0.1%

site-editor

Metric trunk 24e019e Diff Change
load 1740 ms 1785 ms +45 ms ⚪ 0.0%

site-startup

Metric trunk 24e019e Diff Change
siteCreation 8094 ms 8084 ms 10 ms ⚪ 0.0%
siteStartup 4949 ms 4949 ms 0 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

- Handle parallelism for `run-blueprint` messages
- Harden `config.blueprint` types to avoid non-null assertion
- Remove `runProcessToCompletion` in favor of `runPhpCommand`
Copy link
Copy Markdown
Contributor

@fredrikekelund fredrikekelund left a comment

Choose a reason for hiding this comment

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

Fundamentally, this works incredibly well 👍

I took the liberty of making a few changes to address things I identified while reviewing:

  • Type-safe and reusable GitHub release fetching in scripts/download-wp-server-files.ts.
  • Use a queue for run-blueprint messages.
  • Hardened config.blueprint types to avoid non-null assertion.
  • Removed runProcessToCompletion in favor of runPhpCommand.
  • Write the blueprint JSON file to the original blueprint directory so blueprint.phar can read local assets correctly.
  • Use fs.rm to remove the sqlite-database-integration symlink (Claude says Windows will throw when unlinking junctions). For extra safety, this is backed up by verifying the inode before deleting.

We should investigate how blueprint.phar handles remote resources more closely. Right now, php-server-child.ts errors if blueprint.uri is an HTTP URL. We can work around that by fetching the URL before handing it to blueprint.phar, but that only work if there are no additional gotchas related to how blueprint.phar handles HTTP fetching compared to the Playground implementation.

bcotrim and others added 12 commits April 29, 2026 11:16
- `wordpress-server-manager.ts` got stuck waiting for a response to the `start-server` message because zod parsing failed on the response. That's because JSON serialization drops keys where the value is undefined. It's unclear why we haven't seen this error for `playground-server-child.ts`. The fix was to change the `childMessageResult` schema.
- Pass environment variables from the CLI through the process manager daemon when spawning a process so that `getPhpBinaryPath` returns the same result as in the CLI process when running E2E tests.
@fredrikekelund fredrikekelund merged commit 2f95352 into trunk Apr 30, 2026
9 of 10 checks passed
@fredrikekelund fredrikekelund deleted the claude/vigilant-turing-54f824 branch April 30, 2026 13:44
fredrikekelund added a commit that referenced this pull request May 5, 2026
## Related issues

<!--
Link a related issue to this PR. If the PR does not immediately resolve
the issue,
for example, it requires a separate deployment to production, avoid
using the "Fixes" keyword and use "Related to" instead.
-->

- Fixes RSM-1725

## How AI was used in this PR

<!--
Help reviewers understand what to look for and verify that you've
reviewed the code yourself.
-->

Primarily to help me debug the problem. I alternated between Claude and
Codex.

## Proposed Changes

While working on #3271,
@bcotrim discovered that the E2E tests were failing when run with
`STUDIO_RUNTIME=native-php`. This PR fixes that by implementing the
following fixes:

- When Studio clones a site, explicitly update the site URL. Playground
can handle this internally, but the native PHP runtime obviously cannot.
- On Posix platforms, spawn process daemon children with `detached:
true` to assign them to a new process group. This is so we can pass a
negative PID to `process.kill` to terminate the entire process group
(the Node.js process and the associated PHP process). This approach
isn't bulletproof, but it's alright, and it works for the purpose of the
E2E tests.
- Favor SIGTERM over SIGKILL when killing child processes to give them a
chance to clean up.

## Testing Instructions

<!--
Add as many details as possible to help others reproduce the issue and
test the fix.
"Before / After" screenshots can also be very helpful when the change is
visual.
-->

Run the E2E tests locally on macOS by following these steps:

1. `npm run package`
2. `STUDIO_RUNTIME=native-php npm run e2e`
3. Ensure that they pass successfully

## Pre-merge Checklist

<!--
Complete applicable items on this checklist **before** merging into
trunk. Inapplicable items can be left unchecked.

Both the PR author and reviewer are responsible for ensuring the
checklist is completed.
-->

- [ ] Have you checked for TypeScript, React or other console errors?
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