feat: preview deployment logs
This commit is contained in:
@@ -23,6 +23,7 @@ class GetLogs extends Component
|
|||||||
public ServiceApplication|ServiceDatabase|null $servicesubtype = null;
|
public ServiceApplication|ServiceDatabase|null $servicesubtype = null;
|
||||||
public Server $server;
|
public Server $server;
|
||||||
public ?string $container = null;
|
public ?string $container = null;
|
||||||
|
public ?string $pull_request = null;
|
||||||
public ?bool $streamLogs = false;
|
public ?bool $streamLogs = false;
|
||||||
public ?bool $showTimeStamps = true;
|
public ?bool $showTimeStamps = true;
|
||||||
public int $numberOfLines = 100;
|
public int $numberOfLines = 100;
|
||||||
@@ -72,6 +73,11 @@ class GetLogs extends Component
|
|||||||
{
|
{
|
||||||
if (!$refresh && $this->resource?->getMorphClass() === 'App\Models\Service') return;
|
if (!$refresh && $this->resource?->getMorphClass() === 'App\Models\Service') return;
|
||||||
if ($this->container) {
|
if ($this->container) {
|
||||||
|
if (str($this->container)->contains('-pr-')) {
|
||||||
|
$this->pull_request = "Pull Request: " . str($this->container)->afterLast('-pr-')->beforeLast('_')->value();
|
||||||
|
} else {
|
||||||
|
$this->pull_request = 'branch';
|
||||||
|
}
|
||||||
if ($this->showTimeStamps) {
|
if ($this->showTimeStamps) {
|
||||||
if ($this->server->isSwarm()) {
|
if ($this->server->isSwarm()) {
|
||||||
$sshCommand = generateSshCommand($this->server, "docker service logs -n {$this->numberOfLines} -t {$this->container}");
|
$sshCommand = generateSshCommand($this->server, "docker service logs -n {$this->numberOfLines} -t {$this->container}");
|
||||||
|
|||||||
@@ -41,13 +41,19 @@ class Logs extends Component
|
|||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
|
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, includePullrequests: true);
|
||||||
}
|
}
|
||||||
if ($containers->count() > 0) {
|
if ($containers->count() > 0) {
|
||||||
$containers->each(function ($container) {
|
$containers->each(function ($container) {
|
||||||
$this->containers->push(str_replace('/', '', $container['Names']));
|
$this->containers->push(str_replace('/', '', $container['Names']));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
$this->containers = $this->containers->sortByDesc(function ($container) {
|
||||||
|
if (str_contains($container, '-pr-')) {
|
||||||
|
return explode('-pr-', $container)[1];
|
||||||
|
}
|
||||||
|
return $container;
|
||||||
|
});
|
||||||
} else if (data_get($this->parameters, 'database_uuid')) {
|
} else if (data_get($this->parameters, 'database_uuid')) {
|
||||||
$this->type = 'database';
|
$this->type = 'database';
|
||||||
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
|
||||||
@@ -70,21 +76,15 @@ class Logs extends Component
|
|||||||
$this->status = $this->resource->status;
|
$this->status = $this->resource->status;
|
||||||
$this->server = $this->resource->destination->server;
|
$this->server = $this->resource->destination->server;
|
||||||
$this->container = $this->resource->uuid;
|
$this->container = $this->resource->uuid;
|
||||||
// if (str(data_get($this, 'resource.status'))->startsWith('running')) {
|
$this->containers->push($this->container);
|
||||||
$this->containers->push($this->container);
|
|
||||||
// }
|
|
||||||
} else if (data_get($this->parameters, 'service_uuid')) {
|
} else if (data_get($this->parameters, 'service_uuid')) {
|
||||||
$this->type = 'service';
|
$this->type = 'service';
|
||||||
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
|
||||||
$this->resource->applications()->get()->each(function ($application) {
|
$this->resource->applications()->get()->each(function ($application) {
|
||||||
// if (str(data_get($application, 'status'))->contains('running')) {
|
$this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
|
||||||
$this->containers->push(data_get($application, 'name') . '-' . data_get($this->resource, 'uuid'));
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
$this->resource->databases()->get()->each(function ($database) {
|
$this->resource->databases()->get()->each(function ($database) {
|
||||||
// if (str(data_get($database, 'status'))->contains('running')) {
|
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
|
||||||
$this->containers->push(data_get($database, 'name') . '-' . data_get($this->resource, 'uuid'));
|
|
||||||
// }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$this->server = $this->resource->server;
|
$this->server = $this->resource->server;
|
||||||
|
|||||||
@@ -8,18 +8,21 @@ use Illuminate\Support\Collection;
|
|||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
|
|
||||||
function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null): Collection
|
function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null, ?bool $includePullrequests = false): Collection
|
||||||
{
|
{
|
||||||
$containers = collect([]);
|
$containers = collect([]);
|
||||||
if (!$server->isSwarm()) {
|
if (!$server->isSwarm()) {
|
||||||
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server);
|
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server);
|
||||||
$containers = format_docker_command_output_to_json($containers);
|
$containers = format_docker_command_output_to_json($containers);
|
||||||
$containers = $containers->map(function ($container) use ($pullRequestId) {
|
$containers = $containers->map(function ($container) use ($pullRequestId, $includePullrequests) {
|
||||||
$labels = data_get($container, 'Labels');
|
$labels = data_get($container, 'Labels');
|
||||||
if (!str($labels)->contains("coolify.pullRequestId=")) {
|
if (!str($labels)->contains("coolify.pullRequestId=")) {
|
||||||
data_set($container, 'Labels', $labels . ",coolify.pullRequestId={$pullRequestId}");
|
data_set($container, 'Labels', $labels . ",coolify.pullRequestId={$pullRequestId}");
|
||||||
return $container;
|
return $container;
|
||||||
}
|
}
|
||||||
|
if ($includePullrequests) {
|
||||||
|
return $container;
|
||||||
|
}
|
||||||
if (str($labels)->contains("coolify.pullRequestId=$pullRequestId")) {
|
if (str($labels)->contains("coolify.pullRequestId=$pullRequestId")) {
|
||||||
return $container;
|
return $container;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,15 +86,21 @@
|
|||||||
Redeploy
|
Redeploy
|
||||||
@endif
|
@endif
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
<x-forms.button class="bg-coolgray-500"
|
|
||||||
wire:click="stop({{ data_get($preview, 'pull_request_id') }})">Remove Preview
|
|
||||||
</x-forms.button>
|
|
||||||
<a
|
<a
|
||||||
href="{{ route('project.application.deployment.index', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
|
href="{{ route('project.application.deployment.index', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
|
||||||
<x-forms.button class="bg-coolgray-500">
|
<x-forms.button class="bg-coolgray-500">
|
||||||
Get Deployment Logs
|
Deployment Logs
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
href="{{ route('project.application.logs', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
|
||||||
|
<x-forms.button class="bg-coolgray-500">
|
||||||
|
Application Logs
|
||||||
|
</x-forms.button>
|
||||||
|
</a>
|
||||||
|
<x-forms.button isError class="bg-coolgray-500"
|
||||||
|
wire:click="stop({{ data_get($preview, 'pull_request_id') }})">Delete
|
||||||
|
</x-forms.button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
<div>
|
<div>
|
||||||
<div x-init="$wire.getLogs">
|
<div x-init="$wire.getLogs" id="screen" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }" >
|
||||||
<div class="flex gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<h4>Container: {{ $container }}</h4>
|
<h3>{{ $container }}</h3>
|
||||||
|
<div>({{$pull_request}})</div>
|
||||||
@if ($streamLogs)
|
@if ($streamLogs)
|
||||||
<span wire:poll.2000ms='getLogs(true)' class="loading loading-xs text-warning loading-spinner"></span>
|
<span wire:poll.2000ms='getLogs(true)' class="loading loading-xs text-warning loading-spinner"></span>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<form wire:submit='getLogs(true)' class="flex items-end gap-2 pt-2 ">
|
||||||
|
<div class="w-96">
|
||||||
|
<x-forms.input label="Only Show Number of Lines" placeholder="1000" required
|
||||||
|
id="numberOfLines"></x-forms.input>
|
||||||
|
</div>
|
||||||
|
<x-forms.button type="submit">Refresh</x-forms.button>
|
||||||
<x-forms.checkbox instantSave label="Stream Logs" id="streamLogs"></x-forms.checkbox>
|
<x-forms.checkbox instantSave label="Stream Logs" id="streamLogs"></x-forms.checkbox>
|
||||||
<x-forms.checkbox instantSave label="Include Timestamps" id="showTimeStamps"></x-forms.checkbox>
|
<x-forms.checkbox instantSave label="Include Timestamps" id="showTimeStamps"></x-forms.checkbox>
|
||||||
</div>
|
|
||||||
<form wire:submit='getLogs(true)' class="flex items-end gap-2">
|
|
||||||
<x-forms.input label="Only Show Number of Lines" placeholder="1000" required
|
|
||||||
id="numberOfLines"></x-forms.input>
|
|
||||||
<x-forms.button type="submit">Refresh</x-forms.button>
|
|
||||||
</form>
|
</form>
|
||||||
<div id="screen" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }" :class="fullscreen ? 'fullscreen' : 'w-full py-4 mx-auto'">
|
<div :class="fullscreen ? 'fullscreen' : 'relative w-full py-4 mx-auto'">
|
||||||
<div class="relative flex flex-col-reverse w-full p-4 pt-6 overflow-y-auto text-white bg-coolgray-100 scrollbar border-coolgray-300"
|
<div class="flex flex-col-reverse w-full p-4 pt-2 overflow-y-auto text-white bg-coolgray-100 scrollbar border-coolgray-300"
|
||||||
:class="fullscreen ? '' : 'max-h-[40rem] border border-solid rounded'">
|
:class="fullscreen ? '' : 'max-h-96 border border-solid rounded'">
|
||||||
<button title="Minimize" x-show="fullscreen" class="fixed top-4 right-4"
|
<button title="Minimize" x-show="fullscreen" class="fixed top-4 right-4"
|
||||||
x-on:click="makeFullscreen"><svg class="icon" viewBox="0 0 24 24"
|
x-on:click="makeFullscreen"><svg class="icon" viewBox="0 0 24 24"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
@@ -36,8 +37,8 @@
|
|||||||
stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4" />
|
stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4" />
|
||||||
</svg></button>
|
</svg></button>
|
||||||
|
|
||||||
<button title="Fullscreen" x-show="!fullscreen" class="absolute top-2 right-2"
|
<button title="Fullscreen" x-show="!fullscreen" class="absolute top-6 right-4"
|
||||||
x-on:click="makeFullscreen"><svg class=" icon" viewBox="0 0 24 24"
|
x-on:click="makeFullscreen"><svg class="icon" viewBox="0 0 24 24"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<g fill="none">
|
<g fill="none">
|
||||||
<path
|
<path
|
||||||
|
|||||||
Reference in New Issue
Block a user