fix(railpack): include scoped env vars in builds
Build Railpack variables from generic build-time vars plus Railpack-specific vars, filter unrelated buildpack control vars, and ensure curl/wget deploy apt packages are present. Add coverage for standard and preview deployments.
This commit is contained in:
parent
a37c39e6c1
commit
d5946dcfca
6 changed files with 216 additions and 16 deletions
|
|
@ -2509,11 +2509,16 @@ private function normalize_resolved_build_variable_value(EnvironmentVariable $en
|
|||
*/
|
||||
private function railpack_build_variables(): Collection
|
||||
{
|
||||
$envCollection = $this->pull_request_id === 0
|
||||
? $this->application->environment_variables()->where('is_buildtime', true)->get()
|
||||
: $this->application->environment_variables_preview()->where('is_buildtime', true)->get();
|
||||
$genericBuildVariables = $this->pull_request_id === 0
|
||||
? $this->application->environment_variables()->withoutBuildpackControlVariables()->where('is_buildtime', true)->get()
|
||||
: $this->application->environment_variables_preview()->withoutBuildpackControlVariables()->where('is_buildtime', true)->get();
|
||||
|
||||
$variables = $envCollection
|
||||
$railpackVariables = $this->pull_request_id === 0
|
||||
? $this->application->railpack_environment_variables()->get()
|
||||
: $this->application->railpack_environment_variables_preview()->get();
|
||||
|
||||
$variables = $genericBuildVariables
|
||||
->merge($railpackVariables)
|
||||
->mapWithKeys(function (EnvironmentVariable $environmentVariable) {
|
||||
$value = $this->normalize_resolved_build_variable_value($environmentVariable);
|
||||
if (is_null($value) || $value === '') {
|
||||
|
|
@ -2527,6 +2532,8 @@ private function railpack_build_variables(): Collection
|
|||
$variables->put('RAILPACK_INSTALL_CMD', $this->application->install_command);
|
||||
}
|
||||
|
||||
$variables = $this->merge_railpack_deploy_apt_packages($variables);
|
||||
|
||||
// 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/<KEY>.
|
||||
foreach ($this->generate_coolify_env_variables(forBuildTime: true) as $key => $value) {
|
||||
|
|
@ -2538,6 +2545,23 @@ private function railpack_build_variables(): Collection
|
|||
return $variables;
|
||||
}
|
||||
|
||||
private function merge_railpack_deploy_apt_packages(Collection $variables): Collection
|
||||
{
|
||||
$packages = collect(preg_split('/\s+/', trim((string) $variables->get('RAILPACK_DEPLOY_APT_PACKAGES', ''))) ?: [])
|
||||
->filter()
|
||||
->values();
|
||||
|
||||
foreach (['curl', 'wget'] as $package) {
|
||||
if (! $packages->contains($package)) {
|
||||
$packages->push($package);
|
||||
}
|
||||
}
|
||||
|
||||
$variables->put('RAILPACK_DEPLOY_APT_PACKAGES', $packages->implode(' '));
|
||||
|
||||
return $variables;
|
||||
}
|
||||
|
||||
private function railpack_build_environment_prefix(Collection $variables): string
|
||||
{
|
||||
if ($variables->isEmpty()) {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -285,3 +285,104 @@ function readDeploymentJobProperty(object $job, ReflectionClass $reflection, str
|
|||
expect($job->writtenDockerfile)->not->toContain('ARG NIXPACKS_NODE_VERSION=');
|
||||
expect($job->writtenDockerfile)->not->toContain('ARG RAILPACK_NODE_VERSION=');
|
||||
});
|
||||
|
||||
it('builds railpack variables from generic buildtime vars railpack vars and coolify vars only', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture([
|
||||
'build_pack' => 'railpack',
|
||||
'fqdn' => 'https://railpack.example.com',
|
||||
'install_command' => 'pnpm install --frozen-lockfile',
|
||||
]);
|
||||
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'APP_ENV',
|
||||
'value' => 'production',
|
||||
'is_runtime' => false,
|
||||
'is_buildtime' => true,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'RUNTIME_ONLY',
|
||||
'value' => 'runtime',
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'NIXPACKS_NODE_VERSION',
|
||||
'value' => '22',
|
||||
'is_runtime' => false,
|
||||
'is_buildtime' => true,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'RAILPACK_NODE_VERSION',
|
||||
'value' => '20',
|
||||
'is_runtime' => false,
|
||||
'is_buildtime' => true,
|
||||
]);
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application->fresh(), $server, [
|
||||
'build_pack' => 'railpack',
|
||||
'branch' => 'main',
|
||||
]);
|
||||
|
||||
/** @var Collection $variables */
|
||||
$variables = invokeDeploymentJobMethod($job, $reflection, 'railpack_build_variables');
|
||||
|
||||
expect($variables->get('APP_ENV'))->toBe('production');
|
||||
expect($variables->get('RAILPACK_NODE_VERSION'))->toBe('20');
|
||||
expect($variables->get('RAILPACK_INSTALL_CMD'))->toBe('pnpm install --frozen-lockfile');
|
||||
expect($variables->get('RAILPACK_DEPLOY_APT_PACKAGES'))->toBe('curl wget');
|
||||
expect($variables->get('COOLIFY_RESOURCE_UUID'))->toBe($application->uuid);
|
||||
expect($variables->has('NIXPACKS_NODE_VERSION'))->toBeFalse();
|
||||
expect($variables->has('RUNTIME_ONLY'))->toBeFalse();
|
||||
});
|
||||
|
||||
it('builds preview railpack variables without leaking stale nixpacks vars', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture([
|
||||
'build_pack' => 'railpack',
|
||||
'fqdn' => 'https://railpack.example.com',
|
||||
]);
|
||||
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'PREVIEW_BUILD_FLAG',
|
||||
'value' => 'enabled',
|
||||
'is_preview' => true,
|
||||
'is_runtime' => false,
|
||||
'is_buildtime' => true,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'PREVIEW_RUNTIME_ONLY',
|
||||
'value' => 'runtime',
|
||||
'is_preview' => true,
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'NIXPACKS_NODE_VERSION',
|
||||
'value' => '22',
|
||||
'is_preview' => true,
|
||||
'is_runtime' => false,
|
||||
'is_buildtime' => true,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'RAILPACK_NODE_VERSION',
|
||||
'value' => '20',
|
||||
'is_preview' => true,
|
||||
'is_runtime' => false,
|
||||
'is_buildtime' => true,
|
||||
]);
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application->fresh(), $server, [
|
||||
'build_pack' => 'railpack',
|
||||
'branch' => 'feature/railpack',
|
||||
'pull_request_id' => 123,
|
||||
]);
|
||||
|
||||
/** @var Collection $variables */
|
||||
$variables = invokeDeploymentJobMethod($job, $reflection, 'railpack_build_variables');
|
||||
|
||||
expect($variables->get('PREVIEW_BUILD_FLAG'))->toBe('enabled');
|
||||
expect($variables->get('RAILPACK_NODE_VERSION'))->toBe('20');
|
||||
expect($variables->get('RAILPACK_DEPLOY_APT_PACKAGES'))->toBe('curl wget');
|
||||
expect($variables->get('COOLIFY_RESOURCE_UUID'))->toBe($application->uuid);
|
||||
expect($variables->has('NIXPACKS_NODE_VERSION'))->toBeFalse();
|
||||
expect($variables->has('PREVIEW_RUNTIME_ONLY'))->toBeFalse();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -219,6 +219,7 @@ function invokeRailpackMethod(object $job, ReflectionClass $reflection, string $
|
|||
collect([
|
||||
'RAILPACK_NODE_VERSION' => '22',
|
||||
'RAILPACK_INSTALL_CMD' => 'npm ci && npm run postinstall',
|
||||
'RAILPACK_DEPLOY_APT_PACKAGES' => 'curl wget',
|
||||
'SECRET_JSON' => '{"token":"abc"}',
|
||||
]),
|
||||
],
|
||||
|
|
@ -226,9 +227,11 @@ function invokeRailpackMethod(object $job, ReflectionClass $reflection, string $
|
|||
|
||||
expect($command)->toContain("env RAILPACK_NODE_VERSION='22'");
|
||||
expect($command)->toContain("RAILPACK_INSTALL_CMD='npm ci && npm run postinstall'");
|
||||
expect($command)->toContain("RAILPACK_DEPLOY_APT_PACKAGES='curl wget'");
|
||||
expect($command)->toContain("SECRET_JSON='{\"token\":\"abc\"}'");
|
||||
expect($command)->toContain('--secret id=RAILPACK_NODE_VERSION,env=RAILPACK_NODE_VERSION');
|
||||
expect($command)->toContain('--secret id=RAILPACK_INSTALL_CMD,env=RAILPACK_INSTALL_CMD');
|
||||
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').'"');
|
||||
|
|
|
|||
|
|
@ -42,10 +42,15 @@
|
|||
$nullValue->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn(null);
|
||||
|
||||
$envQuery = Mockery::mock();
|
||||
$envQuery->shouldReceive('withoutBuildpackControlVariables')->once()->andReturnSelf();
|
||||
$envQuery->shouldReceive('where')->with('is_buildtime', true)->once()->andReturnSelf();
|
||||
$envQuery->shouldReceive('get')->once()->andReturn(collect([$nodeVersion, $literalValue, $jsonValue, $nullValue]));
|
||||
$envQuery->shouldReceive('get')->once()->andReturn(collect([]));
|
||||
$application->shouldReceive('environment_variables')->once()->andReturn($envQuery);
|
||||
|
||||
$railpackQuery = Mockery::mock();
|
||||
$railpackQuery->shouldReceive('get')->once()->andReturn(collect([$nodeVersion, $literalValue, $jsonValue, $nullValue]));
|
||||
$application->shouldReceive('railpack_environment_variables')->once()->andReturn($railpackQuery);
|
||||
|
||||
$job = Mockery::mock(ApplicationDeploymentJob::class)->makePartial();
|
||||
$job->shouldAllowMockingProtectedMethods();
|
||||
$job->shouldReceive('generate_coolify_env_variables')->andReturn(collect([]));
|
||||
|
|
@ -76,11 +81,13 @@
|
|||
'RAILPACK_CUSTOM_FLAG' => 'hello world',
|
||||
'RAILPACK_JSON' => '{"token":"abc"}',
|
||||
'RAILPACK_INSTALL_CMD' => 'npm ci && npm run postinstall',
|
||||
'RAILPACK_DEPLOY_APT_PACKAGES' => 'curl wget',
|
||||
]);
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_NODE_VERSION=22'");
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_CUSTOM_FLAG=hello world'");
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_JSON={\"token\":\"abc\"}'");
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_INSTALL_CMD=npm ci && npm run postinstall'");
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_DEPLOY_APT_PACKAGES=curl wget'");
|
||||
expect($envArgs)->not->toContain('RAILPACK_NULL');
|
||||
});
|
||||
|
||||
|
|
@ -97,10 +104,15 @@
|
|||
$previewValue->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn('preview-value');
|
||||
|
||||
$previewQuery = Mockery::mock();
|
||||
$previewQuery->shouldReceive('withoutBuildpackControlVariables')->once()->andReturnSelf();
|
||||
$previewQuery->shouldReceive('where')->with('is_buildtime', true)->once()->andReturnSelf();
|
||||
$previewQuery->shouldReceive('get')->once()->andReturn(collect([$previewValue]));
|
||||
$previewQuery->shouldReceive('get')->once()->andReturn(collect([]));
|
||||
$application->shouldReceive('environment_variables_preview')->once()->andReturn($previewQuery);
|
||||
|
||||
$railpackPreviewQuery = Mockery::mock();
|
||||
$railpackPreviewQuery->shouldReceive('get')->once()->andReturn(collect([$previewValue]));
|
||||
$application->shouldReceive('railpack_environment_variables_preview')->once()->andReturn($railpackPreviewQuery);
|
||||
|
||||
$job = Mockery::mock(ApplicationDeploymentJob::class)->makePartial();
|
||||
$job->shouldAllowMockingProtectedMethods();
|
||||
$job->shouldReceive('generate_coolify_env_variables')->andReturn(collect([]));
|
||||
|
|
@ -124,6 +136,7 @@
|
|||
|
||||
expect($variables->all())->toBe([
|
||||
'RAILPACK_PREVIEW_ONLY' => 'preview-value',
|
||||
'RAILPACK_DEPLOY_APT_PACKAGES' => 'curl wget',
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
@ -140,10 +153,15 @@
|
|||
$userVar->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn('hello');
|
||||
|
||||
$envQuery = Mockery::mock();
|
||||
$envQuery->shouldReceive('withoutBuildpackControlVariables')->once()->andReturnSelf();
|
||||
$envQuery->shouldReceive('where')->with('is_buildtime', true)->once()->andReturnSelf();
|
||||
$envQuery->shouldReceive('get')->once()->andReturn(collect([$userVar]));
|
||||
$application->shouldReceive('environment_variables')->once()->andReturn($envQuery);
|
||||
|
||||
$railpackQuery = Mockery::mock();
|
||||
$railpackQuery->shouldReceive('get')->once()->andReturn(collect([]));
|
||||
$application->shouldReceive('railpack_environment_variables')->once()->andReturn($railpackQuery);
|
||||
|
||||
$job = Mockery::mock(ApplicationDeploymentJob::class)->makePartial();
|
||||
$job->shouldAllowMockingProtectedMethods();
|
||||
$job->shouldReceive('generate_coolify_env_variables')
|
||||
|
|
@ -177,6 +195,7 @@
|
|||
|
||||
expect($variables->all())->toBe([
|
||||
'MY_BUILD_VAR' => 'hello',
|
||||
'RAILPACK_DEPLOY_APT_PACKAGES' => 'curl wget',
|
||||
'COOLIFY_URL' => 'https://app.example.com',
|
||||
'COOLIFY_FQDN' => 'app.example.com',
|
||||
'COOLIFY_BRANCH' => 'main',
|
||||
|
|
@ -190,6 +209,59 @@
|
|||
|
||||
expect($envArgs)->toContain("--env 'COOLIFY_URL=https://app.example.com'");
|
||||
expect($envArgs)->toContain("--env 'SOURCE_COMMIT=abc123'");
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_DEPLOY_APT_PACKAGES=curl wget'");
|
||||
expect($envArgs)->not->toContain('EMPTY_VAR');
|
||||
expect($envArgs)->not->toContain('NULL_VAR');
|
||||
});
|
||||
|
||||
it('preserves user railpack deploy apt packages while adding healthcheck tools once', function () {
|
||||
$application = Mockery::mock(Application::class);
|
||||
$application->shouldReceive('getAttribute')->with('install_command')->andReturn(null);
|
||||
|
||||
$deployPackages = Mockery::mock(EnvironmentVariable::class)->makePartial();
|
||||
$deployPackages->forceFill([
|
||||
'key' => 'RAILPACK_DEPLOY_APT_PACKAGES',
|
||||
'is_literal' => false,
|
||||
'is_multiline' => false,
|
||||
]);
|
||||
$deployPackages->shouldReceive('getResolvedValueWithServer')->once()->with(Mockery::type(Server::class))->andReturn('ffmpeg curl');
|
||||
|
||||
$envQuery = Mockery::mock();
|
||||
$envQuery->shouldReceive('withoutBuildpackControlVariables')->once()->andReturnSelf();
|
||||
$envQuery->shouldReceive('where')->with('is_buildtime', true)->once()->andReturnSelf();
|
||||
$envQuery->shouldReceive('get')->once()->andReturn(collect([]));
|
||||
$application->shouldReceive('environment_variables')->once()->andReturn($envQuery);
|
||||
|
||||
$railpackQuery = Mockery::mock();
|
||||
$railpackQuery->shouldReceive('get')->once()->andReturn(collect([$deployPackages]));
|
||||
$application->shouldReceive('railpack_environment_variables')->once()->andReturn($railpackQuery);
|
||||
|
||||
$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');
|
||||
$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->get('RAILPACK_DEPLOY_APT_PACKAGES'))->toBe('ffmpeg curl wget');
|
||||
|
||||
$envArgsProperty = $reflection->getProperty('env_railpack_args');
|
||||
$envArgsProperty->setAccessible(true);
|
||||
$envArgs = $envArgsProperty->getValue($job);
|
||||
|
||||
expect($envArgs)->toContain("--env 'RAILPACK_DEPLOY_APT_PACKAGES=ffmpeg curl wget'");
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue