feat: preview deployment logs

This commit is contained in:
Andras Bacsai 2024-02-27 09:01:19 +01:00
parent a8970df91b
commit 8ab72c7e10
5 changed files with 46 additions and 30 deletions

View File

@ -23,6 +23,7 @@ class GetLogs extends Component
public ServiceApplication|ServiceDatabase|null $servicesubtype = null;
public Server $server;
public ?string $container = null;
public ?string $pull_request = null;
public ?bool $streamLogs = false;
public ?bool $showTimeStamps = true;
public int $numberOfLines = 100;
@ -72,6 +73,11 @@ public function getLogs($refresh = false)
{
if (!$refresh && $this->resource?->getMorphClass() === 'App\Models\Service') return;
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->server->isSwarm()) {
$sshCommand = generateSshCommand($this->server, "docker service logs -n {$this->numberOfLines} -t {$this->container}");

View File

@ -41,13 +41,19 @@ public function mount()
]
]);
} else {
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, 0);
$containers = getCurrentApplicationContainerStatus($this->server, $this->resource->id, includePullrequests: true);
}
if ($containers->count() > 0) {
$containers->each(function ($container) {
$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')) {
$this->type = 'database';
$resource = StandalonePostgresql::where('uuid', $this->parameters['database_uuid'])->first();
@ -70,21 +76,15 @@ public function mount()
$this->status = $this->resource->status;
$this->server = $this->resource->destination->server;
$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')) {
$this->type = 'service';
$this->resource = Service::where('uuid', $this->parameters['service_uuid'])->firstOrFail();
$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) {
// 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;

View File

@ -8,18 +8,21 @@
use Illuminate\Support\Str;
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([]);
if (!$server->isSwarm()) {
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server);
$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');
if (!str($labels)->contains("coolify.pullRequestId=")) {
data_set($container, 'Labels', $labels . ",coolify.pullRequestId={$pullRequestId}");
return $container;
}
if ($includePullrequests) {
return $container;
}
if (str($labels)->contains("coolify.pullRequestId=$pullRequestId")) {
return $container;
}

View File

@ -86,15 +86,21 @@ class="text-warning">{{ $application->destination->server->name }}</span>.</div>
Redeploy
@endif
</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
href="{{ route('project.application.deployment.index', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
<x-forms.button class="bg-coolgray-500">
Get Deployment Logs
Deployment Logs
</x-forms.button>
</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>
@endforeach

View File

@ -1,23 +1,24 @@
<div>
<div x-init="$wire.getLogs">
<div class="flex gap-2">
<h4>Container: {{ $container }}</h4>
<div x-init="$wire.getLogs" id="screen" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }" >
<div class="flex items-center gap-2">
<h3>{{ $container }}</h3>
<div>({{$pull_request}})</div>
@if ($streamLogs)
<span wire:poll.2000ms='getLogs(true)' class="loading loading-xs text-warning loading-spinner"></span>
@endif
</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="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>
<div id="screen" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }" :class="fullscreen ? 'fullscreen' : '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"
:class="fullscreen ? '' : 'max-h-[40rem] border border-solid rounded'">
<div :class="fullscreen ? 'fullscreen' : 'relative w-full py-4 mx-auto'">
<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-96 border border-solid rounded'">
<button title="Minimize" x-show="fullscreen" class="fixed top-4 right-4"
x-on:click="makeFullscreen"><svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
@ -36,8 +37,8 @@ class="fixed top-4 right-16" x-on:click="toggleScroll"><svg class="icon" viewBox
stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4" />
</svg></button>
<button title="Fullscreen" x-show="!fullscreen" class="absolute top-2 right-2"
x-on:click="makeFullscreen"><svg class=" icon" viewBox="0 0 24 24"
<button title="Fullscreen" x-show="!fullscreen" class="absolute top-6 right-4"
x-on:click="makeFullscreen"><svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<g fill="none">
<path