From 38fb54e4cab0f755d4bf5c0278b16d4ba7a380d3 Mon Sep 17 00:00:00 2001 From: Jase Date: Fri, 3 Apr 2026 15:13:29 -0700 Subject: [PATCH 1/4] fix(cli): preload project autoloader in bin --- packages/cli/bin/marko | 14 +++++++++----- packages/cli/tests/BinMarkoTest.php | 19 ++++++++++++++----- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/packages/cli/bin/marko b/packages/cli/bin/marko index 4905d0f7..261320e3 100755 --- a/packages/cli/bin/marko +++ b/packages/cli/bin/marko @@ -21,11 +21,15 @@ use Marko\Cli\CliKernel; use Marko\Cli\ProjectFinder; try { - $kernel = new CliKernel( - projectFinder: new ProjectFinder(), - ); - $exitCode = $kernel->run($argv); - exit($exitCode); + $projectFinder = new ProjectFinder(); + + if (($projectRoot = $projectFinder->find()) !== null) { + require_once "{$projectRoot}/vendor/autoload.php"; + } + + exit((new CliKernel( + projectFinder: $projectFinder, + ))->run($argv)); } catch (Throwable $e) { fwrite(STDERR, "Error: " . $e->getMessage() . PHP_EOL); exit(1); diff --git a/packages/cli/tests/BinMarkoTest.php b/packages/cli/tests/BinMarkoTest.php index 1ad5c58c..28e52ce2 100644 --- a/packages/cli/tests/BinMarkoTest.php +++ b/packages/cli/tests/BinMarkoTest.php @@ -24,21 +24,30 @@ $content = file_get_contents($binMarkoPath); expect($content)->toContain('use Marko\Cli\CliKernel') - ->and($content)->toContain('new CliKernel'); + ->and($content)->toContain('new CliKernel') + ->and($content)->toContain('projectFinder: $projectFinder'); }); it('passes argv to kernel run method', function () use ($binMarkoPath) { $content = file_get_contents($binMarkoPath); - expect($content)->toContain('$kernel->run($argv)'); + expect($content)->toContain('->run($argv)'); }); it('exits with code returned from kernel', function () use ($binMarkoPath) { $content = file_get_contents($binMarkoPath); - // Should capture exit code and use it - expect($content)->toContain('$exitCode') - ->and($content)->toContain('exit($exitCode)'); + expect($content)->toContain('exit((new CliKernel(') + ->and($content)->toContain('))->run($argv));'); +}); + +it('loads the project autoloader when a project root is found', function () use ($binMarkoPath) { + $content = file_get_contents($binMarkoPath); + + expect($content)->toContain('use Marko\Cli\ProjectFinder') + ->and($content)->toContain('$projectFinder = new ProjectFinder()') + ->and($content)->toContain('$projectFinder->find()') + ->and($content)->toContain('require_once "{$projectRoot}/vendor/autoload.php"'); }); it('handles uncaught exceptions gracefully', function () use ($binMarkoPath) { From 8b8d3a10368b793a28c1bda274599856056bddaf Mon Sep 17 00:00:00 2001 From: Jase Date: Fri, 3 Apr 2026 16:31:25 -0700 Subject: [PATCH 2/4] test(dev-server): account for process group cleanup timing in dev down test --- .../tests/Command/DevDownCommandTest.php | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/dev-server/tests/Command/DevDownCommandTest.php b/packages/dev-server/tests/Command/DevDownCommandTest.php index 77e8c8bf..36c32ef3 100644 --- a/packages/dev-server/tests/Command/DevDownCommandTest.php +++ b/packages/dev-server/tests/Command/DevDownCommandTest.php @@ -304,10 +304,22 @@ function createDevDownCommand(array $config = [], ?string $tmpDir = null): array usleep(100000); // let signals propagate - // Both parent and child should be dead — process group killed - expect($pidFile->isProcessGroupRunning($parentPid))->toBeFalse(); - proc_close($proc); + + $groupRunning = true; + for ($i = 0; $i < 10; $i++) { + $groupRunning = $pidFile->isProcessGroupRunning($parentPid); + if ($groupRunning === false) { + break; + } + + usleep(50000); + } + + // Reap the parent process before asserting so zombie cleanup does not + // make the process group appear alive briefly on Linux. + expect($groupRunning)->toBeFalse(); + devDownRemoveDir($tmpDir); }); From aef83047e362d572ca3bcf2e342f2f7a9aa3622a Mon Sep 17 00:00:00 2001 From: Jase Date: Fri, 3 Apr 2026 16:31:25 -0700 Subject: [PATCH 3/4] test(integration): isolate clean install verification from the live workspace --- tests/IntegrationVerificationTest.php | 40 ++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/tests/IntegrationVerificationTest.php b/tests/IntegrationVerificationTest.php index fbfa9427..3f8962e8 100644 --- a/tests/IntegrationVerificationTest.php +++ b/tests/IntegrationVerificationTest.php @@ -43,15 +43,46 @@ function () use ($packagesRoot, $packages): void { it( 'removes composer.lock and vendor/ before running composer update to ensure clean resolution', function () use ($root): void { - $php = '/opt/homebrew/Cellar/php/8.5.1_2/bin/php'; + $php = PHP_BINARY; $resultFile = tempnam(sys_get_temp_dir(), 'marko_result_'); + $isolatedRoot = sys_get_temp_dir() . '/marko-clean-install-' . uniqid(); $script = <<<'SCRIPT' &1', $testOutput, @@ -96,6 +126,8 @@ function () use ($root): void { $php . ' ' . escapeshellarg($scriptFile) . ' ' . escapeshellarg($root) . ' ' . escapeshellarg($resultFile) + . ' ' . escapeshellarg($php) + . ' ' . escapeshellarg($isolatedRoot) . ' 2>/dev/null', ); @@ -121,7 +153,7 @@ function () use ($root): void { })->group('integration-destructive'); it('runs the full test suite and all tests pass', function () use ($root): void { - $php = '/opt/homebrew/Cellar/php/8.5.1_2/bin/php'; + $php = PHP_BINARY; $output = []; $exitCode = 0; exec( From 0ad4cf17268d1e1f7f79379af9e09aa3834ec94e Mon Sep 17 00:00:00 2001 From: Mark Shust Date: Sat, 4 Apr 2026 23:41:03 -0400 Subject: [PATCH 4/4] fix: remove hardcoded PHP path in favor of portable PHP_BIN env var The release script and its tests referenced a macOS-specific Homebrew PHP path. This replaces it with `${PHP_BIN:-php}` so any contributor can override via env var or rely on their PATH. Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/release-process.md | 2 +- bin/release.sh | 5 +---- tests/ReleaseScriptTest.php | 6 ++---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/.claude/release-process.md b/.claude/release-process.md index 68404812..256d530c 100644 --- a/.claude/release-process.md +++ b/.claude/release-process.md @@ -222,7 +222,7 @@ Runtime env vars for bin scripts (not stored as secrets — passed at the comman **Tests fail before release** - Fix the failing tests. They must pass before tagging. -- Run locally: `/opt/homebrew/Cellar/php/8.5.1_2/bin/php vendor/bin/pest --parallel` +- Run locally: `php vendor/bin/pest --parallel` (ensure PHP 8.5+ is on your PATH, or set `PHP_BIN` env var) **`composer update` fails locally** - Always run from the repo root (not inside a package directory) diff --git a/bin/release.sh b/bin/release.sh index 77751dd0..66522047 100755 --- a/bin/release.sh +++ b/bin/release.sh @@ -32,10 +32,7 @@ if git rev-parse "$TAG" >/dev/null 2>&1; then fi # Find PHP 8.5+ binary -PHP_BIN="/opt/homebrew/Cellar/php/8.5.1_2/bin/php" -if [[ ! -x "$PHP_BIN" ]]; then - PHP_BIN="php" -fi +PHP_BIN="${PHP_BIN:-php}" PHP_VERSION=$("$PHP_BIN" -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;') if [[ "$PHP_VERSION" != "8.5" ]]; then diff --git a/tests/ReleaseScriptTest.php b/tests/ReleaseScriptTest.php index 7499ffe0..f8eef484 100644 --- a/tests/ReleaseScriptTest.php +++ b/tests/ReleaseScriptTest.php @@ -67,12 +67,10 @@ ->and($contents)->toContain('Expected X.Y.Z'); }); -it('validates PHP version is 8.5+ (checks /opt/homebrew/Cellar/php/8.5.1_2/bin/php or system php)', function () use ($script): void { - // Script should check the Homebrew PHP path first, then fall back to system php +it('validates PHP version is 8.5+ (uses PHP_BIN env var or defaults to system php)', function () use ($script): void { $contents = file_get_contents($script); - expect($contents)->toContain('/opt/homebrew/Cellar/php/8.5.1_2/bin/php') - ->and($contents)->toContain('PHP_BIN="php"') + expect($contents)->toContain('PHP_BIN="${PHP_BIN:-php}"') ->and($contents)->toContain('8.5'); });