Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/Jobs/ApplicationDeploymentJob.php
Original file line number Diff line number Diff line change
Expand Up @@ -2487,7 +2487,7 @@ private function generate_compose_file()
return escapeDollarSign($value);
});
}
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->application->project()->name, $this->application->name, $this->application->environment->name, $this->pull_request_id))->toArray();
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->application->project()->name, $this->application->name, $this->application->environment->name, $this->pull_request_id, tags: $this->application->tags))->toArray();

Comment on lines +2490 to 2491
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid an extra query: eager-load tags before using $this->application->tags.

Self-hosting on real servers is great; surprise DB queries in hot paths are less great. Consider loading tags once in the constructor (or just before label generation).

Proposed fix (example)
-        $this->application = Application::find($this->application_deployment_queue->application_id);
+        $this->application = Application::with('tags')->find($this->application_deployment_queue->application_id);
🤖 Prompt for AI Agents
In @app/Jobs/ApplicationDeploymentJob.php around lines 2490 - 2491, The code
calls $this->application->tags inside ApplicationDeploymentJob when building
$labels which triggers an extra DB query; eager-load the tags beforehand (e.g.,
in the job constructor or immediately before label generation) by loading the
relationship on the Application model (use load or loadMissing for 'tags') so
that defaultLabels(...) receives the already-loaded tags and no additional query
is executed at merge time; update the ApplicationDeploymentJob constructor or
the label-generation path to ensure $this->application->relationLoaded('tags')
is true before calling defaultLabels.

// Check for custom HEALTHCHECK
if ($this->application->build_pack === 'dockerfile' || $this->application->dockerfile) {
Expand Down
31 changes: 30 additions & 1 deletion bootstrap/helpers/docker.php
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,23 @@ function get_port_from_dockerfile($dockerfile): ?int
return null;
}

function formatTagsForDockerLabel($tags, int $limit = 50): string
{
if (! $tags || $tags->isEmpty()) {
return '';
}

return $tags->take($limit)
->pluck('name')
->map(function ($tag) {
// Sanitize to only allow characters safe for Docker labels
// Allows: alphanumeric, hyphens, underscores, periods, colons, forward slashes
return preg_replace('/[^a-zA-Z0-9\-_.:\/]/', '', $tag);
})
->filter() // Remove empty strings after sanitization
->implode(' ');
}
Comment on lines +212 to +227
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Harden formatTagsForDockerLabel() against non-Collection inputs (arrays/relations) to avoid fatal errors.

Right now, an array (or relation object) passed into this helper will trigger a runtime crash. Skynet doesn’t even need serverless for that—just one unexpected type and “I’ll be back” becomes your queue retry loop.

Proposed fix
-function formatTagsForDockerLabel($tags, int $limit = 50): string
+function formatTagsForDockerLabel(Collection|array|null $tags, int $limit = 50): string
 {
-    if (! $tags || $tags->isEmpty()) {
+    $tags = collect($tags);
+    if ($tags->isEmpty()) {
         return '';
     }
 
     return $tags->take($limit)
         ->pluck('name')
         ->map(function ($tag) {
             // Sanitize to only allow characters safe for Docker labels
             // Allows: alphanumeric, hyphens, underscores, periods, colons, forward slashes
-            return preg_replace('/[^a-zA-Z0-9\-_.:\/]/', '', $tag);
+            $sanitized = preg_replace('/[^a-zA-Z0-9\-_.:\/]/', '', (string) $tag);
+            return $sanitized ?? '';
         })
         ->filter() // Remove empty strings after sanitization
         ->implode(' ');
 }
🤖 Prompt for AI Agents
In @bootstrap/helpers/docker.php around lines 212 - 227,
formatTagsForDockerLabel currently assumes $tags is a Collection and will fatal
on arrays or relation objects; fix by normalizing input at the start (use
collect($tags) or ensure it's an instance of Illuminate\Support\Collection) so
arrays, Eloquent relations, null, and other iterables are safely handled, then
apply the existing take($limit)->pluck('name')->map(...)->filter()->implode(' ')
chain on the normalized collection and return '' when empty; reference the
function name formatTagsForDockerLabel and replace direct
$tags->isEmpty()/->take calls with operations on the collected instance.


function defaultDatabaseLabels($database)
{
$labels = collect([]);
Expand All @@ -221,10 +238,15 @@ function defaultDatabaseLabels($database)
$labels->push('coolify.environmentName='.Str::slug($database->environment->name));
$labels->push('coolify.database.subType='.$database->type());

$tagsLabel = formatTagsForDockerLabel($database->tags ?? collect());
if ($tagsLabel) {
$labels->push('coolify.tags='.$tagsLabel);
}

return $labels;
}

function defaultLabels($id, $name, string $projectName, string $resourceName, string $environment, $pull_request_id = 0, string $type = 'application', $subType = null, $subId = null, $subName = null)
function defaultLabels($id, $name, string $projectName, string $resourceName, string $environment, $pull_request_id = 0, string $type = 'application', $subType = null, $subId = null, $subName = null, $tags = null)
{
$labels = collect([]);
$labels->push('coolify.managed=true');
Expand All @@ -244,6 +266,13 @@ function defaultLabels($id, $name, string $projectName, string $resourceName, st
$subName && $labels->push('coolify.service.subName='.Str::slug($subName));
}

if ($tags) {
$tagsLabel = formatTagsForDockerLabel($tags);
if ($tagsLabel) {
$labels->push('coolify.tags='.$tagsLabel);
}
}

return $labels;
}

Expand Down
2 changes: 2 additions & 0 deletions bootstrap/helpers/parsers.php
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
pull_request_id: $pullRequestId,
type: 'application',
environment: $resource->environment->name,
tags: $resource->tags,
);

$isDatabase = isDatabaseImage($image, $service);
Expand Down Expand Up @@ -2272,6 +2273,7 @@ function serviceParser(Service $resource): Collection
subId: $savedService->id,
subName: $savedService->human_name ?? $savedService->name,
environment: $resource->environment->name,
tags: $resource->tags,
);

// Add COOLIFY_FQDN & COOLIFY_URL to environment
Expand Down
4 changes: 3 additions & 1 deletion bootstrap/helpers/shared.php
Original file line number Diff line number Diff line change
Expand Up @@ -1918,6 +1918,7 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
subId: $savedService->id,
subName: $savedService->name,
environment: $resource->environment->name,
tags: $resource->tags,
);
$serviceLabels = $serviceLabels->merge($defaultLabels);
if (! $isDatabase && $fqdns->count() > 0) {
Expand Down Expand Up @@ -2765,7 +2766,8 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
resourceName: $resource->name,
environment: $resource->environment->name,
pull_request_id: $pull_request_id,
type: 'application'
type: 'application',
tags: $resource->tags,
);
$serviceLabels = $serviceLabels->merge($defaultLabels);

Expand Down