Skip to content

CLI: Install the bundled WordPress into server-files atomically so concurrent commands never copy a partial install - #5314

Merged
chubes4 merged 7 commits into
trunkfrom
cli/install-latest-wordpress-atomically
Oct 27, 2026
Merged

chubes4 merged 7 commits into
trunkfrom
cli/install-latest-wordpress-atomically

Conversation

@chubes4

@chubes4 chubes4 commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

Related issues

  • Windows desktop E2E on build 23490 (E2E: Drop desktop site tests that repeat CLI E2E checks and merge the two adopt-directory tests #5311) failed in appearance.test.ts's beforeAll: the Add site dialog never closed because site creation died with a PHP fatal, Failed opening required '…\wp-includes/Requests/…'. The new site was missing WordPress files.
  • The same job logs EBUSY: resource busy or locked, copyfile from Failed to set up dependency WordPress version on every Windows run (5–20 times per job in the eight Windows E2E logs checked: builds 23401, 23432, 23455, 23474, 23478, 23481, 23490). Until now it looked harmless; on 23490 it reached a site.

How AI was used in this PR

Claude (via the Pi coding agent in a Roadie session) traced the failure through the job log, wrote the fix and the tests, and confirmed the test fails on trunk's code. I reviewed the diff and the evidence.

Proposed Changes

Every CLI command runs setupServerFiles as middleware. When server-files/wordpress-versions/latest has no WordPress (first run, or a fresh E2E session), it copies the bundled wp-files/latest/wordpress into it. The app starts several CLI commands at once, so several processes copied into the same folder at the same time:

  1. Two processes write the same file; on Windows one gets EBUSY. setupServerFiles logs that and carries on.
  2. latest is a valid-looking install as soon as wp-includes/version.php lands, which happens early in the copy. Any process checking then sees WordPress there and skips its own copy.
  3. site create then copies the incomplete latest into the new site, and PHP fails on the first missing file.

This copies into a staging folder beside latest (latest.partial-<pid>-<uuid>) and renames it into place, so latest only ever appears complete:

  • If the rename fails because another process installed latest first, that copy is used and the staging folder is removed.
  • If latest exists but has no version.php (an interrupted copy), it's removed and the rename is retried, up to 5 times with a short backoff. Another process can install its own copy in between; that copy is accepted.
  • The newer-bundled-version path is unchanged apart from using the same install step: the old latest still moves to wordpress-versions/<version>.

This isn't only a CI problem: a user's first launch after installing or updating Studio runs the same concurrent commands.

Not changed here: downloadWordPress (the throttled update from wordpress.org) also copies straight into its target. It runs at most once a day, and nothing in the logs shows it racing, so it's left for a separate change if needed.

Testing Instructions

Primary evidence, the real path: Windows desktop E2E on this PR (build 23494) logged no EBUSY and no Failed to set up dependency WordPress version errors. Trunk's run of the same 37 tests (23478) logged 18 and 9. Every E2E and CLI E2E job passed.

npx vitest run apps/cli/lib/dependency-management/tests/setup-server-files.test.ts is one test on real temp folders. It starts from an interrupted earlier copy, runs eight setupServerFiles() calls at once, and watches latest while they run. It checks that latest never shows version.php before every file is there, and that it ends as one complete copy. On trunk's code it fails (31–32 partial sightings per run); with the fix it passes, 3 of 3 runs.

Pre-merge Checklist

  • Windows E2E log has no Failed to set up dependency WordPress version errors.

…ncurrent commands never copy a partial install
@wpmobilebot

wpmobilebot commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing 0134e5f vs trunk

app-size

Metric trunk 0134e5f Diff Change
App Size (Mac) 954.28 MB 954.28 MB +0.00 MB ⚪ 0.0%

site-editor

Metric trunk 0134e5f Diff Change
load 1215 ms 1212 ms 3 ms ⚪ 0.0%

site-startup

Metric trunk 0134e5f Diff Change
siteCreation 6002 ms 5562 ms 440 ms 🟢 -7.3%
siteStartup 2861 ms 2874 ms +13 ms ⚪ 0.0%

Results are median values from multiple test runs.

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

@chubes4
chubes4 merged commit 2e81455 into trunk Oct 27, 2026
12 checks passed
@chubes4
chubes4 deleted the cli/install-latest-wordpress-atomically branch October 27, 2026 15:05
chubes4 added a commit that referenced this pull request Oct 27, 2026
…ws CI's time limit (#5499)

## Related issues

- Follows #5314, which added this test.

## How AI was used in this PR

Claude (via the Pi coding agent in a Roadie session) found the Windows
timeouts in CI logs and wrote the change. I reviewed it.

## Proposed Changes

The test from #5314 runs eight concurrent installs of a 400-file
WordPress copy. It takes about 1 s on macOS but sometimes exceeds
Vitest's default 5 s on the Windows Unit Tests agent, which failed
Windows Unit Tests on #5494 (build 24210) and #5495 (build 24211) with
`Test timed out in 5000ms`. Its trunk runs passed, so it is
intermittent.

The size adds no detection. Run 5 times each on macOS against #5314's
fix and against the code before it:

| Size | With the fix | Before the fix |
| --- | --- | --- |
| 8 installs × 400 files | 5/5 pass | 5/5 fail |
| 4 installs × 100 files | 5/5 pass | 5/5 fail |
| 2 installs × 50 files | 5/5 pass | 5/5 fail |

Before the fix, the failing check is the one this test exists for:
`latest` shows `version.php` before all its files are there. This
shrinks the test to two installs of 50 files, which keeps the concurrent
and interrupted-copy paths at about 1/30 of the file work.

## Testing Instructions

- `npx vitest run --project cli
apps/cli/lib/dependency-management/tests/setup-server-files.test.ts`
passes. With `apps/cli/lib/dependency-management/setup.ts` from before
#5314, it fails.
- Windows Unit Tests pass in CI.

## Pre-merge Checklist

- [x] 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.

2 participants