Merge remote-tracking branch 'origin/next' into feat/configurable-stop-grace-period

This commit is contained in:
Andras Bacsai committed 2026-05-11 23:20:31 +02:00
commit d1220895d9
193 files changed
+10693 -3525

No files matched your search

+502 -22
View File
@@ -33,6 +33,7 @@ use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Collection;
use Illuminate\Support\Sleep;
use Illuminate\Support\Str;
use JsonException;
use Spatie\Url\Url;
use Symfony\Component\Yaml\Yaml;
use Throwable;
@@ -48,6 +49,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private const NIXPACKS_PLAN_PATH = '/artifacts/thegameplan.json';
private const RAILPACK_REPOSITORY_CONFIG_PATH = 'railpack.json';
private const RAILPACK_GENERATED_CONFIG_PATH = '.coolify/railpack.generated.json';
public $tries = 1;
public $timeout = 3600;
@@ -124,6 +129,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private $env_nixpacks_args;
private $env_railpack_args;
private $docker_compose;
private $docker_compose_base64;
@@ -174,6 +181,8 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
private bool $dockerBuildkitSupported = false;
private bool $dockerBuildxAvailable = false;
private bool $dockerSecretsSupported = false;
private bool $skip_build = false;
@@ -414,6 +423,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if ($majorVersion < 18 || ($majorVersion == 18 && $minorVersion < 9)) {
$this->dockerBuildkitSupported = false;
$this->dockerBuildxAvailable = false;
$this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} on {$serverName} does not support BuildKit (requires 18.09+).");
return;
@@ -427,8 +437,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if (trim($buildxAvailable) === 'available') {
$this->dockerBuildkitSupported = true;
$this->dockerBuildxAvailable = true;
$this->application_deployment_queue->addLogEntry("Docker {$dockerVersion} with BuildKit and Buildx detected on {$serverName}.");
} else {
$this->dockerBuildxAvailable = false;
// Fallback: test DOCKER_BUILDKIT=1 support via --progress flag
$buildkitTest = instant_remote_process(
["DOCKER_BUILDKIT=1 docker build --help 2>&1 | grep -q '\\-\\-progress' && echo 'supported' || echo 'not-supported'"],
@@ -461,6 +474,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
} catch (Exception $e) {
$this->dockerBuildkitSupported = false;
$this->dockerBuildxAvailable = false;
$this->dockerSecretsSupported = false;
$this->application_deployment_queue->addLogEntry("Could not detect BuildKit capabilities on {$serverName}: {$e->getMessage()}");
}
@@ -484,8 +498,12 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->deploy_dockerfile_buildpack();
} elseif ($this->application->build_pack === 'static') {
$this->deploy_static_buildpack();
} else {
} elseif ($this->application->build_pack === 'nixpacks') {
$this->deploy_nixpacks_buildpack();
} elseif ($this->application->build_pack === 'railpack') {
$this->deploy_railpack_buildpack();
} else {
throw new DeploymentException("Unsupported build pack: {$this->application->build_pack}");
}
$this->post_deployment();
}
@@ -938,6 +956,37 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->rolling_update();
}
private function deploy_railpack_buildpack(): void
{
if ($this->use_build_server) {
$this->server = $this->build_server;
}
$this->application_deployment_queue->addLogEntry("Starting deployment of {$this->customRepository}:{$this->application->git_branch} to {$this->server->name}.");
$this->prepare_builder_image();
$this->check_git_if_build_needed();
$this->generate_image_names();
if (! $this->force_rebuild) {
$this->check_image_locally_or_remotely();
if ($this->should_skip_build()) {
return;
}
}
$this->clone_repository();
$this->cleanup_git();
$this->generate_compose_file();
// Save build-time .env file BEFORE the build
$this->save_buildtime_environment_variables();
$this->generate_build_env_variables();
$this->build_railpack_image();
// Save runtime environment variables AFTER the build
$this->save_runtime_environment_variables();
$this->push_to_docker_registry();
$this->rolling_update();
}
private function deploy_static_buildpack()
{
if ($this->use_build_server) {
@@ -1105,12 +1154,15 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->production_image_name = "{$this->dockerImage}:{$this->dockerImageTag}";
}
} elseif ($this->pull_request_id !== 0) {
$previewImageTag = $this->previewImageTag();
$previewBuildImageTag = $this->previewImageTag(build: true);
if ($this->application->docker_registry_image_name) {
$this->build_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}-build";
$this->production_image_name = "{$this->application->docker_registry_image_name}:pr-{$this->pull_request_id}";
$this->build_image_name = "{$this->application->docker_registry_image_name}:{$previewBuildImageTag}";
$this->production_image_name = "{$this->application->docker_registry_image_name}:{$previewImageTag}";
} else {
$this->build_image_name = "{$this->application->uuid}:pr-{$this->pull_request_id}-build";
$this->production_image_name = "{$this->application->uuid}:pr-{$this->pull_request_id}";
$this->build_image_name = "{$this->application->uuid}:{$previewBuildImageTag}";
$this->production_image_name = "{$this->application->uuid}:{$previewImageTag}";
}
} else {
$this->dockerImageTag = str($this->commit)->substr(0, 128);
@@ -1127,6 +1179,27 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
}
private function previewImageTag(bool $build = false): string
{
$prefix = "pr-{$this->pull_request_id}-";
$suffix = $build ? '-build' : '';
$maxCommitLength = max(1, 128 - strlen($prefix) - strlen($suffix));
$commitSource = ($this->commit === 'HEAD' || blank($this->commit))
? $this->deployment_uuid
: $this->commit;
$commit = Str::of($commitSource)
->replaceMatches('/[^A-Za-z0-9_.-]/', '-')
->substr(0, $maxCommitLength)
->toString();
if ($commit === '') {
$commit = 'HEAD';
}
return "{$prefix}{$commit}{$suffix}";
}
private function just_restart()
{
$this->application_deployment_queue->addLogEntry("Restarting {$this->customRepository}:{$this->application->git_branch} on {$this->server->name}.");
@@ -1217,11 +1290,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$envs = collect([]);
$sort = $this->application->settings->is_env_sorting_enabled;
if ($sort) {
$sorted_environment_variables = $this->application->environment_variables->sortBy('key');
$sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('key');
$sorted_environment_variables = $this->application->runtime_environment_variables->sortBy('key');
$sorted_environment_variables_preview = $this->application->runtime_environment_variables_preview->sortBy('key');
} else {
$sorted_environment_variables = $this->application->environment_variables->sortBy('id');
$sorted_environment_variables_preview = $this->application->environment_variables_preview->sortBy('id');
$sorted_environment_variables = $this->application->runtime_environment_variables->sortBy('id');
$sorted_environment_variables_preview = $this->application->runtime_environment_variables_preview->sortBy('id');
}
if ($this->build_pack === 'dockercompose') {
$sorted_environment_variables = $sorted_environment_variables->filter(function ($env) {
@@ -1592,6 +1665,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
// 4. Add user-defined build-time variables LAST (highest priority - can override everything)
if ($this->pull_request_id === 0) {
$sorted_environment_variables = $this->application->environment_variables()
->withoutBuildpackControlVariables()
->where('is_buildtime', true) // ONLY build-time variables
->orderBy($this->application->settings->is_env_sorting_enabled ? 'key' : 'id')
->get();
@@ -1644,6 +1718,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
} else {
$sorted_environment_variables = $this->application->environment_variables_preview()
->withoutBuildpackControlVariables()
->where('is_buildtime', true) // ONLY build-time variables
->orderBy($this->application->settings->is_env_sorting_enabled ? 'key' : 'id')
->get();
@@ -1983,7 +2058,11 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
if ($this->application->build_pack === 'dockerfile') {
$this->add_build_env_variables_to_dockerfile();
}
$this->build_image();
if ($this->application->build_pack === 'railpack') {
$this->build_railpack_image();
} else {
$this->build_image();
}
// This overwrites the build-time .env with ALL variables (build-time + runtime)
$this->save_runtime_environment_variables();
@@ -2422,7 +2501,409 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->env_nixpacks_args = $this->env_nixpacks_args->implode(' ');
}
private function generate_coolify_env_variables(bool $forBuildTime = false): Collection
private function generate_railpack_env_variables(): Collection
{
$variables = $this->railpack_build_variables();
$this->env_railpack_args = $variables
->map(function ($value, $key) {
return '--env '.escapeShellValue("{$key}={$value}");
})
->implode(' ');
return $variables;
}
private function normalize_resolved_build_variable_value(EnvironmentVariable $environmentVariable): ?string
{
$resolvedValue = $environmentVariable->getResolvedValueWithServer($this->mainServer);
if (is_null($resolvedValue) || $resolvedValue === '') {
return null;
}
if ($environmentVariable->is_literal || $environmentVariable->is_multiline) {
return trim($resolvedValue, "'");
}
return $resolvedValue;
}
/**
* All buildtime variables that must reach the Railpack build.
*
* Railpack's BuildKit frontend treats every `--env` passed to `railpack prepare`
* as a build secret entry in the generated plan, then pairs it with `--secret id=,env=`
* on `docker buildx build`. Because Railpack's schema disallows top-level `variables`
* (unlike Nixpacks, which bakes variables into the plan), this `--env` → `--secret`
* channel is the only way user-defined buildtime variables become available to
* commands declared with `useSecrets: true`.
*/
private function railpack_build_variables(): Collection
{
$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();
$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 === '') {
return [];
}
return [$environmentVariable->key => $value];
});
if ($this->application->install_command) {
$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) {
if (! is_null($value) && $value !== '') {
$variables->put($key, $value);
}
}
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()) {
return '';
}
return 'env '.$variables
->map(function ($value, $key) {
return escapeShellValue("{$key}={$value}");
})
->implode(' ').' ';
}
private function railpack_build_secret_flags(Collection $variables): string
{
if ($variables->isEmpty()) {
return '';
}
return ' '.$variables
->map(function ($value, $key) {
return '--secret '.escapeShellValue("id={$key},env={$key}");
})
->implode(' ');
}
private function railpack_build_command(string $imageName, Collection $variables): string
{
$cacheArgs = '';
if ($this->force_rebuild) {
$cacheArgs = '--no-cache';
} else {
$cacheArgs = "--build-arg cache-key='{$this->application->uuid}'";
}
if ($variables->isNotEmpty()) {
$cacheArgs .= ' --build-arg secrets-hash='.$this->generate_secrets_hash($variables);
}
$environmentPrefix = $this->railpack_build_environment_prefix($variables);
$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"
." {$this->addHosts} --network host"
." --build-arg BUILDKIT_SYNTAX=\"{$frontendImage}\""
." {$cacheArgs}"
."{$secretFlags}"
.' -f /artifacts/railpack-plan.json'
.' --progress plain'
.' --load'
." -t {$imageName}"
." {$this->workdir}";
}
private function decode_railpack_config(string $config, string $source): array
{
try {
$decoded = json_decode($config, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw new DeploymentException("Invalid {$source}: {$exception->getMessage()}", $exception->getCode(), $exception);
}
if (! is_array($decoded)) {
throw new DeploymentException("Invalid {$source}: expected a JSON object.");
}
return $decoded;
}
private function is_assoc_array(array $value): bool
{
if ($value === []) {
return false;
}
return array_keys($value) !== range(0, count($value) - 1);
}
private function merge_railpack_config(array $base, array $overrides): array
{
foreach ($overrides as $key => $value) {
if (
array_key_exists($key, $base)
&& is_array($base[$key])
&& is_array($value)
&& $this->is_assoc_array($base[$key])
&& $this->is_assoc_array($value)
) {
$base[$key] = $this->merge_railpack_config($base[$key], $value);
} else {
$base[$key] = $value;
}
}
return $base;
}
private function railpack_config_overrides(): array
{
return [];
}
private function generated_railpack_config_relative_path(): string
{
return self::RAILPACK_GENERATED_CONFIG_PATH;
}
private function generated_railpack_config_absolute_path(): string
{
return "{$this->workdir}/".self::RAILPACK_GENERATED_CONFIG_PATH;
}
private function generate_railpack_config_file(): ?string
{
$repositoryConfig = [];
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "test -f {$this->workdir}/".self::RAILPACK_REPOSITORY_CONFIG_PATH." && echo 'exists' || echo 'missing'"),
'hidden' => true,
'save' => 'railpack_config_exists',
]);
if (str($this->saved_outputs->get('railpack_config_exists'))->trim()->toString() === 'exists') {
$this->execute_remote_command([
executeInDocker($this->deployment_uuid, "cat {$this->workdir}/".self::RAILPACK_REPOSITORY_CONFIG_PATH),
'hidden' => true,
'save' => 'railpack_repository_config',
]);
$repositoryConfig = $this->decode_railpack_config(
$this->saved_outputs->get('railpack_repository_config', ''),
'repository railpack.json'
);
}
$overrides = $this->railpack_config_overrides();
if ($repositoryConfig === [] && $overrides === []) {
return null;
}
$mergedConfig = $this->merge_railpack_config($repositoryConfig, $overrides);
if (! array_key_exists('$schema', $mergedConfig)) {
$mergedConfig['$schema'] = 'https://schema.railpack.com';
}
try {
$encodedConfig = json_encode($mergedConfig, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
} catch (JsonException $exception) {
throw new DeploymentException("Failed to encode generated Railpack config: {$exception->getMessage()}", $exception->getCode(), $exception);
}
$configPath = $this->generated_railpack_config_absolute_path();
$encodedConfig = base64_encode($encodedConfig);
$this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, "mkdir -p {$this->workdir}/.coolify"),
'hidden' => true,
],
[
executeInDocker($this->deployment_uuid, "echo '{$encodedConfig}' | base64 -d | tee {$configPath} > /dev/null"),
'hidden' => true,
]
);
if (isDev()) {
$this->application_deployment_queue->addLogEntry('Generated Railpack config: '.json_encode($mergedConfig, JSON_PRETTY_PRINT), hidden: true);
}
return $this->generated_railpack_config_relative_path();
}
private function railpack_prepare_command(?string $configFilePath = null): string
{
$prepare_command = 'railpack prepare';
if ($this->application->build_command) {
$prepare_command .= ' --build-cmd '.escapeShellValue($this->application->build_command);
}
if ($this->application->start_command) {
$prepare_command .= ' --start-cmd '.escapeShellValue($this->application->start_command);
}
if ($this->env_railpack_args) {
$prepare_command .= " {$this->env_railpack_args}";
}
if ($configFilePath) {
$prepare_command .= ' --config-file '.escapeShellValue($configFilePath);
}
$prepare_command .= " --plan-out /artifacts/railpack-plan.json {$this->workdir}";
return $prepare_command;
}
private function ensure_docker_buildx_available_for_railpack(): void
{
if ($this->dockerBuildxAvailable) {
return;
}
throw new DeploymentException('Railpack deployments require the Docker buildx CLI plugin on the build server. Install or enable docker buildx and retry the deployment.');
}
private function build_railpack_image(): void
{
$this->ensure_docker_buildx_available_for_railpack();
$railpackVariables = $this->generate_railpack_env_variables();
$railpackConfigPath = $this->generate_railpack_config_file();
// Step 1: Generate build plan with railpack prepare
$prepare_command = $this->railpack_prepare_command($railpackConfigPath);
$this->application_deployment_queue->addLogEntry('Generating Railpack build plan.');
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, $prepare_command), 'hidden' => true],
[
executeInDocker($this->deployment_uuid, 'cat /artifacts/railpack-plan.json'),
'hidden' => true,
'save' => 'railpack_plan',
],
);
$railpackPlanRaw = $this->saved_outputs->get('railpack_plan');
if (! empty($railpackPlanRaw)) {
if (isDev()) {
$this->application_deployment_queue->addLogEntry("Final Railpack plan: {$railpackPlanRaw}", hidden: true);
} else {
$parsedPlan = json_decode($railpackPlanRaw, true);
if (is_array($parsedPlan)) {
// Strip secrets array to avoid logging variable names in production.
unset($parsedPlan['secrets']);
$this->application_deployment_queue->addLogEntry('Final Railpack plan: '.json_encode($parsedPlan, JSON_PRETTY_PRINT), hidden: true);
}
}
}
// Step 2: Build image using docker buildx with railpack frontend.
// Railpack's frontend requires full BuildKit (mergeop), so we use a docker-container driver builder.
$this->application_deployment_queue->addLogEntry('Building docker image with Railpack.');
$this->application_deployment_queue->addLogEntry('To check the current progress, click on Show Debug Logs.');
$image_name = $this->application->settings->is_static
? $this->build_image_name
: $this->production_image_name;
if ($this->application->settings->is_static && $this->application->static_image) {
$this->pull_latest_image($this->application->static_image);
}
$build_command = $this->railpack_build_command($image_name, $railpackVariables);
$base64_build_command = base64_encode($build_command);
$this->execute_remote_command(
[
executeInDocker($this->deployment_uuid, "echo '{$base64_build_command}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'),
'hidden' => true,
],
[
executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH),
'hidden' => true,
],
[
executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH),
'hidden' => true,
]
);
// Step 3: If static, copy built assets into nginx image
if ($this->application->settings->is_static) {
$this->build_railpack_static_image();
}
}
private function build_railpack_static_image(): void
{
$publishDir = trim($this->application->publish_directory, '/');
$publishDir = $publishDir ? "/{$publishDir}" : '';
$dockerfile = base64_encode("FROM {$this->application->static_image}
WORKDIR /usr/share/nginx/html/
LABEL coolify.deploymentId={$this->deployment_uuid}
COPY --from={$this->build_image_name} /app{$publishDir} .
COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if (str($this->application->custom_nginx_configuration)->isNotEmpty()) {
$nginx_config = base64_encode($this->application->custom_nginx_configuration);
} else {
$nginx_config = $this->application->settings->is_spa
? base64_encode(defaultNginxConfiguration('spa'))
: base64_encode(defaultNginxConfiguration());
}
$static_build = $this->dockerBuildkitSupported
? "DOCKER_BUILDKIT=1 docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile --progress plain -t {$this->production_image_name} {$this->workdir}"
: "docker build {$this->addHosts} --network host -f {$this->workdir}/Dockerfile -t {$this->production_image_name} {$this->workdir}";
$base64_static_build = base64_encode($static_build);
$this->execute_remote_command(
[executeInDocker($this->deployment_uuid, "echo '{$dockerfile}' | base64 -d | tee {$this->workdir}/Dockerfile > /dev/null")],
[executeInDocker($this->deployment_uuid, "echo '{$nginx_config}' | base64 -d | tee {$this->workdir}/nginx.conf > /dev/null")],
[executeInDocker($this->deployment_uuid, "echo '{$base64_static_build}' | base64 -d | tee ".self::BUILD_SCRIPT_PATH.' > /dev/null'), 'hidden' => true],
[executeInDocker($this->deployment_uuid, 'cat '.self::BUILD_SCRIPT_PATH), 'hidden' => true],
[executeInDocker($this->deployment_uuid, 'bash '.self::BUILD_SCRIPT_PATH), 'hidden' => true],
);
}
protected function generate_coolify_env_variables(bool $forBuildTime = false): Collection
{
$coolify_envs = collect([]);
$local_branch = $this->branch;
@@ -2538,7 +3019,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
// For build process, include only environment variables where is_buildtime = true
if ($this->pull_request_id === 0) {
$envs = $this->application->environment_variables()
->where('key', 'not like', 'NIXPACKS_%')
->withoutBuildpackControlVariables()
->where('is_buildtime', true)
->get();
@@ -2550,7 +3031,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
}
} else {
$envs = $this->application->environment_variables_preview()
->where('key', 'not like', 'NIXPACKS_%')
->withoutBuildpackControlVariables()
->where('is_buildtime', true)
->get();
@@ -3075,29 +3556,28 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
$this->execute_remote_command([executeInDocker($this->deployment_uuid, 'rm '.self::NIXPACKS_PLAN_PATH), 'hidden' => true]);
} else {
// Dockerfile buildpack
$safeNetwork = escapeshellarg($this->destination->network);
if ($this->dockerSecretsSupported) {
// Modify the Dockerfile to use build secrets
$this->modify_dockerfile_for_secrets("{$this->workdir}{$this->dockerfile_location}");
$secrets_flags = $this->build_secrets ? " {$this->build_secrets}" : '';
if ($this->force_rebuild) {
$build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->buildTarget} --network {$safeNetwork} -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t $this->build_image_name {$this->workdir}");
$build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t $this->build_image_name {$this->workdir}");
} else {
$build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->buildTarget} --network {$safeNetwork} -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t $this->build_image_name {$this->workdir}");
$build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location}{$secrets_flags} --progress plain -t $this->build_image_name {$this->workdir}");
}
} elseif ($this->dockerBuildkitSupported) {
// BuildKit without secrets
if ($this->force_rebuild) {
$build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->buildTarget} --network {$safeNetwork} -f {$this->workdir}{$this->dockerfile_location} --progress plain -t $this->build_image_name {$this->build_args} {$this->workdir}");
$build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} --progress plain -t $this->build_image_name {$this->build_args} {$this->workdir}");
} else {
$build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->buildTarget} --network {$safeNetwork} -f {$this->workdir}{$this->dockerfile_location} --progress plain -t $this->build_image_name {$this->build_args} {$this->workdir}");
$build_command = $this->wrap_build_command_with_env_export("DOCKER_BUILDKIT=1 docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} --progress plain -t $this->build_image_name {$this->build_args} {$this->workdir}");
}
} else {
// Traditional build with args
if ($this->force_rebuild) {
$build_command = $this->wrap_build_command_with_env_export("docker build --no-cache {$this->buildTarget} --network {$safeNetwork} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} -t $this->build_image_name {$this->workdir}");
$build_command = $this->wrap_build_command_with_env_export("docker build --no-cache {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} -t $this->build_image_name {$this->workdir}");
} else {
$build_command = $this->wrap_build_command_with_env_export("docker build {$this->buildTarget} --network {$safeNetwork} -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} -t $this->build_image_name {$this->workdir}");
$build_command = $this->wrap_build_command_with_env_export("docker build {$this->buildTarget} {$this->addHosts} --network host -f {$this->workdir}{$this->dockerfile_location} {$this->build_args} -t $this->build_image_name {$this->workdir}");
}
}
$base64_build_command = base64_encode($build_command);
@@ -3638,7 +4118,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if ($this->pull_request_id === 0) {
// Only add environment variables that are available during build
$envs = $this->application->environment_variables()
->where('key', 'not like', 'NIXPACKS_%')
->withoutBuildpackControlVariables()
->where('is_buildtime', true)
->get();
foreach ($envs as $env) {
@@ -3660,7 +4140,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()
->where('key', 'not like', 'NIXPACKS_%')
->withoutBuildpackControlVariables()
->where('is_buildtime', true)
->get();
foreach ($envs as $env) {