fix: pull requests

feat: add follow for full screen logs
This commit is contained in:
Andras Bacsai 2023-11-01 20:55:21 +01:00
parent b557ea1e1d
commit 0838343841
7 changed files with 98 additions and 52 deletions

View File

@ -138,9 +138,16 @@ public function __construct(int $application_deployment_queue_id)
public function handle(): void
{
// ray()->measure();
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id);
if ($containers->count() > 0) {
$containers = getCurrentApplicationContainerStatus($this->server, $this->application->id, $this->pull_request_id);
if ($containers->count() === 1) {
$this->currently_running_container_name = data_get($containers[0], 'Names');
} else {
$foundContainer = $containers->filter(function ($container) {
return !str(data_get($container, 'Names'))->startsWith("{$this->application->uuid}-pr-");
})->first();
if ($foundContainer) {
$this->currently_running_container_name = data_get($foundContainer, 'Names');
}
}
if ($this->pull_request_id !== 0 && $this->pull_request_id !== null) {
$this->currently_running_container_name = $this->container_name;
@ -747,14 +754,21 @@ private function generate_compose_file()
$labels = collect(generateLabelsApplication($this->application, $this->preview));
}
if ($this->pull_request_id !== 0) {
$newLabels = collect(generateLabelsApplication($this->application, $this->preview));
$newHostLabel = $newLabels->filter(function ($label) {
return str($label)->contains('Host');
});
$labels = $labels->reject(function ($label) {
return str($label)->contains('Host');
});
$newLabels = collect(generateLabelsApplication($this->application, $this->preview));
$hostLabels = $newLabels->filter(function ($label) {
return str($label)->contains('Host');
$labels = $labels->map(function ($label) {
$pattern = '/([a-zA-Z0-9]+)-(\d+)-(http|https)/';
$replacement = "$1-pr-{$this->pull_request_id}-$2-$3";
$newLabel = preg_replace($pattern, $replacement, $label);
return $newLabel;
});
$labels = $labels->merge($hostLabels);
$labels = $labels->merge($newHostLabel);
}
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
$docker_compose = [

View File

@ -18,7 +18,6 @@
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
{
@ -26,6 +25,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
public function __construct(public Server $server)
{
$this->handle();
}
public function middleware(): array
{

View File

@ -6,11 +6,14 @@
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Spatie\Url\Url;
use Visus\Cuid2\Cuid2;
function getCurrentApplicationContainerStatus(Server $server, int $id): Collection
function getCurrentApplicationContainerStatus(Server $server, int $id, ?int $pullRequestId = null): Collection
{
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}' "], $server);
if ($pullRequestId) {
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --filter='label=coolify.pullRequestId={$pullRequestId}' --format '{{json .}}' "], $server);
} else {
$containers = instant_remote_process(["docker ps -a --filter='label=coolify.applicationId={$id}' --format '{{json .}}'"], $server);
}
if (!$containers) {
return collect([]);
}
@ -77,20 +80,6 @@ function executeInDocker(string $containerId, string $command)
// return "docker exec {$this->deployment_uuid} bash -c '{$command} |& tee -a /proc/1/fd/1; [ \$PIPESTATUS -eq 0 ] || exit \$PIPESTATUS'";
}
function getApplicationContainerStatus(Application $application)
{
$server = data_get($application, 'destination.server');
$id = $application->id;
if (!$server) {
return 'exited';
}
$containers = getCurrentApplicationContainerStatus($server, $id);
if ($containers->count() > 0) {
$status = data_get($containers[0], 'State', 'exited');
return $status;
}
return 'exited';
}
function getContainerStatus(Server $server, string $container_id, bool $all_data = false, bool $throwError = false)
{
$container = instant_remote_process(["docker inspect --format '{{json .}}' {$container_id}"], $server, $throwError);
@ -212,9 +201,9 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
$onlyPort = $ports[0];
}
$pull_request_id = data_get($preview, 'pull_request_id', 0);
$appId = $application->id;
if ($pull_request_id !== 0 && $pull_request_id !== null) {
$appId = $appId . '-pr-' . $pull_request_id;
$appUuid = $application->uuid;
if ($pull_request_id !== 0) {
$appUuid = $appUuid . '-pr-' . $pull_request_id;
}
$labels = collect([]);
if ($application->fqdn) {
@ -224,7 +213,7 @@ function generateLabelsApplication(Application $application, ?ApplicationPreview
$domains = Str::of(data_get($application, 'fqdn'))->explode(',');
}
// Add Traefik labels no matter which proxy is selected
$labels = $labels->merge(fqdnLabelsForTraefik($application->uuid, $domains, $application->settings->is_force_https_enabled, $onlyPort));
$labels = $labels->merge(fqdnLabelsForTraefik($appUuid, $domains, $application->settings->is_force_https_enabled, $onlyPort));
}
return $labels->all();
}

View File

@ -122,5 +122,5 @@ .subtitle {
@apply pt-2 pb-10;
}
.fullscreen {
@apply fixed top-0 left-0 w-full h-full z-[9999] bg-coolgray-200 overflow-y-auto scrollbar ;
@apply fixed top-0 left-0 w-full h-full z-[9999] bg-coolgray-100 overflow-y-auto scrollbar pb-4 ;
}

View File

@ -1,4 +1,4 @@
<div class="pt-4" x-data="{ fullscreen: false }">
<div class="pt-4" x-data="{ fullscreen: false, alwaysScroll: false, intervalId: null }">
<livewire:project.application.deployment-navbar :application_deployment_queue="$application_deployment_queue" />
@if (data_get($application_deployment_queue, 'status') === 'in_progress')
<div class="flex items-center gap-1 pt-2 ">Deployment is
@ -6,24 +6,35 @@
</div>
<x-loading class="loading-ring" />
</div>
<div class="">Logs will be updated automatically.</div>
{{-- <div class="">Logs will be updated automatically.</div> --}}
@else
<div class="pt-2 ">Deployment is <span
class="text-warning">{{ Str::headline(data_get($application_deployment_queue, 'status')) }}</span>.
</div>
@endif
<div :class="fullscreen ? 'fullscreen' : ''">
<div id="screen" :class="fullscreen ? 'fullscreen' : ''">
<div @if ($isKeepAliveOn) wire:poll.2000ms="polling" @endif
class="relative flex flex-col-reverse w-full p-2 px-4 mt-4 overflow-y-auto text-xs border border-dotted rounded scrollbar border-coolgray-400"
:class="fullscreen ? '' : 'max-h-[32rem]'">
<button title="Minimize" x-show="fullscreen" class="fixed top-2 right-2"
x-on:click="fullscreen = !fullscreen"><svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
class="relative flex flex-col-reverse w-full p-2 px-4 mt-4 overflow-y-auto scrollbar border-coolgray-400"
:class="fullscreen ? '' : 'max-h-[40rem] border border-dotted 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">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />
</svg></button>
<button title="Go Top" x-show="fullscreen" class="fixed top-4 right-28" x-on:click="goTop"> <svg
class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M12 5v14m4-10l-4-4M8 9l4-4" />
</svg></button>
<button title="Follow Logs" x-show="fullscreen" :class="alwaysScroll ? 'text-warning' : ''"
class="fixed top-4 right-16" x-on:click="toggleScroll"><svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
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-8"
x-on:click="fullscreen = !fullscreen"><svg class="fixed icon" viewBox="0 0 24 24"
x-on:click="makeFullscreen"><svg class="fixed icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<g fill="none">
<path
@ -32,7 +43,7 @@ class="relative flex flex-col-reverse w-full p-2 px-4 mt-4 overflow-y-auto text-
d="M9.793 12.793a1 1 0 0 1 1.497 1.32l-.083.094L6.414 19H9a1 1 0 0 1 .117 1.993L9 21H4a1 1 0 0 1-.993-.883L3 20v-5a1 1 0 0 1 1.993-.117L5 15v2.586l4.793-4.793ZM20 3a1 1 0 0 1 .993.883L21 4v5a1 1 0 0 1-1.993.117L19 9V6.414l-4.793 4.793a1 1 0 0 1-1.497-1.32l.083-.094L17.586 5H15a1 1 0 0 1-.117-1.993L15 3h5Z" />
</g>
</svg></button>
<span class="flex flex-col">
<div id="logs" class="flex flex-col">
@if (decode_remote_command_output($application_deployment_queue)->count() > 0)
@foreach (decode_remote_command_output($application_deployment_queue) as $line)
<div @class([
@ -49,7 +60,40 @@ class="relative flex flex-col-reverse w-full p-2 px-4 mt-4 overflow-y-auto text-
@else
<span class="font-mono text-neutral-400">No logs yet.</span>
@endif
</span>
</div>
</div>
</div>
<script>
function makeFullscreen() {
this.fullscreen = !this.fullscreen;
if (this.fullscreen === false) {
this.alwaysScroll = false;
clearInterval(this.intervalId);
}
}
function toggleScroll() {
this.alwaysScroll = !this.alwaysScroll;
if (this.alwaysScroll) {
this.intervalId = setInterval(() => {
const screen = document.getElementById('screen');
const logs = document.getElementById('logs');
if (screen.scrollTop !== logs.scrollHeight) {
screen.scrollTop = logs.scrollHeight;
}
}, 100);
} else {
clearInterval(this.intervalId);
this.intervalId = null;
}
}
function goTop() {
this.alwaysScroll = false;
clearInterval(this.intervalId);
const screen = document.getElementById('screen');
screen.scrollTop = 0;
}
</script>
</div>

View File

@ -78,11 +78,9 @@
Redeploy
@endif
</x-forms.button>
@if (data_get($preview, 'status') !== 'exited')
<x-forms.button wire:click="stop({{ data_get($preview, 'pull_request_id') }})">Remove
Preview
</x-forms.button>
@endif
<x-forms.button wire:click="stop({{ data_get($preview, 'pull_request_id') }})">Remove
Preview
</x-forms.button>
<a
href="{{ route('project.application.deployments', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
<x-forms.button>

View File

@ -13,17 +13,18 @@
<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 x-data="{ fullscreen: false }" :class="fullscreen ? 'fullscreen' : 'container w-full pt-4 mx-auto'">
<div
class="relative flex flex-col-reverse w-full p-4 pt-6 overflow-y-auto text-xs text-white border border-solid rounded scrollbar border-coolgray-300" :class="fullscreen ? '' : 'max-h-[32rem]'">
<button title="Minimize" x-show="fullscreen" class="fixed top-2 right-2" x-on:click="fullscreen = !fullscreen"><svg
class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<div class="relative flex flex-col-reverse w-full p-4 pt-6 overflow-y-auto text-white scrollbar border-coolgray-300"
:class="fullscreen ? '' : 'max-h-[40rem] border border-solid rounded'">
<button title="Minimize" x-show="fullscreen" class="fixed top-4 right-4"
x-on:click="fullscreen = !fullscreen"><svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />
</svg></button>
<button title="Fullscreen" x-show="!fullscreen" class="absolute top-2 right-8" x-on:click="fullscreen = !fullscreen"><svg
class="fixed icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<button title="Fullscreen" x-show="!fullscreen" class="absolute top-2 right-8"
x-on:click="fullscreen = !fullscreen"><svg class="fixed icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<g fill="none">
<path
d="M24 0v24H0V0h24ZM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018Zm.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022Zm-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01l-.184-.092Z" />