From b6ca6b1b2038795481027580773fd86e4183fad4 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 30 Apr 2026 18:31:41 +0200 Subject: [PATCH] feat(railpack): expose COOLIFY_* vars at build time and generalize buildpack control flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors Nixpacks behavior: inject COOLIFY_* and SOURCE_COMMIT into railpack build variables so apps (e.g. SPAs baking public URLs) can read them via /run/secrets/. Rename is_nixpacks → is_buildpack_control to cover both NIXPACKS_ and RAILPACK_ prefixed keys. Update the env variable view and appends list accordingly. Promote generate_coolify_env_variables to protected for testability. --- app/Jobs/ApplicationDeploymentJob.php | 10 ++- app/Models/EnvironmentVariable.php | 12 +--- .../environment-variable/show.blade.php | 8 +-- ...icationDeploymentRailpackEnvParityTest.php | 69 +++++++++++++++++++ ...nvironmentVariableBuildpackControlTest.php | 37 ++++++++++ 5 files changed, 122 insertions(+), 14 deletions(-) create mode 100644 tests/Unit/EnvironmentVariableBuildpackControlTest.php diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 6f9aabfd5..0131d218a 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -2527,6 +2527,14 @@ private function railpack_build_variables(): Collection $variables->put('RAILPACK_INSTALL_CMD', $this->application->install_command); } + // Mirror Nixpacks behavior: expose COOLIFY_* and SOURCE_COMMIT to the build so apps + // (e.g. SPAs baking the public URL) can read them via /run/secrets/. + foreach ($this->generate_coolify_env_variables(forBuildTime: true) as $key => $value) { + if (! is_null($value) && $value !== '') { + $variables->put($key, $value); + } + } + return $variables; } @@ -2829,7 +2837,7 @@ private function build_railpack_static_image(): void ); } - private function generate_coolify_env_variables(bool $forBuildTime = false): Collection + protected function generate_coolify_env_variables(bool $forBuildTime = false): Collection { $coolify_envs = collect([]); $local_branch = $this->branch; diff --git a/app/Models/EnvironmentVariable.php b/app/Models/EnvironmentVariable.php index 25e2dcfb3..ac0d238b3 100644 --- a/app/Models/EnvironmentVariable.php +++ b/app/Models/EnvironmentVariable.php @@ -77,7 +77,7 @@ class EnvironmentVariable extends BaseModel 'resourceable_id' => 'integer', ]; - protected $appends = ['real_value', 'is_shared', 'is_really_required', 'is_nixpacks', 'is_coolify']; + protected $appends = ['real_value', 'is_shared', 'is_really_required', 'is_buildpack_control', 'is_coolify']; protected static function booted() { @@ -215,16 +215,10 @@ protected function isReallyRequired(): Attribute ); } - protected function isNixpacks(): Attribute + protected function isBuildpackControl(): Attribute { return Attribute::make( - get: function () { - if (str($this->key)->startsWith('NIXPACKS_')) { - return true; - } - - return false; - } + get: fn () => self::isBuildpackControlKey($this->key), ); } diff --git a/resources/views/livewire/project/shared/environment-variable/show.blade.php b/resources/views/livewire/project/shared/environment-variable/show.blade.php index 6630d0500..cbb7afa2f 100644 --- a/resources/views/livewire/project/shared/environment-variable/show.blade.php +++ b/resources/views/livewire/project/shared/environment-variable/show.blade.php @@ -58,7 +58,7 @@ @endif @else - @if (!$env->is_nixpacks) + @if (!$env->is_buildpack_control) @@ -67,7 +67,7 @@ helper="Make this variable available in the running container at runtime." label="Available at Runtime" /> @if (!$isMagicVariable) - @if (!$env->is_nixpacks) + @if (!$env->is_buildpack_control) @if ($is_multiline === false) @endif @else - @if (!$env->is_nixpacks) + @if (!$env->is_buildpack_control) @@ -245,7 +245,7 @@ helper="Make this variable available in the running container at runtime." label="Available at Runtime" /> @if (!$isMagicVariable) - @if (!$env->is_nixpacks) + @if (!$env->is_buildpack_control) @if ($is_multiline === false) makePartial(); $job->shouldAllowMockingProtectedMethods(); + $job->shouldReceive('generate_coolify_env_variables')->andReturn(collect([])); $reflection = new ReflectionClass(ApplicationDeploymentJob::class); $applicationProperty = $reflection->getProperty('application'); @@ -102,6 +103,7 @@ $job = Mockery::mock(ApplicationDeploymentJob::class)->makePartial(); $job->shouldAllowMockingProtectedMethods(); + $job->shouldReceive('generate_coolify_env_variables')->andReturn(collect([])); $reflection = new ReflectionClass(ApplicationDeploymentJob::class); $applicationProperty = $reflection->getProperty('application'); @@ -124,3 +126,70 @@ 'RAILPACK_PREVIEW_ONLY' => 'preview-value', ]); }); + +it('merges coolify env variables into railpack build variables', function () { + $application = Mockery::mock(Application::class); + $application->shouldReceive('getAttribute')->with('install_command')->andReturn(null); + + $userVar = Mockery::mock(EnvironmentVariable::class)->makePartial(); + $userVar->forceFill([ + 'key' => 'MY_BUILD_VAR', + 'is_literal' => false, + 'is_multiline' => false, + ]); + $userVar->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn('hello'); + + $envQuery = Mockery::mock(); + $envQuery->shouldReceive('where')->with('is_buildtime', true)->once()->andReturnSelf(); + $envQuery->shouldReceive('get')->once()->andReturn(collect([$userVar])); + $application->shouldReceive('environment_variables')->once()->andReturn($envQuery); + + $job = Mockery::mock(ApplicationDeploymentJob::class)->makePartial(); + $job->shouldAllowMockingProtectedMethods(); + $job->shouldReceive('generate_coolify_env_variables') + ->with(true) + ->andReturn(collect([ + 'COOLIFY_URL' => 'https://app.example.com', + 'COOLIFY_FQDN' => 'app.example.com', + 'COOLIFY_BRANCH' => 'main', + 'COOLIFY_RESOURCE_UUID' => 'app-uuid', + 'SOURCE_COMMIT' => 'abc123', + 'EMPTY_VAR' => '', + 'NULL_VAR' => null, + ])); + + $reflection = new ReflectionClass(ApplicationDeploymentJob::class); + $applicationProperty = $reflection->getProperty('application'); + $applicationProperty->setAccessible(true); + $applicationProperty->setValue($job, $application); + + $pullRequestProperty = $reflection->getProperty('pull_request_id'); + $pullRequestProperty->setAccessible(true); + $pullRequestProperty->setValue($job, 0); + + $mainServerProperty = $reflection->getProperty('mainServer'); + $mainServerProperty->setAccessible(true); + $mainServerProperty->setValue($job, Mockery::mock(Server::class)); + + $method = $reflection->getMethod('generate_railpack_env_variables'); + $method->setAccessible(true); + $variables = $method->invoke($job); + + expect($variables->all())->toBe([ + 'MY_BUILD_VAR' => 'hello', + 'COOLIFY_URL' => 'https://app.example.com', + 'COOLIFY_FQDN' => 'app.example.com', + 'COOLIFY_BRANCH' => 'main', + 'COOLIFY_RESOURCE_UUID' => 'app-uuid', + 'SOURCE_COMMIT' => 'abc123', + ]); + + $envArgsProperty = $reflection->getProperty('env_railpack_args'); + $envArgsProperty->setAccessible(true); + $envArgs = $envArgsProperty->getValue($job); + + expect($envArgs)->toContain("--env 'COOLIFY_URL=https://app.example.com'"); + expect($envArgs)->toContain("--env 'SOURCE_COMMIT=abc123'"); + expect($envArgs)->not->toContain('EMPTY_VAR'); + expect($envArgs)->not->toContain('NULL_VAR'); +}); diff --git a/tests/Unit/EnvironmentVariableBuildpackControlTest.php b/tests/Unit/EnvironmentVariableBuildpackControlTest.php new file mode 100644 index 000000000..1a277bcdd --- /dev/null +++ b/tests/Unit/EnvironmentVariableBuildpackControlTest.php @@ -0,0 +1,37 @@ +key = 'NIXPACKS_NODE_VERSION'; + + expect($env->is_buildpack_control)->toBeTrue(); +}); + +it('flags RAILPACK_ keys as buildpack control variables', function () { + $env = new EnvironmentVariable; + $env->key = 'RAILPACK_NODE_VERSION'; + + expect($env->is_buildpack_control)->toBeTrue(); +}); + +it('does not flag user-defined keys as buildpack control variables', function () { + $env = new EnvironmentVariable; + $env->key = 'MY_BUILD_VAR'; + + expect($env->is_buildpack_control)->toBeFalse(); +}); + +it('does not flag empty key as buildpack control variable', function () { + $env = new EnvironmentVariable; + + expect($env->is_buildpack_control)->toBeFalse(); +}); + +it('lists is_buildpack_control in appends and drops legacy is_nixpacks', function () { + $env = new EnvironmentVariable; + + expect($env->getAppends())->toContain('is_buildpack_control'); + expect($env->getAppends())->not->toContain('is_nixpacks'); +});