Repository navigation
CLI: Install the bundled WordPress into server-files atomically so concurrent commands never copy a partial install - #5314
Merged
Conversation
…ncurrent commands never copy a partial install
Collaborator
📊 Performance Test ResultsComparing 0134e5f vs trunk app-size
site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
…ordpress-atomically
…ordpress-atomically
Merged
1 task done
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?
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.
Related issues
appearance.test.ts'sbeforeAll: 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.EBUSY: resource busy or locked, copyfilefromFailed to set up dependency WordPress versionon 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
setupServerFilesas middleware. Whenserver-files/wordpress-versions/latesthas no WordPress (first run, or a fresh E2E session), it copies the bundledwp-files/latest/wordpressinto it. The app starts several CLI commands at once, so several processes copied into the same folder at the same time:EBUSY.setupServerFileslogs that and carries on.latestis a valid-looking install as soon aswp-includes/version.phplands, which happens early in the copy. Any process checking then sees WordPress there and skips its own copy.site createthen copies the incompletelatestinto 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, solatestonly ever appears complete:latestfirst, that copy is used and the staging folder is removed.latestexists but has noversion.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.lateststill moves towordpress-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
EBUSYand noFailed to set up dependency WordPress versionerrors. 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.tsis one test on real temp folders. It starts from an interrupted earlier copy, runs eightsetupServerFiles()calls at once, and watcheslatestwhile they run. It checks thatlatestnever showsversion.phpbefore 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
Failed to set up dependency WordPress versionerrors.