mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-12 15:53:46 -04:00
fix(previews): pass production runtime variables without a preview copy
Previews from v4.5.1 on did not get production runtime variables that have no preview variable. They get them again, except when public PR deployments are on. The fallback skips keys that have a preview variable, also when Runtime is off on it. Each preview deployment now logs the variables that it does not get: copies with the production value that public PR deployments skip, and production variables that have no preview variable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
edfa45aae2
commit
c49d9f623b
@@ -0,0 +1,6 @@
|
||||
---
|
||||
type: fixed
|
||||
---
|
||||
Missing production runtime variables in preview deployments when the variable has no preview variable
|
||||
|
||||
- A preview gets these production runtime variables again, except when `PR deployment access` is public. The deployment log lists each variable that a preview does not get
|
||||
@@ -461,6 +461,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
try {
|
||||
$this->validateDeploymentEnvironmentVariableKeys();
|
||||
$this->warnAboutPreviewVariablesNotPassed();
|
||||
$this->ensureRegistryImageForMultipleServers();
|
||||
$this->ensureRegistryImageIsNotShared();
|
||||
$this->warnAboutVolumesOnMultipleServers();
|
||||
@@ -2184,9 +2185,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$envs->push($this->runtimeEnvironmentAssignment($env));
|
||||
}
|
||||
|
||||
// A preview from before v4.5.1 also gets the production variables that have no preview copy.
|
||||
if ($this->preview?->keeps_production_values && $runtime_environment_variables_preview->isNotEmpty()) {
|
||||
$previewKeys = $runtime_environment_variables_preview->pluck('key')->all();
|
||||
// The production runtime variables that have no preview variable.
|
||||
if ($this->previewGetsProductionRuntimeFallback()) {
|
||||
$previewKeys = $this->application->environment_variables_preview()->pluck('key')->all();
|
||||
$sorted_environment_variables
|
||||
->filter(fn (EnvironmentVariable $env) => $env->is_runtime && ! in_array($env->key, $previewKeys, true) && ! $this->shouldOmitBlankComposeEnvironmentVariable($env))
|
||||
->each(fn (EnvironmentVariable $env) => $envs->push($this->runtimeEnvironmentAssignment($env)));
|
||||
@@ -2194,12 +2195,12 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
|
||||
// Add PORT if not exists, use the first port as default
|
||||
if ($this->build_pack !== 'dockercompose') {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'PORT')->isEmpty()) {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'PORT')->isEmpty() && ! $envs->contains(fn (string $env) => str_starts_with($env, 'PORT='))) {
|
||||
$envs->push("PORT={$ports[0]}");
|
||||
}
|
||||
}
|
||||
// Add HOST if not exists
|
||||
if ($this->deployedPreviewVariables()->where('key', 'HOST')->isEmpty()) {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'HOST')->isEmpty() && ! $envs->contains(fn (string $env) => str_starts_with($env, 'HOST='))) {
|
||||
$envs->push('HOST=0.0.0.0');
|
||||
}
|
||||
}
|
||||
@@ -2805,6 +2806,64 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->application_deployment_queue->addLogEntry(' Suggested name: '.ValidationPatterns::displayShellEnvironmentVariableKey($suggestedKey), type: 'info');
|
||||
}
|
||||
|
||||
/**
|
||||
* A preview can run code from a fork when public PR deployments are on, so it must not get production
|
||||
* values. A preview from before v4.5.1 keeps them, because it was deployed with them.
|
||||
*/
|
||||
private function previewUsesProductionValues(): bool
|
||||
{
|
||||
return ! $this->application->settings->is_pr_deployments_public_enabled || (bool) $this->preview?->keeps_production_values;
|
||||
}
|
||||
|
||||
/**
|
||||
* A preview gets the production runtime variables that have no preview variable, as before v4.5.1.
|
||||
* This needs at least one runtime preview variable, so previews without preview variables stay
|
||||
* without production values.
|
||||
*/
|
||||
private function previewGetsProductionRuntimeFallback(): bool
|
||||
{
|
||||
return $this->pull_request_id !== 0
|
||||
&& $this->previewUsesProductionValues()
|
||||
&& $this->application->runtime_environment_variables_preview()->where('is_runtime', true)->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the variables that this preview deployment does not get, so users know what to add.
|
||||
*/
|
||||
private function warnAboutPreviewVariablesNotPassed(): void
|
||||
{
|
||||
if ($this->pull_request_id === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$previewVariables = $this->application->environment_variables_preview()->get();
|
||||
$skippedKeys = $previewVariables->whereIn('id', $this->previewVariableIdsWithProductionValues())->pluck('key');
|
||||
if ($skippedKeys->isNotEmpty()) {
|
||||
$this->application_deployment_queue->addLogEntry('⚠️ Public PR deployments are on, so this preview does not get the preview variables that still have the production value: '.$skippedKeys->implode(', '), 'stderr');
|
||||
$this->application_deployment_queue->addLogEntry(' To use them, give them a preview value in Environment Variables > Preview Deployments.', type: 'info');
|
||||
}
|
||||
|
||||
$getsRuntimeFallback = $this->previewGetsProductionRuntimeFallback();
|
||||
$passesBuildtimeVariables = $this->deploymentPassesBuildtimeVariables();
|
||||
$missingKeys = $this->application->environment_variables()->get()
|
||||
->reject(fn (EnvironmentVariable $env) => $previewVariables->contains('key', $env->key))
|
||||
->reject(fn (EnvironmentVariable $env) => $this->build_pack === 'dockercompose' && $this->isGeneratedDockerComposeEnvironmentVariable($env))
|
||||
->map(function (EnvironmentVariable $env) use ($getsRuntimeFallback, $passesBuildtimeVariables): ?string {
|
||||
$missingAtRuntime = $env->is_runtime && ! $getsRuntimeFallback;
|
||||
$missingAtBuildtime = $env->is_buildtime && $passesBuildtimeVariables;
|
||||
if (! $missingAtRuntime && ! $missingAtBuildtime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $env->is_runtime && ! $missingAtRuntime ? "{$env->key} (at build time)" : $env->key;
|
||||
})
|
||||
->filter();
|
||||
if ($missingKeys->isNotEmpty()) {
|
||||
$this->application_deployment_queue->addLogEntry('⚠️ These production variables have no preview variable, so this preview does not get them: '.$missingKeys->implode(', '), 'stderr');
|
||||
$this->application_deployment_queue->addLogEntry(' To use them, add them in Environment Variables > Preview Deployments.', type: 'info');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs of preview variables that have the same value as the production variable with the same key.
|
||||
* Coolify copies each new production variable to the previews. With public PR deployments, a
|
||||
@@ -2818,7 +2877,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
return $this->previewVariableIdsWithProductionValues;
|
||||
}
|
||||
|
||||
if ($this->pull_request_id === 0 || ! $this->application->settings->is_pr_deployments_public_enabled || $this->preview?->keeps_production_values) {
|
||||
if ($this->pull_request_id === 0 || $this->previewUsesProductionValues()) {
|
||||
return $this->previewVariableIdsWithProductionValues = [];
|
||||
}
|
||||
|
||||
|
||||
@@ -948,8 +948,9 @@ it('filters docker compose generated service variables from build args', functio
|
||||
expect($envArgs->has('SERVICE_URL_APP'))->toBeFalse();
|
||||
});
|
||||
|
||||
it('does not fall back to production vars when preview copies are deleted', function () {
|
||||
it('falls back to production runtime vars without a preview copy only when public PR deployments are off', function (bool $isPublic) {
|
||||
[$application, $server] = makeDeploymentControlVarFixture();
|
||||
$application->settings()->update(['is_pr_deployments_public_enabled' => $isPublic]);
|
||||
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'APP_NAME',
|
||||
@@ -967,16 +968,19 @@ it('does not fall back to production vars when preview copies are deleted', func
|
||||
|
||||
$application->environment_variables_preview()->where('key', 'APP_NAME')->delete();
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application, $server, [
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application->fresh(), $server, [
|
||||
'pull_request_id' => 99,
|
||||
]);
|
||||
|
||||
/** @var Collection $runtimeEnvs */
|
||||
$runtimeEnvs = invokeDeploymentJobMethod($job, $reflection, 'generate_runtime_environment_variables');
|
||||
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('APP_NAME=')))->toBeFalse();
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('APP_NAME=')))->toBe(! $isPublic);
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('PREVIEW_FLAG=')))->toBeTrue();
|
||||
});
|
||||
})->with([
|
||||
'public PR deployments off' => [false],
|
||||
'public PR deployments on' => [true],
|
||||
]);
|
||||
|
||||
it('does not give production runtime values to a preview copy that is not a runtime variable', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture([
|
||||
|
||||
Reference in New Issue
Block a user