feat(deployment): handle buildtime and runtime variables during deployment
This commit is contained in:
@@ -169,6 +169,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
|
|
||||||
private bool $dockerBuildkitSupported = false;
|
private bool $dockerBuildkitSupported = false;
|
||||||
|
|
||||||
|
private bool $skip_build = false;
|
||||||
|
|
||||||
private Collection|string $build_secrets;
|
private Collection|string $build_secrets;
|
||||||
|
|
||||||
public function tags()
|
public function tags()
|
||||||
@@ -566,7 +568,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
if ($this->application->settings->is_raw_compose_deployment_enabled) {
|
||||||
$this->application->oldRawParser();
|
$this->application->oldRawParser();
|
||||||
$yaml = $composeFile = $this->application->docker_compose_raw;
|
$yaml = $composeFile = $this->application->docker_compose_raw;
|
||||||
$this->save_environment_variables();
|
$this->generate_runtime_environment_variables();
|
||||||
|
|
||||||
// For raw compose, we cannot automatically add secrets configuration
|
// For raw compose, we cannot automatically add secrets configuration
|
||||||
// User must define it manually in their docker-compose file
|
// User must define it manually in their docker-compose file
|
||||||
@@ -575,7 +577,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id'));
|
$composeFile = $this->application->parse(pull_request_id: $this->pull_request_id, preview_id: data_get($this->preview, 'id'));
|
||||||
$this->save_environment_variables();
|
$this->generate_runtime_environment_variables();
|
||||||
if (filled($this->env_filename)) {
|
if (filled($this->env_filename)) {
|
||||||
$services = collect(data_get($composeFile, 'services', []));
|
$services = collect(data_get($composeFile, 'services', []));
|
||||||
$services = $services->map(function ($service, $name) {
|
$services = $services->map(function ($service, $name) {
|
||||||
@@ -759,6 +761,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->generate_build_env_variables();
|
$this->generate_build_env_variables();
|
||||||
$this->build_image();
|
$this->build_image();
|
||||||
|
|
||||||
|
// For Nixpacks, save runtime environment variables AFTER the build
|
||||||
|
// to prevent them from being accessible during the build process
|
||||||
|
$this->save_runtime_environment_variables();
|
||||||
$this->push_to_docker_registry();
|
$this->push_to_docker_registry();
|
||||||
$this->rolling_update();
|
$this->rolling_update();
|
||||||
}
|
}
|
||||||
@@ -952,18 +958,17 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
|
if (str($this->saved_outputs->get('local_image_found'))->isNotEmpty()) {
|
||||||
if ($this->is_this_additional_server) {
|
if ($this->is_this_additional_server) {
|
||||||
|
$this->skip_build = true;
|
||||||
$this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
$this->application_deployment_queue->addLogEntry("Image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->push_to_docker_registry();
|
$this->push_to_docker_registry();
|
||||||
$this->rolling_update();
|
$this->rolling_update();
|
||||||
if ($this->restart_only) {
|
|
||||||
$this->post_deployment();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (! $this->application->isConfigurationChanged()) {
|
if (! $this->application->isConfigurationChanged()) {
|
||||||
$this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
$this->application_deployment_queue->addLogEntry("No configuration changed & image found ({$this->production_image_name}) with the same Git Commit SHA. Build step skipped.");
|
||||||
|
$this->skip_build = true;
|
||||||
$this->generate_compose_file();
|
$this->generate_compose_file();
|
||||||
$this->push_to_docker_registry();
|
$this->push_to_docker_registry();
|
||||||
$this->rolling_update();
|
$this->rolling_update();
|
||||||
@@ -1004,7 +1009,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function save_environment_variables()
|
private function generate_runtime_environment_variables()
|
||||||
{
|
{
|
||||||
$envs = collect([]);
|
$envs = collect([]);
|
||||||
$sort = $this->application->settings->is_env_sorting_enabled;
|
$sort = $this->application->settings->is_env_sorting_enabled;
|
||||||
@@ -1061,9 +1066,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out buildtime-only variables from runtime environment
|
// Filter runtime variables (only include variables that are available at runtime)
|
||||||
$runtime_environment_variables = $sorted_environment_variables->filter(function ($env) {
|
$runtime_environment_variables = $sorted_environment_variables->filter(function ($env) {
|
||||||
return ! $env->is_buildtime_only;
|
return $env->is_runtime;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort runtime environment variables: those referencing SERVICE_ variables come after others
|
// Sort runtime environment variables: those referencing SERVICE_ variables come after others
|
||||||
@@ -1117,9 +1122,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out buildtime-only variables from runtime environment for preview
|
// Filter runtime variables for preview (only include variables that are available at runtime)
|
||||||
$runtime_environment_variables_preview = $sorted_environment_variables_preview->filter(function ($env) {
|
$runtime_environment_variables_preview = $sorted_environment_variables_preview->filter(function ($env) {
|
||||||
return ! $env->is_buildtime_only;
|
return $env->is_runtime;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort runtime environment variables: those referencing SERVICE_ variables come after others
|
// Sort runtime environment variables: those referencing SERVICE_ variables come after others
|
||||||
@@ -1176,6 +1181,9 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
$this->env_filename = null;
|
$this->env_filename = null;
|
||||||
} else {
|
} else {
|
||||||
|
// For Nixpacks builds, we save the .env file AFTER the build to prevent
|
||||||
|
// runtime-only variables from being accessible during the build process
|
||||||
|
if ($this->application->build_pack !== 'nixpacks' || $this->skip_build) {
|
||||||
$envs_base64 = base64_encode($envs->implode("\n"));
|
$envs_base64 = base64_encode($envs->implode("\n"));
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[
|
[
|
||||||
@@ -1199,9 +1207,45 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
$this->environment_variables = $envs;
|
$this->environment_variables = $envs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function save_runtime_environment_variables()
|
||||||
|
{
|
||||||
|
// This method saves the .env file with runtime variables
|
||||||
|
// It should be called AFTER the build for Nixpacks to prevent runtime-only variables
|
||||||
|
// from being accessible during the build process
|
||||||
|
|
||||||
|
if ($this->environment_variables && $this->environment_variables->isNotEmpty() && $this->env_filename) {
|
||||||
|
$envs_base64 = base64_encode($this->environment_variables->implode("\n"));
|
||||||
|
|
||||||
|
// Write .env file to workdir (for container runtime)
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
executeInDocker($this->deployment_uuid, "echo '$envs_base64' | base64 -d | tee $this->workdir/{$this->env_filename} > /dev/null"),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Write .env file to configuration directory
|
||||||
|
if ($this->use_build_server) {
|
||||||
|
$this->server = $this->original_server;
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$this->server = $this->build_server;
|
||||||
|
} else {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
[
|
||||||
|
"echo '$envs_base64' | base64 -d | tee $this->configuration_dir/{$this->env_filename} > /dev/null",
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function elixir_finetunes()
|
private function elixir_finetunes()
|
||||||
{
|
{
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
@@ -1418,6 +1462,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->add_build_env_variables_to_dockerfile();
|
$this->add_build_env_variables_to_dockerfile();
|
||||||
}
|
}
|
||||||
$this->build_image();
|
$this->build_image();
|
||||||
|
// For Nixpacks, save runtime environment variables AFTER the build
|
||||||
|
if ($this->application->build_pack === 'nixpacks') {
|
||||||
|
$this->save_runtime_environment_variables();
|
||||||
|
}
|
||||||
$this->push_to_docker_registry();
|
$this->push_to_docker_registry();
|
||||||
$this->rolling_update();
|
$this->rolling_update();
|
||||||
}
|
}
|
||||||
@@ -1681,6 +1729,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
{
|
{
|
||||||
$nixpacks_command = $this->nixpacks_build_cmd();
|
$nixpacks_command = $this->nixpacks_build_cmd();
|
||||||
$this->application_deployment_queue->addLogEntry("Generating nixpacks configuration with: $nixpacks_command");
|
$this->application_deployment_queue->addLogEntry("Generating nixpacks configuration with: $nixpacks_command");
|
||||||
|
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
[executeInDocker($this->deployment_uuid, $nixpacks_command), 'save' => 'nixpacks_plan', 'hidden' => true],
|
[executeInDocker($this->deployment_uuid, $nixpacks_command), 'save' => 'nixpacks_plan', 'hidden' => true],
|
||||||
[executeInDocker($this->deployment_uuid, "nixpacks detect {$this->workdir}"), 'save' => 'nixpacks_type', 'hidden' => true],
|
[executeInDocker($this->deployment_uuid, "nixpacks detect {$this->workdir}"), 'save' => 'nixpacks_type', 'hidden' => true],
|
||||||
@@ -1700,6 +1749,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$parsed = Toml::Parse($this->nixpacks_plan);
|
$parsed = Toml::Parse($this->nixpacks_plan);
|
||||||
|
|
||||||
// Do any modifications here
|
// Do any modifications here
|
||||||
|
// We need to generate envs here because nixpacks need to know to generate a proper Dockerfile
|
||||||
$this->generate_env_variables();
|
$this->generate_env_variables();
|
||||||
$merged_envs = collect(data_get($parsed, 'variables', []))->merge($this->env_args);
|
$merged_envs = collect(data_get($parsed, 'variables', []))->merge($this->env_args);
|
||||||
$aptPkgs = data_get($parsed, 'phases.setup.aptPkgs', []);
|
$aptPkgs = data_get($parsed, 'phases.setup.aptPkgs', []);
|
||||||
@@ -1872,13 +1922,13 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->env_args->put('SOURCE_COMMIT', $this->commit);
|
$this->env_args->put('SOURCE_COMMIT', $this->commit);
|
||||||
$coolify_envs = $this->generate_coolify_env_variables();
|
$coolify_envs = $this->generate_coolify_env_variables();
|
||||||
|
|
||||||
// Include ALL environment variables (both build-time and runtime) for all build packs
|
// For build process, include only environment variables where is_buildtime = true
|
||||||
// This deprecates the need for is_build_time flag
|
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
// Get all environment variables except NIXPACKS_ prefixed ones for non-nixpacks builds
|
// Get environment variables that are marked as available during build
|
||||||
$envs = $this->application->build_pack === 'nixpacks'
|
$envs = $this->application->environment_variables()
|
||||||
? $this->application->runtime_environment_variables
|
->where('key', 'not like', 'NIXPACKS_%')
|
||||||
: $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->get();
|
->where('is_buildtime', true)
|
||||||
|
->get();
|
||||||
|
|
||||||
foreach ($envs as $env) {
|
foreach ($envs as $env) {
|
||||||
if (! is_null($env->real_value)) {
|
if (! is_null($env->real_value)) {
|
||||||
@@ -1900,10 +1950,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Get all preview environment variables except NIXPACKS_ prefixed ones for non-nixpacks builds
|
// Get preview environment variables that are marked as available during build
|
||||||
$envs = $this->application->build_pack === 'nixpacks'
|
$envs = $this->application->environment_variables_preview()
|
||||||
? $this->application->runtime_environment_variables_preview
|
->where('key', 'not like', 'NIXPACKS_%')
|
||||||
: $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->get();
|
->where('is_buildtime', true)
|
||||||
|
->get();
|
||||||
|
|
||||||
foreach ($envs as $env) {
|
foreach ($envs as $env) {
|
||||||
if (! is_null($env->real_value)) {
|
if (! is_null($env->real_value)) {
|
||||||
@@ -1935,8 +1986,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$persistent_storages = $this->generate_local_persistent_volumes();
|
$persistent_storages = $this->generate_local_persistent_volumes();
|
||||||
$persistent_file_volumes = $this->application->fileStorages()->get();
|
$persistent_file_volumes = $this->application->fileStorages()->get();
|
||||||
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
$volume_names = $this->generate_local_persistent_volumes_only_volume_names();
|
||||||
// $environment_variables = $this->generate_environment_variables($ports);
|
$this->generate_runtime_environment_variables();
|
||||||
$this->save_environment_variables();
|
|
||||||
if (data_get($this->application, 'custom_labels')) {
|
if (data_get($this->application, 'custom_labels')) {
|
||||||
$this->application->parseContainerLabels();
|
$this->application->parseContainerLabels();
|
||||||
$labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels)));
|
$labels = collect(preg_split("/\r\n|\n|\r/", base64_decode($this->application->custom_labels)));
|
||||||
@@ -2652,6 +2702,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
if ($this->application->build_pack === 'nixpacks') {
|
if ($this->application->build_pack === 'nixpacks') {
|
||||||
$variables = collect($this->nixpacks_plan_json->get('variables'));
|
$variables = collect($this->nixpacks_plan_json->get('variables'));
|
||||||
} else {
|
} else {
|
||||||
|
// Generate environment variables for build process (filters by is_buildtime = true)
|
||||||
$this->generate_env_variables();
|
$this->generate_env_variables();
|
||||||
$variables = collect([])->merge($this->env_args);
|
$variables = collect([])->merge($this->env_args);
|
||||||
}
|
}
|
||||||
@@ -2678,8 +2729,8 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
}
|
}
|
||||||
|
|
||||||
$variables = $this->pull_request_id === 0
|
$variables = $this->pull_request_id === 0
|
||||||
? $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->get()
|
? $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->where('is_buildtime', true)->get()
|
||||||
: $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->get();
|
: $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->where('is_buildtime', true)->get();
|
||||||
|
|
||||||
if ($variables->isEmpty()) {
|
if ($variables->isEmpty()) {
|
||||||
return '';
|
return '';
|
||||||
@@ -2722,7 +2773,11 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
$dockerfile = collect(str($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
$dockerfile = collect(str($this->saved_outputs->get('dockerfile'))->trim()->explode("\n"));
|
||||||
|
|
||||||
if ($this->pull_request_id === 0) {
|
if ($this->pull_request_id === 0) {
|
||||||
$envs = $this->application->environment_variables()->where('key', 'not like', 'NIXPACKS_%')->get();
|
// Only add environment variables that are available during build
|
||||||
|
$envs = $this->application->environment_variables()
|
||||||
|
->where('key', 'not like', 'NIXPACKS_%')
|
||||||
|
->where('is_buildtime', true)
|
||||||
|
->get();
|
||||||
foreach ($envs as $env) {
|
foreach ($envs as $env) {
|
||||||
if (data_get($env, 'is_multiline') === true) {
|
if (data_get($env, 'is_multiline') === true) {
|
||||||
$dockerfile->splice(1, 0, ["ARG {$env->key}"]);
|
$dockerfile->splice(1, 0, ["ARG {$env->key}"]);
|
||||||
@@ -2731,8 +2786,11 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Get all preview environment variables except NIXPACKS_ prefixed ones
|
// Only add preview environment variables that are available during build
|
||||||
$envs = $this->application->environment_variables_preview()->where('key', 'not like', 'NIXPACKS_%')->get();
|
$envs = $this->application->environment_variables_preview()
|
||||||
|
->where('key', 'not like', 'NIXPACKS_%')
|
||||||
|
->where('is_buildtime', true)
|
||||||
|
->get();
|
||||||
foreach ($envs as $env) {
|
foreach ($envs as $env) {
|
||||||
if (data_get($env, 'is_multiline') === true) {
|
if (data_get($env, 'is_multiline') === true) {
|
||||||
$dockerfile->splice(1, 0, ["ARG {$env->key}"]);
|
$dockerfile->splice(1, 0, ["ARG {$env->key}"]);
|
||||||
|
Reference in New Issue
Block a user