Merge branch 'next' into improve-scheduled-tasks
This commit is contained in:
commit
f9ab2a7ca8
13 changed files with 77 additions and 76 deletions
|
|
@ -10,6 +10,7 @@
|
|||
use App\Models\ServiceDatabase;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
||||
class GetContainersStatus
|
||||
|
|
@ -376,6 +377,10 @@ public function handle(Server $server, ?Collection $containers = null, ?Collecti
|
|||
if (isset($this->applicationContainerRestartCounts) && $this->applicationContainerRestartCounts->has($applicationId)) {
|
||||
$containerRestartCounts = $this->applicationContainerRestartCounts->get($applicationId);
|
||||
$maxRestartCount = $containerRestartCounts->max() ?? 0;
|
||||
}
|
||||
|
||||
// Wrap all database updates in a transaction to ensure consistency
|
||||
DB::transaction(function () use ($application, $maxRestartCount, $containerStatuses) {
|
||||
$previousRestartCount = $application->restart_count ?? 0;
|
||||
|
||||
if ($maxRestartCount > $previousRestartCount) {
|
||||
|
|
@ -398,18 +403,18 @@ public function handle(Server $server, ?Collection $containers = null, ?Collecti
|
|||
$url = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aggregate status after tracking restart counts
|
||||
$aggregatedStatus = $this->aggregateApplicationStatus($application, $containerStatuses, $maxRestartCount);
|
||||
if ($aggregatedStatus) {
|
||||
$statusFromDb = $application->status;
|
||||
if ($statusFromDb !== $aggregatedStatus) {
|
||||
$application->update(['status' => $aggregatedStatus]);
|
||||
} else {
|
||||
$application->update(['last_online_at' => now()]);
|
||||
// Aggregate status after tracking restart counts
|
||||
$aggregatedStatus = $this->aggregateApplicationStatus($application, $containerStatuses, $maxRestartCount);
|
||||
if ($aggregatedStatus) {
|
||||
$statusFromDb = $application->status;
|
||||
if ($statusFromDb !== $aggregatedStatus) {
|
||||
$application->update(['status' => $aggregatedStatus]);
|
||||
} else {
|
||||
$application->update(['last_online_at' => now()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,22 +20,23 @@ public function handle(Service $service, bool $pullLatestImages = false, bool $s
|
|||
}
|
||||
$service->saveComposeConfigs();
|
||||
$service->isConfigurationChanged(save: true);
|
||||
$commands[] = 'cd '.$service->workdir();
|
||||
$commands[] = "echo 'Saved configuration files to {$service->workdir()}.'";
|
||||
// Ensure .env file exists before docker compose tries to load it
|
||||
$workdir = $service->workdir();
|
||||
// $commands[] = "cd {$workdir}";
|
||||
$commands[] = "echo 'Saved configuration files to {$workdir}.'";
|
||||
// Ensure .env exists in the correct directory before docker compose tries to load it
|
||||
// This is defensive programming - saveComposeConfigs() already creates it,
|
||||
// but we guarantee it here in case of any edge cases or manual deployments
|
||||
$commands[] = 'touch .env';
|
||||
$commands[] = "touch {$workdir}/.env";
|
||||
if ($pullLatestImages) {
|
||||
$commands[] = "echo 'Pulling images.'";
|
||||
$commands[] = 'docker compose pull';
|
||||
$commands[] = "docker compose --project-directory {$workdir} pull";
|
||||
}
|
||||
if ($service->networks()->count() > 0) {
|
||||
$commands[] = "echo 'Creating Docker network.'";
|
||||
$commands[] = "docker network inspect $service->uuid >/dev/null 2>&1 || docker network create --attachable $service->uuid";
|
||||
}
|
||||
$commands[] = 'echo Starting service.';
|
||||
$commands[] = 'docker compose up -d --remove-orphans --force-recreate --build';
|
||||
$commands[] = "docker compose --project-directory {$workdir} -f {$workdir}/docker-compose.yml --project-name {$service->uuid} up -d --remove-orphans --force-recreate --build";
|
||||
$commands[] = "docker network connect $service->uuid coolify-proxy >/dev/null 2>&1 || true";
|
||||
if (data_get($service, 'connect_to_docker_network')) {
|
||||
$compose = data_get($service, 'docker_compose', []);
|
||||
|
|
|
|||
|
|
@ -222,9 +222,14 @@ private function cleanup_stucked_resources()
|
|||
try {
|
||||
$scheduled_backups = ScheduledDatabaseBackup::all();
|
||||
foreach ($scheduled_backups as $scheduled_backup) {
|
||||
if (! $scheduled_backup->server()) {
|
||||
echo "Deleting stuck scheduledbackup: {$scheduled_backup->name}\n";
|
||||
$scheduled_backup->delete();
|
||||
try {
|
||||
$server = $scheduled_backup->server();
|
||||
if (! $server) {
|
||||
echo "Deleting stuck scheduledbackup: {$scheduled_backup->name}\n";
|
||||
$scheduled_backup->delete();
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error checking server for scheduledbackup {$scheduled_backup->id}: {$e->getMessage()}\n";
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
|
|
@ -416,7 +421,7 @@ private function cleanup_stucked_resources()
|
|||
foreach ($serviceApplications as $service) {
|
||||
if (! data_get($service, 'service')) {
|
||||
echo 'ServiceApplication without service: '.$service->name.'\n';
|
||||
DeleteResourceJob::dispatch($service);
|
||||
$service->forceDelete();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
|
@ -429,7 +434,7 @@ private function cleanup_stucked_resources()
|
|||
foreach ($serviceDatabases as $service) {
|
||||
if (! data_get($service, 'service')) {
|
||||
echo 'ServiceDatabase without service: '.$service->name.'\n';
|
||||
DeleteResourceJob::dispatch($service);
|
||||
$service->forceDelete();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,7 @@ public function handle()
|
|||
$this->call('cleanup:stucked-resources');
|
||||
} catch (\Throwable $e) {
|
||||
echo "Error in cleanup:stucked-resources command: {$e->getMessage()}\n";
|
||||
echo "Continuing with initialization - cleanup errors will not prevent Coolify from starting\n";
|
||||
}
|
||||
try {
|
||||
$updatedCount = ApplicationDeploymentQueue::whereIn('status', [
|
||||
|
|
|
|||
|
|
@ -275,16 +275,6 @@ public function manual(Request $request)
|
|||
instant_remote_process(["docker rm -f {$deployment_uuid}"], $server);
|
||||
$activeDeployment->addLogEntry('Deployment container stopped.');
|
||||
}
|
||||
|
||||
// Kill running process if process ID exists
|
||||
if ($activeDeployment->current_process_id) {
|
||||
try {
|
||||
$processKillCommand = "kill -9 {$activeDeployment->current_process_id}";
|
||||
instant_remote_process([$processKillCommand], $server);
|
||||
} catch (\Throwable $e) {
|
||||
// Process might already be gone
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Silently handle errors during deployment cancellation
|
||||
}
|
||||
|
|
@ -555,15 +545,6 @@ public function normal(Request $request)
|
|||
$activeDeployment->addLogEntry('Deployment container stopped.');
|
||||
}
|
||||
|
||||
// Kill running process if process ID exists
|
||||
if ($activeDeployment->current_process_id) {
|
||||
try {
|
||||
$processKillCommand = "kill -9 {$activeDeployment->current_process_id}";
|
||||
instant_remote_process([$processKillCommand], $server);
|
||||
} catch (\Throwable $e) {
|
||||
// Process might already be gone
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Silently handle errors during deployment cancellation
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1173,7 +1173,10 @@ private function generate_runtime_environment_variables()
|
|||
if ($this->build_pack !== 'dockercompose') {
|
||||
$detectedPort = $this->application->detectPortFromEnvironment(false);
|
||||
if ($detectedPort && ! empty($ports) && ! in_array($detectedPort, $ports)) {
|
||||
ray()->orange("PORT environment variable ({$detectedPort}) does not match configured ports_exposes: ".implode(',', $ports));
|
||||
$this->application_deployment_queue->addLogEntry(
|
||||
"Warning: PORT environment variable ({$detectedPort}) does not match configured ports_exposes: ".implode(',', $ports).'. It could case "bad gateway" or "no server" errors. Check the "General" page to fix it.',
|
||||
'stderr'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3062,7 +3065,6 @@ private function start_by_compose_file()
|
|||
{
|
||||
// Ensure .env file exists before docker compose tries to load it (defensive programming)
|
||||
$this->execute_remote_command(
|
||||
["touch {$this->workdir}/.env", 'hidden' => true],
|
||||
["touch {$this->configuration_dir}/.env", 'hidden' => true],
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -145,25 +145,17 @@ private function deleteApplicationPreview()
|
|||
|
||||
// Check if helper container exists and kill it
|
||||
$deployment_uuid = $activeDeployment->deployment_uuid;
|
||||
$checkCommand = "docker ps -a --filter name={$deployment_uuid} --format '{{.Names}}'";
|
||||
$escapedDeploymentUuid = escapeshellarg($deployment_uuid);
|
||||
$checkCommand = "docker ps -a --filter name={$escapedDeploymentUuid} --format '{{.Names}}'";
|
||||
$containerExists = instant_remote_process([$checkCommand], $server);
|
||||
|
||||
if ($containerExists && str($containerExists)->trim()->isNotEmpty()) {
|
||||
instant_remote_process(["docker rm -f {$deployment_uuid}"], $server);
|
||||
instant_remote_process(["docker rm -f {$escapedDeploymentUuid}"], $server);
|
||||
$activeDeployment->addLogEntry('Deployment container stopped.');
|
||||
} else {
|
||||
$activeDeployment->addLogEntry('Helper container not yet started. Deployment will be cancelled when job checks status.');
|
||||
}
|
||||
|
||||
// Kill running process if process ID exists
|
||||
if ($activeDeployment->current_process_id) {
|
||||
try {
|
||||
$processKillCommand = "kill -9 {$activeDeployment->current_process_id}";
|
||||
instant_remote_process([$processKillCommand], $server);
|
||||
} catch (\Throwable $e) {
|
||||
// Process might already be gone
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
// Silently handle errors during deployment cancellation
|
||||
}
|
||||
|
|
@ -171,7 +163,8 @@ private function deleteApplicationPreview()
|
|||
|
||||
try {
|
||||
if ($server->isSwarm()) {
|
||||
instant_remote_process(["docker stack rm {$application->uuid}-{$pull_request_id}"], $server);
|
||||
$escapedStackName = escapeshellarg("{$application->uuid}-{$pull_request_id}");
|
||||
instant_remote_process(["docker stack rm {$escapedStackName}"], $server);
|
||||
} else {
|
||||
$containers = getCurrentApplicationContainerStatus($server, $application->id, $pull_request_id)->toArray();
|
||||
$this->stopPreviewContainers($containers, $server);
|
||||
|
|
|
|||
|
|
@ -94,14 +94,6 @@ public function deploy(bool $force_rebuild = false)
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset restart count on deployment
|
||||
$this->application->update([
|
||||
'restart_count' => 0,
|
||||
'last_restart_at' => null,
|
||||
'last_restart_type' => null,
|
||||
]);
|
||||
|
||||
$this->setDeploymentUuid();
|
||||
$result = queue_application_deployment(
|
||||
application: $this->application,
|
||||
|
|
@ -109,11 +101,18 @@ public function deploy(bool $force_rebuild = false)
|
|||
force_rebuild: $force_rebuild,
|
||||
);
|
||||
if ($result['status'] === 'skipped') {
|
||||
$this->dispatch('success', 'Deployment skipped', $result['message']);
|
||||
$this->dispatch('error', 'Deployment skipped', $result['message']);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset restart count on successful deployment
|
||||
$this->application->update([
|
||||
'restart_count' => 0,
|
||||
'last_restart_at' => null,
|
||||
'last_restart_type' => null,
|
||||
]);
|
||||
|
||||
return $this->redirectRoute('project.application.deployment.show', [
|
||||
'project_uuid' => $this->parameters['project_uuid'],
|
||||
'application_uuid' => $this->parameters['application_uuid'],
|
||||
|
|
@ -146,13 +145,6 @@ public function restart()
|
|||
return;
|
||||
}
|
||||
|
||||
// Reset restart count on manual restart
|
||||
$this->application->update([
|
||||
'restart_count' => 0,
|
||||
'last_restart_at' => now(),
|
||||
'last_restart_type' => 'manual',
|
||||
]);
|
||||
|
||||
$this->setDeploymentUuid();
|
||||
$result = queue_application_deployment(
|
||||
application: $this->application,
|
||||
|
|
@ -165,6 +157,13 @@ public function restart()
|
|||
return;
|
||||
}
|
||||
|
||||
// Reset restart count on manual restart
|
||||
$this->application->update([
|
||||
'restart_count' => 0,
|
||||
'last_restart_at' => now(),
|
||||
'last_restart_type' => 'manual',
|
||||
]);
|
||||
|
||||
return $this->redirectRoute('project.application.deployment.show', [
|
||||
'project_uuid' => $this->parameters['project_uuid'],
|
||||
'application_uuid' => $this->parameters['application_uuid'],
|
||||
|
|
|
|||
|
|
@ -149,9 +149,10 @@ public function submit($notify = true)
|
|||
$this->service->save();
|
||||
$this->service->saveExtraFields($this->fields);
|
||||
$this->service->parse();
|
||||
$this->service->refresh();
|
||||
$this->service->saveComposeConfigs();
|
||||
});
|
||||
// Refresh and write files after a successful commit
|
||||
$this->service->refresh();
|
||||
$this->service->saveComposeConfigs();
|
||||
|
||||
$this->dispatch('refreshEnvs');
|
||||
$this->dispatch('refreshServices');
|
||||
|
|
|
|||
|
|
@ -1302,7 +1302,13 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
|
|||
}
|
||||
// Auto-inject .env file so Coolify environment variables are available inside containers
|
||||
// This makes Applications behave consistently with manual .env file usage
|
||||
$payload['env_file'] = ['.env'];
|
||||
$existingEnvFiles = data_get($service, 'env_file');
|
||||
$envFiles = collect(is_null($existingEnvFiles) ? [] : (is_array($existingEnvFiles) ? $existingEnvFiles : [$existingEnvFiles]))
|
||||
->push('.env')
|
||||
->unique()
|
||||
->values();
|
||||
|
||||
$payload['env_file'] = $envFiles;
|
||||
if ($isPullRequest) {
|
||||
$serviceName = addPreviewDeploymentSuffix($serviceName, $pullRequestId);
|
||||
}
|
||||
|
|
@ -2284,7 +2290,13 @@ function serviceParser(Service $resource): Collection
|
|||
}
|
||||
// Auto-inject .env file so Coolify environment variables are available inside containers
|
||||
// This makes Services behave consistently with Applications
|
||||
$payload['env_file'] = ['.env'];
|
||||
$existingEnvFiles = data_get($service, 'env_file');
|
||||
$envFiles = collect(is_null($existingEnvFiles) ? [] : (is_array($existingEnvFiles) ? $existingEnvFiles : [$existingEnvFiles]))
|
||||
->push('.env')
|
||||
->unique()
|
||||
->values();
|
||||
|
||||
$payload['env_file'] = $envFiles;
|
||||
|
||||
$parsedServices->put($serviceName, $payload);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ services:
|
|||
openpanel-worker:
|
||||
image: lindesvard/openpanel-worker:latest
|
||||
environment:
|
||||
- DISABLE_BULLBOARD=${DISABLE_BULLBOARD:-1}
|
||||
- NODE_ENV=production
|
||||
- NEXT_PUBLIC_SELF_HOSTED=true
|
||||
- SERVICE_URL_OPBULLBOARD
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue