From 40a3375486aa8c3cabc54f5cc5ba5eff486a3482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20R=C3=BCtter?= Date: Sat, 4 Jul 2026 20:43:40 +0000 Subject: [PATCH 1/2] Force directory creation to avoid a race between workers Co-Authored-By: Claude Fable 5 --- src/Page.php | 4 +++- tests/PageTest.php | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Page.php b/src/Page.php index 3883092..f09e184 100644 --- a/src/Page.php +++ b/src/Page.php @@ -65,7 +65,9 @@ protected function write($request) $html = $response->getContent(); if (! $this->files->exists($this->directory())) { - $this->files->makeDirectory($this->directory(), 0755, true); + // Force the creation, since when running with multiple workers, + // another worker may have created the directory in the meantime. + $this->files->makeDirectory($this->directory(), 0755, true, true); } $this->files->put($this->path(), $html); diff --git a/tests/PageTest.php b/tests/PageTest.php index 119df1e..498ee1d 100644 --- a/tests/PageTest.php +++ b/tests/PageTest.php @@ -3,6 +3,8 @@ namespace Tests; use Illuminate\Filesystem\Filesystem; +use Illuminate\Http\Request; +use Illuminate\Http\Response; use PHPUnit\Framework\Attributes\Test; use Statamic\Contracts\Entries\Entry; use Statamic\StaticSite\Page; @@ -45,6 +47,34 @@ public function it_gets_the_path_of_the_404_url() $this->assertEquals('/path/to/static', $page->directory()); } + #[Test] + public function it_writes_when_another_worker_creates_the_directory_after_the_existence_check() + { + // Simulate the race between parallel workers, where the directory doesn't + // exist when checked but has appeared by the time mkdir runs. + $files = new class extends Filesystem + { + public function exists($path) + { + return false; + } + }; + + $destination = base_path('static'); + $files->deleteDirectory($destination); + $files->makeDirectory($destination.'/foo/bar', 0755, true); + + $entry = $this->mock(Entry::class); + $entry->shouldReceive('urlWithoutRedirect')->andReturn('/foo/bar'); + $entry->shouldReceive('toResponse')->andReturn(new Response('html')); + + $page = new Page($files, ['destination' => $destination], $entry); + + $page->generate(Request::create('/foo/bar')); + + $this->assertFileExists($destination.'/foo/bar/index.html'); + } + private function page($entry, $config) { return new Page($this->mock(Filesystem::class), $config, $entry); From ee70eae4087d45dc12656ea296e49ada49abb90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joachim=20R=C3=BCtter?= Date: Mon, 6 Jul 2026 08:00:59 +0000 Subject: [PATCH 2/2] Fix code style Co-Authored-By: Claude Fable 5 --- src/Commands/StaticSiteServe.php | 6 +++--- tests/TestCase.php | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Commands/StaticSiteServe.php b/src/Commands/StaticSiteServe.php index 5f1b4c1..ff63a6f 100644 --- a/src/Commands/StaticSiteServe.php +++ b/src/Commands/StaticSiteServe.php @@ -33,7 +33,7 @@ class StaticSiteServe extends Command /** * The list of requests being handled and their start time. * - * @var array + * @var array */ protected $requestsPool; @@ -68,7 +68,7 @@ public function handle() * Start a new server process. * * @param bool $hasEnvironment - * @return \Symfony\Component\Process\Process + * @return Process */ protected function startProcess() { @@ -208,7 +208,7 @@ protected function handleProcessOutput() * Get the date from the given PHP server output. * * @param string $line - * @return \Illuminate\Support\Carbon + * @return Carbon */ protected function getDateFromLine($line) { diff --git a/tests/TestCase.php b/tests/TestCase.php index 88df663..fbeb963 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -4,7 +4,9 @@ use Illuminate\Filesystem\Filesystem; use Orchestra\Testbench\TestCase as OrchestraTestCase; +use Statamic\Providers\StatamicServiceProvider; use Statamic\Statamic; +use Wilderborn\Partyline\ServiceProvider; class TestCase extends OrchestraTestCase { @@ -14,8 +16,8 @@ class TestCase extends OrchestraTestCase protected function getPackageProviders($app) { return [ - \Statamic\Providers\StatamicServiceProvider::class, - \Wilderborn\Partyline\ServiceProvider::class, + StatamicServiceProvider::class, + ServiceProvider::class, \Statamic\StaticSite\ServiceProvider::class, ]; }