fix(railpack): interpolate build-time env variables by sourcing build… (#10768)
This commit is contained in:
commit
f2d11d9300
2 changed files with 92 additions and 6 deletions
|
|
@ -1833,8 +1833,8 @@ private function save_buildtime_environment_variables()
|
|||
]
|
||||
);
|
||||
}
|
||||
} elseif ($this->build_pack === 'dockercompose' || $this->build_pack === 'dockerfile') {
|
||||
// For Docker Compose and Dockerfile, create an empty .env file even if there are no build-time variables
|
||||
} elseif (in_array($this->build_pack, ['dockercompose', 'dockerfile', 'railpack'], true)) {
|
||||
// For build packs that source the build-time .env file, create an empty file even if there are no build-time variables
|
||||
// This ensures the file exists when referenced in build commands
|
||||
$this->application_deployment_queue->addLogEntry('Creating empty build-time .env file in /artifacts (no build-time variables defined).', hidden: true);
|
||||
|
||||
|
|
@ -2697,12 +2697,22 @@ private function railpack_build_command(string $imageName, Collection $variables
|
|||
$cacheArgs .= ' --build-arg secrets-hash='.$this->generate_secrets_hash($variables);
|
||||
}
|
||||
|
||||
$environmentPrefix = $this->railpack_build_environment_prefix($variables);
|
||||
// Build-time variables reach the build through the sourced build-time .env file
|
||||
// (written by save_buildtime_environment_variables), which interpolates shell-style
|
||||
// references such as BETTER_AUTH_URL=$COOLIFY_URL. Passing them inline via `env`
|
||||
// would forward the literal `$COOLIFY_URL` because each value is single-quoted and
|
||||
// `env` does not interpolate its own assignments. Only buildpack control variables
|
||||
// (NIXPACKS_/RAILPACK_) — which are excluded from the build-time .env file and never
|
||||
// need interpolation — are still passed inline.
|
||||
$controlVariables = $variables->filter(
|
||||
fn ($value, $key) => str($key)->startsWith(EnvironmentVariable::BUILDPACK_CONTROL_VARIABLE_PREFIXES)
|
||||
);
|
||||
|
||||
$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');
|
||||
|
||||
return 'docker buildx create --name coolify-railpack --driver docker-container 2>/dev/null || true'
|
||||
." && {$environmentPrefix}docker buildx build --builder coolify-railpack"
|
||||
$buildxBuildCommand = "{$environmentPrefix}docker buildx build --builder coolify-railpack"
|
||||
." {$this->addHosts} --network host"
|
||||
." --build-arg BUILDKIT_SYNTAX=\"{$frontendImage}\""
|
||||
." {$cacheArgs}"
|
||||
|
|
@ -2712,6 +2722,9 @@ private function railpack_build_command(string $imageName, Collection $variables
|
|||
.' --load'
|
||||
." -t {$imageName}"
|
||||
." {$this->workdir}";
|
||||
|
||||
return 'docker buildx create --name coolify-railpack --driver docker-container 2>/dev/null || true'
|
||||
.' && '.$this->wrap_build_command_with_env_export($buildxBuildCommand);
|
||||
}
|
||||
|
||||
private function decode_railpack_config(string $config, string $source): array
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
use App\Exceptions\DeploymentException;
|
||||
use App\Jobs\ApplicationDeploymentJob;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationSetting;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use Illuminate\Support\Collection;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -236,10 +238,15 @@ function invokeRailpackMethod(object $job, ReflectionClass $reflection, string $
|
|||
],
|
||||
);
|
||||
|
||||
// Build-time variables are interpolated by sourcing the build-time .env file before
|
||||
// the build, so user/Coolify variables must NOT be forwarded inline as literals.
|
||||
expect($command)->toContain('set -a && source /artifacts/build-time.env && set +a');
|
||||
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\"}'");
|
||||
// SECRET_JSON is not a buildpack control variable, so it is provided via the sourced
|
||||
// build-time .env file (which supports $VAR interpolation) rather than inline `env`.
|
||||
expect($command)->not->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'");
|
||||
|
|
@ -247,3 +254,69 @@ function invokeRailpackMethod(object $job, ReflectionClass $reflection, string $
|
|||
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').'"');
|
||||
});
|
||||
|
||||
it('interpolates build-time variable references for railpack by sourcing the build-time env file', function () {
|
||||
[$job, $reflection] = makeRailpackDeploymentJob([
|
||||
'uuid' => 'application-uuid',
|
||||
]);
|
||||
|
||||
// Mirrors the issue: BETTER_AUTH_URL=$COOLIFY_URL must be interpolated at build time.
|
||||
$command = invokeRailpackMethod(
|
||||
$job,
|
||||
$reflection,
|
||||
'railpack_build_command',
|
||||
[
|
||||
'coollabsio/coolify:test',
|
||||
collect([
|
||||
'BETTER_AUTH_URL' => '$COOLIFY_URL',
|
||||
'COOLIFY_URL' => 'https://sapere-10.bobman.dev',
|
||||
]),
|
||||
],
|
||||
);
|
||||
|
||||
// The literal `$COOLIFY_URL` must NOT be forwarded inline; it is resolved by the shell
|
||||
// after sourcing the build-time .env file, then read through the build secret.
|
||||
expect($command)->toContain('set -a && source /artifacts/build-time.env && set +a');
|
||||
expect($command)->not->toContain("'BETTER_AUTH_URL=\$COOLIFY_URL'");
|
||||
expect($command)->not->toContain("env 'BETTER_AUTH_URL");
|
||||
expect($command)->toContain("--secret 'id=BETTER_AUTH_URL,env=BETTER_AUTH_URL'");
|
||||
expect($command)->toContain("--secret 'id=COOLIFY_URL,env=COOLIFY_URL'");
|
||||
});
|
||||
|
||||
it('creates an empty build-time env file for railpack when there are no generated build-time variables', function () {
|
||||
[$job, $reflection] = makeRailpackDeploymentJob([
|
||||
'build_pack' => 'railpack',
|
||||
'compose_parsing_version' => '3',
|
||||
]);
|
||||
|
||||
$applicationProperty = $reflection->getProperty('application');
|
||||
$applicationProperty->setAccessible(true);
|
||||
$application = $applicationProperty->getValue($job);
|
||||
$application->setRelation('settings', new ApplicationSetting([
|
||||
'include_source_commit_in_build' => false,
|
||||
'is_env_sorting_enabled' => false,
|
||||
]));
|
||||
$application->setRelation('environment_variables', collect([
|
||||
new EnvironmentVariable(['key' => 'COOLIFY_FQDN']),
|
||||
new EnvironmentVariable(['key' => 'COOLIFY_URL']),
|
||||
new EnvironmentVariable(['key' => 'COOLIFY_BRANCH']),
|
||||
new EnvironmentVariable(['key' => 'COOLIFY_RESOURCE_UUID']),
|
||||
]));
|
||||
|
||||
foreach ([
|
||||
'application_deployment_queue' => new class
|
||||
{
|
||||
public function addLogEntry(string $message, string $type = 'info', bool $hidden = false): void {}
|
||||
},
|
||||
'build_pack' => 'railpack',
|
||||
'pull_request_id' => 0,
|
||||
] as $property => $value) {
|
||||
$reflectionProperty = $reflection->getProperty($property);
|
||||
$reflectionProperty->setAccessible(true);
|
||||
$reflectionProperty->setValue($job, $value);
|
||||
}
|
||||
|
||||
invokeRailpackMethod($job, $reflection, 'save_buildtime_environment_variables');
|
||||
|
||||
expect(collect($job->recordedCommands)->flatten()->implode(' '))->toContain('touch /artifacts/build-time.env');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue