mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(previews): deploy previews with their own environment variables (#12325)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ddeb05280e
commit
3f3fa1d758
@@ -156,6 +156,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
/** @var array<string, string>|null */
|
||||
private ?array $remote_secrets_cache = null;
|
||||
|
||||
/** @var array<int, int>|null */
|
||||
private ?array $previewVariableIdsWithProductionValues = null;
|
||||
|
||||
private ?bool $has_secret_manager_source = null;
|
||||
|
||||
private $env_nixpacks_args;
|
||||
@@ -1741,10 +1744,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$sort = $this->application->settings->is_env_sorting_enabled;
|
||||
if ($sort) {
|
||||
$sorted_environment_variables = $this->application->runtime_environment_variables->sortBy('key');
|
||||
$sorted_environment_variables_preview = $this->application->runtime_environment_variables_preview->sortBy('key');
|
||||
$sorted_environment_variables_preview = $this->application->runtime_environment_variables_preview()->whereNotIn('id', $this->previewVariableIdsWithProductionValues())->get()->sortBy('key');
|
||||
} else {
|
||||
$sorted_environment_variables = $this->application->runtime_environment_variables->sortBy('id');
|
||||
$sorted_environment_variables_preview = $this->application->runtime_environment_variables_preview->sortBy('id');
|
||||
$sorted_environment_variables_preview = $this->application->runtime_environment_variables_preview()->whereNotIn('id', $this->previewVariableIdsWithProductionValues())->get()->sortBy('id');
|
||||
}
|
||||
if ($this->build_pack === 'dockercompose') {
|
||||
$sorted_environment_variables = $sorted_environment_variables->reject(fn (EnvironmentVariable $env) => $this->isGeneratedDockerComposeEnvironmentVariable($env));
|
||||
@@ -1881,33 +1884,14 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$envs->push($this->runtimeEnvironmentAssignment($env));
|
||||
}
|
||||
|
||||
// Fall back to production env vars for keys not overridden by preview vars,
|
||||
// but only when preview vars are configured. This ensures variables like
|
||||
// DB_PASSWORD that are only set for production will be available in the
|
||||
// preview .env file (fixing ${VAR} interpolation in docker-compose YAML),
|
||||
// while avoiding leaking production values when previews aren't configured.
|
||||
if ($runtime_environment_variables_preview->isNotEmpty()) {
|
||||
$previewKeys = $runtime_environment_variables_preview->pluck('key')->toArray();
|
||||
$fallback_production_vars = $sorted_environment_variables->filter(function ($env) use ($previewKeys) {
|
||||
return $env->is_runtime && ! in_array($env->key, $previewKeys);
|
||||
});
|
||||
foreach ($fallback_production_vars as $env) {
|
||||
if ($this->shouldOmitBlankComposeEnvironmentVariable($env)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$envs->push($this->runtimeEnvironmentAssignment($env));
|
||||
}
|
||||
}
|
||||
|
||||
// Add PORT if not exists, use the first port as default
|
||||
if ($this->build_pack !== 'dockercompose') {
|
||||
if ($this->application->environment_variables_preview->where('key', 'PORT')->isEmpty()) {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'PORT')->isEmpty()) {
|
||||
$envs->push("PORT={$ports[0]}");
|
||||
}
|
||||
}
|
||||
// Add HOST if not exists
|
||||
if ($this->application->environment_variables_preview->where('key', 'HOST')->isEmpty()) {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'HOST')->isEmpty()) {
|
||||
$envs->push('HOST=0.0.0.0');
|
||||
}
|
||||
}
|
||||
@@ -2250,6 +2234,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
} else {
|
||||
$sorted_environment_variables = $this->application->environment_variables_preview()
|
||||
->whereNotIn('id', $this->previewVariableIdsWithProductionValues())
|
||||
->withoutBuildpackControlVariables()
|
||||
->where('is_buildtime', true) // ONLY build-time variables
|
||||
->orderBy($this->application->settings->is_env_sorting_enabled ? 'key' : 'id')
|
||||
@@ -2468,6 +2453,39 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
$this->application_deployment_queue->addLogEntry(' Suggested name: '.ValidationPatterns::displayShellEnvironmentVariableKey($suggestedKey), 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
|
||||
* preview can run code from a fork, so it gets only the preview values that the user changed.
|
||||
*
|
||||
* @return array<int, int>
|
||||
*/
|
||||
private function previewVariableIdsWithProductionValues(): array
|
||||
{
|
||||
if ($this->previewVariableIdsWithProductionValues !== null) {
|
||||
return $this->previewVariableIdsWithProductionValues;
|
||||
}
|
||||
|
||||
if ($this->pull_request_id === 0 || ! $this->application->settings->is_pr_deployments_public_enabled) {
|
||||
return $this->previewVariableIdsWithProductionValues = [];
|
||||
}
|
||||
|
||||
$productionValues = $this->application->environment_variables()->get()->pluck('value', 'key');
|
||||
|
||||
return $this->previewVariableIdsWithProductionValues = $this->application->environment_variables_preview()->get()
|
||||
->filter(fn (EnvironmentVariable $env) => $productionValues->has($env->key) && $productionValues->get($env->key) === $env->value)
|
||||
->pluck('id')
|
||||
->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* The preview variables that this preview deployment uses.
|
||||
*/
|
||||
private function deployedPreviewVariables(): Collection
|
||||
{
|
||||
return $this->application->environment_variables_preview->whereNotIn('id', $this->previewVariableIdsWithProductionValues());
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip build-time variables with names that new variables can no longer use (such as my-var).
|
||||
* validateDeploymentEnvironmentVariableKeys() logs a warning for them before the deployment starts.
|
||||
@@ -3520,7 +3538,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($this->application->nixpacks_environment_variables_preview as $env) {
|
||||
foreach ($this->application->nixpacks_environment_variables_preview()->whereNotIn('id', $this->previewVariableIdsWithProductionValues())->get() as $env) {
|
||||
$resolvedValue = $env->getResolvedValueWithServer($this->mainServer);
|
||||
if (! is_null($resolvedValue) && $resolvedValue !== '') {
|
||||
$value = ($env->is_literal || $env->is_multiline) ? trim($resolvedValue, "'") : $resolvedValue;
|
||||
@@ -3602,11 +3620,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
$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();
|
||||
: $this->application->environment_variables_preview()->whereNotIn('id', $this->previewVariableIdsWithProductionValues())->withoutBuildpackControlVariables()->where('is_buildtime', true)->get();
|
||||
|
||||
$railpackVariables = $this->pull_request_id === 0
|
||||
? $this->application->railpack_environment_variables()->get()
|
||||
: $this->application->railpack_environment_variables_preview()->get();
|
||||
: $this->application->railpack_environment_variables_preview()->whereNotIn('id', $this->previewVariableIdsWithProductionValues())->get();
|
||||
|
||||
$variables = $this->withoutLegacyEnvironmentVariableKeys($genericBuildVariables->merge($railpackVariables))
|
||||
->mapWithKeys(function (EnvironmentVariable $environmentVariable) {
|
||||
@@ -4016,7 +4034,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
// Only add SOURCE_COMMIT for runtime OR when explicitly enabled for build-time
|
||||
// SOURCE_COMMIT changes with each commit and breaks Docker cache if included in build
|
||||
if (! $forBuildTime || $this->application->settings->include_source_commit_in_build) {
|
||||
if ($this->application->environment_variables_preview->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'SOURCE_COMMIT')->isEmpty()) {
|
||||
if (! is_null($this->commit)) {
|
||||
$coolify_envs->put('SOURCE_COMMIT', $this->commit);
|
||||
} else {
|
||||
@@ -4024,14 +4042,14 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'COOLIFY_FQDN')->isEmpty()) {
|
||||
if ((int) $this->application->compose_parsing_version >= 3) {
|
||||
$coolify_envs->put('COOLIFY_URL', $this->preview->fqdn);
|
||||
} else {
|
||||
$coolify_envs->put('COOLIFY_FQDN', $this->preview->fqdn);
|
||||
}
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'COOLIFY_URL')->isEmpty()) {
|
||||
$url = $this->hostsFromFqdn($this->preview->fqdn);
|
||||
if ((int) $this->application->compose_parsing_version >= 3) {
|
||||
$coolify_envs->put('COOLIFY_FQDN', $url);
|
||||
@@ -4040,15 +4058,15 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
}
|
||||
if ($this->application->build_pack !== 'dockercompose' || $this->application->compose_parsing_version === '1' || $this->application->compose_parsing_version === '2') {
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'COOLIFY_BRANCH')->isEmpty()) {
|
||||
$coolify_envs->put('COOLIFY_BRANCH', $local_branch);
|
||||
}
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_RESOURCE_UUID')->isEmpty()) {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'COOLIFY_RESOURCE_UUID')->isEmpty()) {
|
||||
$coolify_envs->put('COOLIFY_RESOURCE_UUID', $this->application->uuid);
|
||||
}
|
||||
// Only add COOLIFY_CONTAINER_NAME for runtime (not build-time) - it changes every deployment and breaks Docker cache
|
||||
if (! $forBuildTime) {
|
||||
if ($this->application->environment_variables_preview->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||
if ($this->deployedPreviewVariables()->where('key', 'COOLIFY_CONTAINER_NAME')->isEmpty()) {
|
||||
$coolify_envs->put('COOLIFY_CONTAINER_NAME', $this->container_name);
|
||||
}
|
||||
}
|
||||
@@ -4056,7 +4074,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
|
||||
// Runtime only, so a project or server change does not break the Docker build cache
|
||||
if (! $forBuildTime) {
|
||||
$coolify_envs = $coolify_envs->merge(coolifyDefaultEnvironmentVariables($this->application, $this->application->environment_variables_preview->pluck('key'), $this->mainServer));
|
||||
$coolify_envs = $coolify_envs->merge(coolifyDefaultEnvironmentVariables($this->application, $this->deployedPreviewVariables()->pluck('key'), $this->mainServer));
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -4153,6 +4171,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
}
|
||||
} else {
|
||||
$envs = $this->application->environment_variables_preview()
|
||||
->whereNotIn('id', $this->previewVariableIdsWithProductionValues())
|
||||
->withoutBuildpackControlVariables()
|
||||
->where('is_buildtime', true)
|
||||
->get();
|
||||
@@ -5364,6 +5383,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
} else {
|
||||
// Only add preview environment variables that are available during build
|
||||
$envs = $this->application->environment_variables_preview()
|
||||
->whereNotIn('id', $this->previewVariableIdsWithProductionValues())
|
||||
->withoutBuildpackControlVariables()
|
||||
->where('is_buildtime', true)
|
||||
->get();
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
|
||||
<div class="w-full">
|
||||
<x-forms.listbox id="isPrDeploymentsPublicEnabled" label="PR deployment access" onChange="savePreviewSettings"
|
||||
helper="When public, anyone can trigger PR deployments. Otherwise only PRs from branches in the same repository are deployed and fork PRs are blocked."
|
||||
helper="When public, anyone can trigger PR deployments. Otherwise only PRs from branches in the same repository are deployed and fork PRs are blocked.<br><br><span class='font-bold'>Secrets:</span> the code of a fork PR can read all environment variables of its preview. When public, previews do not get preview variables that still have the production value. Give previews their own values, and do not use production secrets in them."
|
||||
:options="[
|
||||
['value' => false, 'label' => 'Repository members only'],
|
||||
['value' => true, 'label' => 'Public (fork PRs allowed)'],
|
||||
|
||||
@@ -897,7 +897,7 @@ it('filters docker compose generated service variables from build args', functio
|
||||
expect($envArgs->has('SERVICE_URL_APP'))->toBeFalse();
|
||||
});
|
||||
|
||||
it('filters buildpack control vars from preview runtime env fallback', function () {
|
||||
it('does not fall back to production vars when preview copies are deleted', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture();
|
||||
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
@@ -906,18 +906,6 @@ it('filters buildpack control vars from preview runtime env fallback', function
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'NIXPACKS_NODE_VERSION',
|
||||
'value' => '22',
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'RAILPACK_NODE_VERSION',
|
||||
'value' => '20',
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'PREVIEW_FLAG',
|
||||
'value' => 'enabled',
|
||||
@@ -926,9 +914,7 @@ it('filters buildpack control vars from preview runtime env fallback', function
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
|
||||
$application->environment_variables_preview()
|
||||
->whereIn('key', ['APP_NAME', 'NIXPACKS_NODE_VERSION', 'RAILPACK_NODE_VERSION'])
|
||||
->delete();
|
||||
$application->environment_variables_preview()->where('key', 'APP_NAME')->delete();
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application, $server, [
|
||||
'pull_request_id' => 99,
|
||||
@@ -937,10 +923,95 @@ it('filters buildpack control vars from preview runtime env fallback', function
|
||||
/** @var Collection $runtimeEnvs */
|
||||
$runtimeEnvs = invokeDeploymentJobMethod($job, $reflection, 'generate_runtime_environment_variables');
|
||||
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('APP_NAME=')))->toBeTrue();
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('APP_NAME=')))->toBeFalse();
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('PREVIEW_FLAG=')))->toBeTrue();
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('NIXPACKS_NODE_VERSION=')))->toBeFalse();
|
||||
expect($runtimeEnvs->contains(fn (string $env) => str($env)->startsWith('RAILPACK_NODE_VERSION=')))->toBeFalse();
|
||||
});
|
||||
|
||||
it('does not give production runtime values to a preview copy that is not a runtime variable', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture([
|
||||
'build_pack' => 'dockercompose',
|
||||
'docker_compose_raw' => "services:\n app:\n image: nginx\n",
|
||||
]);
|
||||
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'DB_PASSWORD',
|
||||
'value' => 'production-secret',
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'PREVIEW_FLAG',
|
||||
'value' => 'enabled',
|
||||
'is_preview' => true,
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
$application->environment_variables_preview()->where('key', 'DB_PASSWORD')->update(['is_runtime' => false]);
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application, $server, [
|
||||
'pull_request_id' => 99,
|
||||
]);
|
||||
|
||||
/** @var Collection $runtimeEnvs */
|
||||
$runtimeEnvs = invokeDeploymentJobMethod($job, $reflection, 'generate_runtime_environment_variables');
|
||||
|
||||
expect($runtimeEnvs->filter(fn (string $env) => str($env)->contains('production-secret')))->toBeEmpty();
|
||||
});
|
||||
|
||||
it('keeps copied production values out of previews when public PR deployments are on', function (string $buildPack) {
|
||||
[$application, $server] = makeDeploymentControlVarFixture([
|
||||
'build_pack' => $buildPack,
|
||||
'docker_compose_raw' => "services:\n app:\n image: nginx\n",
|
||||
]);
|
||||
$application->settings()->update(['is_pr_deployments_public_enabled' => true]);
|
||||
|
||||
createApplicationEnvironmentVariable($application, ['key' => 'DB_PASSWORD', 'value' => 'production-secret']);
|
||||
createApplicationEnvironmentVariable($application, ['key' => 'API_URL', 'value' => 'https://api.example.com']);
|
||||
createApplicationEnvironmentVariable($application, ['key' => 'PORT', 'value' => '3000']);
|
||||
$application->environment_variables_preview()->where('key', 'API_URL')->first()
|
||||
->update(['value' => 'https://preview-api.example.com']);
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application->fresh(), $server, [
|
||||
'pull_request_id' => 99,
|
||||
'remote_secrets_cache' => [],
|
||||
]);
|
||||
|
||||
/** @var Collection $runtimeEnvs */
|
||||
$runtimeEnvs = invokeDeploymentJobMethod($job, $reflection, 'generate_runtime_environment_variables');
|
||||
/** @var Collection $buildtimeEnvs */
|
||||
$buildtimeEnvs = invokeDeploymentJobMethod($job, $reflection, 'generate_buildtime_environment_variables');
|
||||
invokeDeploymentJobMethod($job, $reflection, 'generate_env_variables');
|
||||
/** @var Collection $envArgs */
|
||||
$envArgs = readDeploymentJobProperty($job, $reflection, 'env_args');
|
||||
/** @var Collection $railpackVariables */
|
||||
$railpackVariables = invokeDeploymentJobMethod($job, $reflection, 'railpack_build_variables');
|
||||
|
||||
foreach ([$runtimeEnvs, $buildtimeEnvs] as $envLines) {
|
||||
expect($envLines->filter(fn (string $env) => str($env)->contains('production-secret')))->toBeEmpty()
|
||||
->and($envLines->contains(fn (string $env) => str($env)->startsWith('API_URL=') && str($env)->contains('preview-api.example.com')))->toBeTrue();
|
||||
}
|
||||
expect($envArgs->has('DB_PASSWORD'))->toBeFalse()
|
||||
->and($envArgs->get('API_URL'))->toBe('https://preview-api.example.com')
|
||||
->and($railpackVariables->has('DB_PASSWORD'))->toBeFalse()
|
||||
->and($railpackVariables->get('API_URL'))->toBe('https://preview-api.example.com');
|
||||
if ($buildPack !== 'dockercompose') {
|
||||
expect($runtimeEnvs)->toContain('PORT='.$application->main_port()[0]);
|
||||
}
|
||||
})->with(['dockerfile', 'dockercompose']);
|
||||
|
||||
it('keeps copied production values for previews when public PR deployments are off', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture();
|
||||
|
||||
createApplicationEnvironmentVariable($application, ['key' => 'DB_PASSWORD', 'value' => 'production-secret']);
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application, $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('DB_PASSWORD=')))->toBeTrue();
|
||||
});
|
||||
|
||||
it('keeps buildpack variables read at container start in the runtime env', function (int $pullRequestId, bool $isPreview) {
|
||||
@@ -1720,39 +1791,6 @@ it('writes compose-safe preview runtime env files for user values', function ()
|
||||
->toContain('PREVIEW_QUOTED_VALUE='.escapeComposeEnvFileValue('preview quotes" \'$`'));
|
||||
});
|
||||
|
||||
it('writes compose-safe production fallbacks to preview runtime env files', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture();
|
||||
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'PREVIEW_TRIGGER',
|
||||
'value' => 'preview-value',
|
||||
'is_preview' => true,
|
||||
'is_literal' => false,
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
createApplicationEnvironmentVariable($application, [
|
||||
'key' => 'FALLBACK_QUOTED_VALUE',
|
||||
'value' => 'fallback quotes" \'$`',
|
||||
'is_literal' => true,
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
]);
|
||||
$application->environment_variables_preview()
|
||||
->where('key', 'FALLBACK_QUOTED_VALUE')
|
||||
->delete();
|
||||
|
||||
[$job, $reflection] = makeControlVarFilteringJob($application->fresh(), $server, [
|
||||
'pull_request_id' => 42,
|
||||
]);
|
||||
|
||||
/** @var Collection $runtimeEnvs */
|
||||
$runtimeEnvs = invokeDeploymentJobMethod($job, $reflection, 'generate_runtime_environment_variables');
|
||||
|
||||
expect($runtimeEnvs)
|
||||
->toContain('FALLBACK_QUOTED_VALUE='.escapeComposeEnvFileValue('fallback quotes" \'$`'));
|
||||
});
|
||||
|
||||
it('writes compose-safe runtime env values for remote secret references', function () {
|
||||
[$application, $server] = makeDeploymentControlVarFixture();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user