From cddbaf581fd319d9266f31a97194c04ea166fe1e Mon Sep 17 00:00:00 2001 From: Aditya Tripathi Date: Mon, 23 Mar 2026 19:02:10 +0000 Subject: [PATCH] refactor(railpack): extract static image build, fix port logic, bump to v0.22.0 Extract build_railpack_static_image() into its own method, prevent port override when is_static is set, bump Railpack to 0.22.0, and improve test setup with beforeEach and correct polymorphic env var fields. --- app/Jobs/ApplicationDeploymentJob.php | 53 ++++++++++--------- .../Project/New/GithubPrivateRepository.php | 4 +- .../New/GithubPrivateRepositoryDeployKey.php | 4 +- .../Project/New/PublicGitRepository.php | 4 +- docker/coolify-helper/Dockerfile | 2 +- .../ApplicationBuildpackCleanupTest.php | 50 ++++++++++++++--- tests/Feature/ApplicationRailpackTest.php | 38 ++++--------- 7 files changed, 93 insertions(+), 62 deletions(-) diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 33905ce59..460a7bf3d 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -483,7 +483,7 @@ private function decide_what_to_do() } elseif ($this->application->build_pack === 'railpack') { $this->deploy_railpack_buildpack(); } else { - throw new \RuntimeException("Unsupported build pack: {$this->application->build_pack}"); + throw new DeploymentException("Unsupported build pack: {$this->application->build_pack}"); } $this->post_deployment(); } @@ -2517,35 +2517,40 @@ private function build_railpack_image(): void // Step 3: If static, copy built assets into nginx image if ($this->application->settings->is_static) { - $publishDir = trim($this->application->publish_directory, '/'); - $publishDir = $publishDir ? "/{$publishDir}" : ''; - $dockerfile = base64_encode("FROM {$this->application->static_image} + $this->build_railpack_static_image(); + } + } + + private function build_railpack_static_image(): void + { + $publishDir = trim($this->application->publish_directory, '/'); + $publishDir = $publishDir ? "/{$publishDir}" : ''; + $dockerfile = base64_encode("FROM {$this->application->static_image} WORKDIR /usr/share/nginx/html/ LABEL coolify.deploymentId={$this->deployment_uuid} COPY --from={$this->build_image_name} /app{$publishDir} . COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); - if (str($this->application->custom_nginx_configuration)->isNotEmpty()) { - $nginx_config = base64_encode($this->application->custom_nginx_configuration); - } else { - $nginx_config = $this->application->settings->is_spa - ? base64_encode(defaultNginxConfiguration('spa')) - : base64_encode(defaultNginxConfiguration()); - } - - $static_build = $this->dockerBuildkitSupported - ? "DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile --progress plain -t {$this->production_image_name} {$this->workdir}" - : "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile -t {$this->production_image_name} {$this->workdir}"; - - $base64_static_build = base64_encode($static_build); - $this->execute_remote_command( - [executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null")], - [executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null")], - [executeInDocker($this->deployment_uuid, "echo '{$base64_static_build}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'), 'hidden' => true], - [executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH), 'hidden' => true], - [executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH), 'hidden' => true], - ); + if (str($this->application->custom_nginx_configuration)->isNotEmpty()) { + $nginx_config = base64_encode($this->application->custom_nginx_configuration); + } else { + $nginx_config = $this->application->settings->is_spa + ? base64_encode(defaultNginxConfiguration('spa')) + : base64_encode(defaultNginxConfiguration()); } + + $static_build = $this->dockerBuildkitSupported + ? "DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile --progress plain -t {$this->production_image_name} {$this->workdir}" + : "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile -t {$this->production_image_name} {$this->workdir}"; + + $base64_static_build = base64_encode($static_build); + $this->execute_remote_command( + [executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null")], + [executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null")], + [executeInDocker($this->deployment_uuid, "echo '{$base64_static_build}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'), 'hidden' => true], + [executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH), 'hidden' => true], + [executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH), 'hidden' => true], + ); } private function generate_coolify_env_variables(bool $forBuildTime = false): Collection diff --git a/app/Livewire/Project/New/GithubPrivateRepository.php b/app/Livewire/Project/New/GithubPrivateRepository.php index c208e2cd2..63240620b 100644 --- a/app/Livewire/Project/New/GithubPrivateRepository.php +++ b/app/Livewire/Project/New/GithubPrivateRepository.php @@ -84,7 +84,9 @@ public function updatedBuildPack() { if ($this->build_pack === 'nixpacks' || $this->build_pack === 'railpack') { $this->show_is_static = true; - $this->port = 3000; + if (! $this->is_static) { + $this->port = 3000; + } } elseif ($this->build_pack === 'static') { $this->show_is_static = false; $this->is_static = false; diff --git a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php index f312a9dc0..92d388234 100644 --- a/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php +++ b/app/Livewire/Project/New/GithubPrivateRepositoryDeployKey.php @@ -97,7 +97,9 @@ public function updatedBuildPack() { if ($this->build_pack === 'nixpacks' || $this->build_pack === 'railpack') { $this->show_is_static = true; - $this->port = 3000; + if (! $this->is_static) { + $this->port = 3000; + } } elseif ($this->build_pack === 'static') { $this->show_is_static = false; $this->is_static = false; diff --git a/app/Livewire/Project/New/PublicGitRepository.php b/app/Livewire/Project/New/PublicGitRepository.php index eb4ce7b84..dd7a682d1 100644 --- a/app/Livewire/Project/New/PublicGitRepository.php +++ b/app/Livewire/Project/New/PublicGitRepository.php @@ -101,7 +101,9 @@ public function updatedBuildPack() { if ($this->build_pack === 'nixpacks' || $this->build_pack === 'railpack') { $this->show_is_static = true; - $this->port = 3000; + if (! $this->isStatic) { + $this->port = 3000; + } } elseif ($this->build_pack === 'static') { $this->show_is_static = false; $this->isStatic = false; diff --git a/docker/coolify-helper/Dockerfile b/docker/coolify-helper/Dockerfile index ebe667437..1f4ca8788 100644 --- a/docker/coolify-helper/Dockerfile +++ b/docker/coolify-helper/Dockerfile @@ -12,7 +12,7 @@ ARG PACK_VERSION=0.38.2 # https://github.com/railwayapp/nixpacks/releases ARG NIXPACKS_VERSION=1.41.0 # https://github.com/railwayapp/railpack/releases -ARG RAILPACK_VERSION=0.21.0 +ARG RAILPACK_VERSION=0.22.0 # https://github.com/jdx/mise/releases — must match railpack's pinned version (https://raw.githubusercontent.com/railwayapp/railpack/refs/heads/main/core/mise/version.txt) ARG MISE_VERSION=2026.3.12 # https://github.com/minio/mc/releases diff --git a/tests/Feature/ApplicationBuildpackCleanupTest.php b/tests/Feature/ApplicationBuildpackCleanupTest.php index 857410920..0dc0a8303 100644 --- a/tests/Feature/ApplicationBuildpackCleanupTest.php +++ b/tests/Feature/ApplicationBuildpackCleanupTest.php @@ -78,26 +78,29 @@ // Add environment variables that should be deleted EnvironmentVariable::create([ - 'application_id' => $application->id, + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, 'key' => 'SERVICE_FQDN_APP', 'value' => 'app.example.com', - 'is_build_time' => false, + 'is_buildtime' => false, 'is_preview' => false, ]); EnvironmentVariable::create([ - 'application_id' => $application->id, + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, 'key' => 'SERVICE_URL_APP', 'value' => 'http://app.example.com', - 'is_build_time' => false, + 'is_buildtime' => false, 'is_preview' => false, ]); EnvironmentVariable::create([ - 'application_id' => $application->id, + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, 'key' => 'REGULAR_VAR', 'value' => 'should_remain', - 'is_build_time' => false, + 'is_buildtime' => false, 'is_preview' => false, ]); @@ -154,6 +157,34 @@ 'docker_compose_raw' => 'version: "3.8"\nservices:\n app:\n image: nginx', ]); + // Add environment variables that should be deleted + EnvironmentVariable::create([ + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + 'key' => 'SERVICE_FQDN_APP', + 'value' => 'app.example.com', + 'is_buildtime' => false, + 'is_preview' => false, + ]); + + EnvironmentVariable::create([ + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + 'key' => 'SERVICE_URL_APP', + 'value' => 'http://app.example.com', + 'is_buildtime' => false, + 'is_preview' => false, + ]); + + EnvironmentVariable::create([ + 'resourceable_type' => Application::class, + 'resourceable_id' => $application->id, + 'key' => 'REGULAR_VAR', + 'value' => 'should_remain', + 'is_buildtime' => false, + 'is_preview' => false, + ]); + $application->build_pack = 'railpack'; $application->save(); $application->refresh(); @@ -161,6 +192,13 @@ expect($application->build_pack)->toBe('railpack'); expect($application->docker_compose_domains)->toBeNull(); expect($application->docker_compose_raw)->toBeNull(); + + // Verify SERVICE_FQDN_* and SERVICE_URL_* were deleted + expect($application->environment_variables()->where('key', 'SERVICE_FQDN_APP')->count())->toBe(0); + expect($application->environment_variables()->where('key', 'SERVICE_URL_APP')->count())->toBe(0); + + // Verify regular variables remain + expect($application->environment_variables()->where('key', 'REGULAR_VAR')->count())->toBe(1); }); test('model does not clear dockerfile fields when switching to dockerfile', function () { diff --git a/tests/Feature/ApplicationRailpackTest.php b/tests/Feature/ApplicationRailpackTest.php index f3e49cc21..59e8a82e0 100644 --- a/tests/Feature/ApplicationRailpackTest.php +++ b/tests/Feature/ApplicationRailpackTest.php @@ -10,13 +10,15 @@ uses(RefreshDatabase::class); describe('Application Railpack Support', function () { - test('could_set_build_commands returns true for railpack', function () { + beforeEach(function () { $team = Team::factory()->create(); $project = Project::factory()->create(['team_id' => $team->id]); - $environment = Environment::factory()->create(['project_id' => $project->id]); + $this->environment = Environment::factory()->create(['project_id' => $project->id]); + }); + test('could_set_build_commands returns true for railpack', function () { $application = Application::factory()->create([ - 'environment_id' => $environment->id, + 'environment_id' => $this->environment->id, 'build_pack' => 'railpack', ]); @@ -24,12 +26,8 @@ }); test('could_set_build_commands returns true for nixpacks', function () { - $team = Team::factory()->create(); - $project = Project::factory()->create(['team_id' => $team->id]); - $environment = Environment::factory()->create(['project_id' => $project->id]); - $application = Application::factory()->create([ - 'environment_id' => $environment->id, + 'environment_id' => $this->environment->id, 'build_pack' => 'nixpacks', ]); @@ -37,12 +35,8 @@ }); test('could_set_build_commands returns false for dockerfile', function () { - $team = Team::factory()->create(); - $project = Project::factory()->create(['team_id' => $team->id]); - $environment = Environment::factory()->create(['project_id' => $project->id]); - $application = Application::factory()->create([ - 'environment_id' => $environment->id, + 'environment_id' => $this->environment->id, 'build_pack' => 'dockerfile', ]); @@ -50,12 +44,8 @@ }); test('railpack_environment_variables returns only RAILPACK_ prefixed vars', function () { - $team = Team::factory()->create(); - $project = Project::factory()->create(['team_id' => $team->id]); - $environment = Environment::factory()->create(['project_id' => $project->id]); - $application = Application::factory()->create([ - 'environment_id' => $environment->id, + 'environment_id' => $this->environment->id, 'build_pack' => 'railpack', ]); @@ -92,12 +82,8 @@ }); test('runtime_environment_variables excludes RAILPACK_ and NIXPACKS_ prefixed vars', function () { - $team = Team::factory()->create(); - $project = Project::factory()->create(['team_id' => $team->id]); - $environment = Environment::factory()->create(['project_id' => $project->id]); - $application = Application::factory()->create([ - 'environment_id' => $environment->id, + 'environment_id' => $this->environment->id, 'build_pack' => 'railpack', ]); @@ -134,12 +120,8 @@ }); test('railpack_environment_variables_preview returns only RAILPACK_ prefixed preview vars', function () { - $team = Team::factory()->create(); - $project = Project::factory()->create(['team_id' => $team->id]); - $environment = Environment::factory()->create(['project_id' => $project->id]); - $application = Application::factory()->create([ - 'environment_id' => $environment->id, + 'environment_id' => $this->environment->id, 'build_pack' => 'railpack', ]);