diff --git a/.ai/lessons.md b/.ai/lessons.md index 5cde3ef34e..190bbee34e 100644 --- a/.ai/lessons.md +++ b/.ai/lessons.md @@ -13,6 +13,7 @@ - Redirect browser test output to a file (`> /tmp/x.log 2>&1`); piping it (`| tail`) hangs because the Playwright server keeps the pipe open. - Browser `click('Text')` can match a hidden element first (for example inactive modal steps) and wait forever; use `button:visible:has-text("Text")`. - Never run `php artisan test` inside a dev container (`./scripts/dev exec` or `docker exec coolify-dev-*`). The container sets `DB_CONNECTION=pgsql` as a real environment variable, which wins over `phpunit.xml`, so `RefreshDatabase` runs `migrate:fresh` on the dev database and wipes it. Run tests on the host only. +- Do not symlink `vendor` into another worktree. Composer resolves the base path from the real `vendor` path, so the tests load the app classes of the other checkout. Run `composer install --ignore-platform-req=ext-zip` in the worktree. - Call `visit()` directly in each `tests/v4/Browser` test body; Pest does not mark a test that only uses helper-wrapped `visit()` as a browser test, so it fails with `sendText() on null`. ## Verify the complete user flow diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 54d0c8f533..2b67107484 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -3461,7 +3461,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue $environmentPrefix = $this->railpack_build_environment_prefix($controlVariables); $secretFlags = $this->railpack_build_secret_flags($variables); - $frontendImage = 'ghcr.io/railwayapp/railpack-frontend:v'.config('constants.coolify.railpack_version'); + // The helper image sets RAILPACK_VERSION, so the frontend always matches the Railpack CLI in the helper. + // Helper images without it fall back to the version of the Railpack CLI itself. + $frontendImage = 'ghcr.io/railwayapp/railpack-frontend:v${RAILPACK_VERSION:-$(railpack --version | cut -d " " -f 3)}'; $buildxBuildCommand = "{$environmentPrefix}DOCKER_CONFIG=/root/.docker docker buildx build --builder coolify-railpack" ." {$this->addHosts} --network host" diff --git a/config/constants.php b/config/constants.php index 6307bbfaeb..4a24c685df 100644 --- a/config/constants.php +++ b/config/constants.php @@ -7,7 +7,6 @@ return [ 'coolify' => [ 'version' => env('COOLIFY_VERSION') ?: '4.4.4', 'helper_version' => '1.0.17', - 'railpack_version' => '0.23.0', 'self_hosted' => env('SELF_HOSTED', true), 'autoupdate' => env('AUTOUPDATE'), 'base_config_path' => env('BASE_CONFIG_PATH', '/data/coolify'), diff --git a/tests/Unit/ApplicationDeploymentRailpackConfigTest.php b/tests/Unit/ApplicationDeploymentRailpackConfigTest.php index 168e8be881..1a6297153a 100644 --- a/tests/Unit/ApplicationDeploymentRailpackConfigTest.php +++ b/tests/Unit/ApplicationDeploymentRailpackConfigTest.php @@ -323,7 +323,69 @@ it('builds railpack docker command with matching env and secret flags for all ra expect($command)->toContain("--secret 'id=RAILPACK_DEPLOY_APT_PACKAGES,env=RAILPACK_DEPLOY_APT_PACKAGES'"); expect($command)->toContain("--secret 'id=SECRET_JSON,env=SECRET_JSON'"); expect($command)->toContain(' --build-arg secrets-hash='); - expect($command)->toContain('--build-arg BUILDKIT_SYNTAX="ghcr.io/railwayapp/railpack-frontend:v'.config('constants.coolify.railpack_version').'"'); + expect($command)->toContain('--build-arg BUILDKIT_SYNTAX="ghcr.io/railwayapp/railpack-frontend:v${RAILPACK_VERSION:-$(railpack --version | cut -d " " -f 3)}"'); +}); + +/** + * Runs a railpack build command in bash with stub docker and railpack binaries and returns the docker arguments. + * + * @param array $environment + * @return array{exitCode: int, output: string} + */ +function runRailpackBuildCommandWithStubDocker(string $command, array $environment): array +{ + $binDirectory = sys_get_temp_dir().'/railpack-stub-'.uniqid(); + mkdir($binDirectory); + file_put_contents($binDirectory.'/docker', "#!/bin/sh\nprintf '%s\\n' \"\$@\"\n"); + file_put_contents($binDirectory.'/railpack', "#!/bin/sh\necho 'railpack version 0.30.0'\n"); + chmod($binDirectory.'/docker', 0755); + chmod($binDirectory.'/railpack', 0755); + + $process = proc_open( + ['bash', '-c', 'exec 2>&1; cd() { :; }; source() { :; }; '.$command], + [1 => ['pipe', 'w']], + $pipes, + null, + ['PATH' => $binDirectory.':'.getenv('PATH'), ...$environment], + ); + $output = stream_get_contents($pipes[1]); + fclose($pipes[1]); + $exitCode = proc_close($process); + + unlink($binDirectory.'/docker'); + unlink($binDirectory.'/railpack'); + rmdir($binDirectory); + + return ['exitCode' => $exitCode, 'output' => $output]; +} + +it('uses the railpack frontend version from the helper environment', function () { + [$job, $reflection] = makeRailpackDeploymentJob(['uuid' => 'application-uuid']); + + $command = invokeRailpackMethod($job, $reflection, 'railpack_build_command', [ + 'coollabsio/coolify:test', + collect(['RAILPACK_VERSION' => '9.9.9']), + ]); + + $result = runRailpackBuildCommandWithStubDocker($command, ['RAILPACK_VERSION' => '0.26.1']); + + expect($result['exitCode'])->toBe(0) + ->and($result['output'])->toContain('BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v0.26.1') + ->and($result['output'])->not->toContain('railpack-frontend:v9.9.9'); +}); + +it('falls back to the railpack CLI version when the helper does not set RAILPACK_VERSION', function () { + [$job, $reflection] = makeRailpackDeploymentJob(['uuid' => 'application-uuid']); + + $command = invokeRailpackMethod($job, $reflection, 'railpack_build_command', [ + 'coollabsio/coolify:test', + collect(), + ]); + + $result = runRailpackBuildCommandWithStubDocker($command, []); + + expect($result['exitCode'])->toBe(0) + ->and($result['output'])->toContain('BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v0.30.0'); }); it('interpolates build-time variable references for railpack by sourcing the build-time env file', function () {