fix: pull requests deployments
feat: filter deployments logs by pull requests
This commit is contained in:
parent
be8ea78b1b
commit
4520070df3
@ -41,7 +41,7 @@ class ApplicationController extends Controller
|
|||||||
if (!$application) {
|
if (!$application) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 8);
|
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40);
|
||||||
return view('project.application.deployments', ['application' => $application, 'deployments' => $deployments, 'deployments_count' => $count]);
|
return view('project.application.deployments', ['application' => $application, 'deployments' => $deployments, 'deployments_count' => $count]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,24 +3,31 @@
|
|||||||
namespace App\Http\Livewire\Project\Application;
|
namespace App\Http\Livewire\Project\Application;
|
||||||
|
|
||||||
use App\Models\Application;
|
use App\Models\Application;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
|
||||||
class Deployments extends Component
|
class Deployments extends Component
|
||||||
{
|
{
|
||||||
public Application $application;
|
public Application $application;
|
||||||
public $deployments = [];
|
public Array|Collection $deployments = [];
|
||||||
public int $deployments_count = 0;
|
public int $deployments_count = 0;
|
||||||
public string $current_url;
|
public string $current_url;
|
||||||
public int $skip = 0;
|
public int $skip = 0;
|
||||||
public int $default_take = 8;
|
public int $default_take = 40;
|
||||||
public bool $show_next = false;
|
public bool $show_next = false;
|
||||||
|
public ?string $pull_request_id = null;
|
||||||
|
protected $queryString = ['pull_request_id'];
|
||||||
public function mount()
|
public function mount()
|
||||||
{
|
{
|
||||||
$this->current_url = url()->current();
|
$this->current_url = url()->current();
|
||||||
|
$this->show_pull_request_only();
|
||||||
$this->show_more();
|
$this->show_more();
|
||||||
}
|
}
|
||||||
|
private function show_pull_request_only() {
|
||||||
|
if ($this->pull_request_id) {
|
||||||
|
$this->deployments = $this->deployments->where('pull_request_id', $this->pull_request_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
private function show_more()
|
private function show_more()
|
||||||
{
|
{
|
||||||
if (count($this->deployments) !== 0) {
|
if (count($this->deployments) !== 0) {
|
||||||
@ -47,6 +54,7 @@ class Deployments extends Component
|
|||||||
['deployments' => $deployments, 'count' => $count] = $this->application->deployments($this->skip, $take);
|
['deployments' => $deployments, 'count' => $count] = $this->application->deployments($this->skip, $take);
|
||||||
$this->deployments = $deployments;
|
$this->deployments = $deployments;
|
||||||
$this->deployments_count = $count;
|
$this->deployments_count = $count;
|
||||||
|
$this->show_pull_request_only();
|
||||||
$this->show_more();
|
$this->show_more();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -487,9 +487,7 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
}
|
}
|
||||||
private function deploy_pull_request()
|
private function deploy_pull_request()
|
||||||
{
|
{
|
||||||
$this->build_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}-build");
|
$this->generate_image_names();
|
||||||
$this->production_image_name = Str::lower("{$this->application->uuid}:pr-{$this->pull_request_id}");
|
|
||||||
// ray('Build Image Name: ' . $this->build_image_name . ' & Production Image Name: ' . $this->production_image_name)->green();
|
|
||||||
$this->execute_remote_command([
|
$this->execute_remote_command([
|
||||||
"echo 'Starting pull request (#{$this->pull_request_id}) deployment of {$this->application->git_repository}:{$this->application->git_branch}.'",
|
"echo 'Starting pull request (#{$this->pull_request_id}) deployment of {$this->application->git_repository}:{$this->application->git_branch}.'",
|
||||||
]);
|
]);
|
||||||
@ -505,7 +503,12 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
// $this->generate_build_env_variables();
|
// $this->generate_build_env_variables();
|
||||||
// $this->add_build_env_variables_to_dockerfile();
|
// $this->add_build_env_variables_to_dockerfile();
|
||||||
$this->build_image();
|
$this->build_image();
|
||||||
$this->stop_running_container();
|
if ($this->currently_running_container_name) {
|
||||||
|
$this->execute_remote_command(
|
||||||
|
["echo -n 'Removing old version of your application.'"],
|
||||||
|
[executeInDocker($this->deployment_uuid, "docker rm -f $this->currently_running_container_name >/dev/null 2>&1"), "hidden" => true, "ignore_errors" => true],
|
||||||
|
);
|
||||||
|
}
|
||||||
$this->execute_remote_command(
|
$this->execute_remote_command(
|
||||||
["echo -n 'Starting preview deployment.'"],
|
["echo -n 'Starting preview deployment.'"],
|
||||||
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
[executeInDocker($this->deployment_uuid, "docker compose --project-directory {$this->workdir} up -d"), "hidden" => true],
|
||||||
@ -743,6 +746,16 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
} else {
|
} else {
|
||||||
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
$labels = collect(generateLabelsApplication($this->application, $this->preview));
|
||||||
}
|
}
|
||||||
|
if ($this->pull_request_id !== 0) {
|
||||||
|
$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->merge($hostLabels);
|
||||||
|
}
|
||||||
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
|
$labels = $labels->merge(defaultLabels($this->application->id, $this->application->uuid, $this->pull_request_id))->toArray();
|
||||||
$docker_compose = [
|
$docker_compose = [
|
||||||
'version' => '3.8',
|
'version' => '3.8',
|
||||||
|
@ -138,11 +138,10 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
$containerStatus = "$containerStatus ($containerHealth)";
|
$containerStatus = "$containerStatus ($containerHealth)";
|
||||||
$labels = data_get($container, 'Config.Labels');
|
$labels = data_get($container, 'Config.Labels');
|
||||||
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
$labels = Arr::undot(format_docker_labels_to_json($labels));
|
||||||
$labelId = data_get($labels, 'coolify.applicationId');
|
$applicationId = data_get($labels, 'coolify.applicationId');
|
||||||
if ($labelId) {
|
if ($applicationId) {
|
||||||
if (str_contains($labelId, '-pr-')) {
|
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
||||||
$pullRequestId = data_get($labels, 'coolify.pullRequestId');
|
if ($pullRequestId) {
|
||||||
$applicationId = (int) Str::before($labelId, '-pr-');
|
|
||||||
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
$preview = ApplicationPreview::where('application_id', $applicationId)->where('pull_request_id', $pullRequestId)->first();
|
||||||
if ($preview) {
|
if ($preview) {
|
||||||
$foundApplicationPreviews[] = $preview->id;
|
$foundApplicationPreviews[] = $preview->id;
|
||||||
@ -154,7 +153,7 @@ class ContainerStatusJob implements ShouldQueue, ShouldBeEncrypted
|
|||||||
//Notify user that this container should not be there.
|
//Notify user that this container should not be there.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$application = $applications->where('id', $labelId)->first();
|
$application = $applications->where('id', $applicationId)->first();
|
||||||
if ($application) {
|
if ($application) {
|
||||||
$foundApplications[] = $application->id;
|
$foundApplications[] = $application->id;
|
||||||
$statusFromDb = $application->status;
|
$statusFromDb = $application->status;
|
||||||
|
@ -16,14 +16,14 @@
|
|||||||
<div @if ($isKeepAliveOn) wire:poll.2000ms="polling" @endif
|
<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="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]'">
|
:class="fullscreen ? '' : 'max-h-[32rem]'">
|
||||||
<button title="Minimize" x-show="fullscreen" class="absolute top-2 right-2"
|
<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"
|
x-on:click="fullscreen = !fullscreen"><svg class="icon" viewBox="0 0 24 24"
|
||||||
xmlns="http://www.w3.org/2000/svg">
|
xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
<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" />
|
stroke-width="2" d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />
|
||||||
</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-2 right-8"
|
||||||
x-on:click="fullscreen = !fullscreen"><svg class="icon" viewBox="0 0 24 24"
|
x-on:click="fullscreen = !fullscreen"><svg class="fixed 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
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
<div class="flex flex-col gap-2" @if ($skip == 0) wire:poll.5000ms='reload_deployments' @endif>
|
<div class="flex flex-col gap-2 pb-10" @if ($skip == 0) wire:poll.5000ms='reload_deployments' @endif>
|
||||||
<h2 class="pt-4">Deployments <span class="text-xs">({{ $deployments_count }})</span></h2>
|
<div class="flex items-end gap-2 pt-4">
|
||||||
@if ($show_next)
|
<h2>Deployments <span class="text-xs">({{ $deployments_count }})</span></h2>
|
||||||
<x-forms.button wire:click="load_deployments({{ $default_take }})">Show More
|
@if ($show_next)
|
||||||
</x-forms.button>
|
<x-forms.button wire:click="load_deployments({{ $default_take }})">Next Page
|
||||||
@endif
|
</x-forms.button>
|
||||||
@foreach ($deployments as $deployment)
|
@endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<form wire:submit="filter" class="flex items-end gap-2">
|
||||||
|
<x-forms.input id="pull_request_id" label="Pull Request"></x-forms.input>
|
||||||
|
<x-forms.button type="submit">Filter</x-forms.button>
|
||||||
|
</form>
|
||||||
|
@forelse ($deployments as $deployment)
|
||||||
<a @class([
|
<a @class([
|
||||||
'bg-coolgray-200 p-2 border-l border-dashed transition-colors hover:no-underline',
|
'bg-coolgray-200 p-2 border-l border-dashed transition-colors hover:no-underline',
|
||||||
'cursor-not-allowed hover:bg-coolgray-200' =>
|
'cursor-not-allowed hover:bg-coolgray-200' =>
|
||||||
@ -22,73 +29,76 @@
|
|||||||
class="hover:no-underline">
|
class="hover:no-underline">
|
||||||
<div class="flex flex-col justify-start">
|
<div class="flex flex-col justify-start">
|
||||||
<div>
|
<div>
|
||||||
{{ $deployment->id }} <span class=" text-warning">></span> {{ $deployment->deployment_uuid }}
|
{{ $deployment->created_at }} UTC
|
||||||
<span class=" text-warning">></span>
|
<span class=" text-warning">></span>
|
||||||
{{ $deployment->status }}
|
{{ $deployment->status }}
|
||||||
</div>
|
@if (data_get($deployment, 'pull_request_id'))
|
||||||
@if (data_get($deployment, 'pull_request_id'))
|
<span class=" text-warning">></span>
|
||||||
<div>
|
|
||||||
Pull Request #{{ data_get($deployment, 'pull_request_id') }}
|
Pull Request #{{ data_get($deployment, 'pull_request_id') }}
|
||||||
@if (data_get($deployment, 'is_webhook'))
|
@if (data_get($deployment, 'is_webhook'))
|
||||||
(Webhook)
|
(Webhook)
|
||||||
@endif
|
@endif
|
||||||
</div>
|
@elseif (data_get($deployment, 'is_webhook'))
|
||||||
@elseif (data_get($deployment, 'is_webhook'))
|
<span class=" text-warning">></span>
|
||||||
<div>Webhook (sha
|
</div>
|
||||||
@if (data_get($deployment, 'commit'))
|
Webhook (sha
|
||||||
{{ data_get($deployment, 'commit') }})
|
@if (data_get($deployment, 'commit'))
|
||||||
@else
|
{{ data_get($deployment, 'commit') }})
|
||||||
HEAD)
|
@else
|
||||||
@endif
|
HEAD)
|
||||||
</div>
|
|
||||||
@endif
|
@endif
|
||||||
<div class="flex flex-col" x-data="elapsedTime('{{ $deployment->deployment_uuid }}', '{{ $deployment->status }}', '{{ $deployment->created_at }}', '{{ $deployment->updated_at }}')">
|
@endif
|
||||||
<div>
|
</div>
|
||||||
@if ($deployment->status !== 'in_progress')
|
|
||||||
Finished <span x-text="measure_since_started()">0s</span> in
|
<div class="flex flex-col" x-data="elapsedTime('{{ $deployment->deployment_uuid }}', '{{ $deployment->status }}', '{{ $deployment->created_at }}', '{{ $deployment->updated_at }}')">
|
||||||
@else
|
<div>
|
||||||
Running for
|
@if ($deployment->status !== 'in_progress')
|
||||||
@endif
|
Finished <span x-text="measure_since_started()">0s</span> in
|
||||||
<span class="font-bold" x-text="measure_finished_time()">0s</span>
|
@else
|
||||||
</div>
|
Running for
|
||||||
|
@endif
|
||||||
|
<span class="font-bold" x-text="measure_finished_time()">0s</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</div>
|
||||||
@endforeach
|
</a>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js"></script>
|
@empty
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/utc.js"></script>
|
<div class="">No deployments found</div>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/relativeTime.js"></script>
|
@endforelse
|
||||||
<script>
|
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js"></script>
|
||||||
document.addEventListener('alpine:init', () => {
|
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/utc.js"></script>
|
||||||
let timers = {};
|
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/relativeTime.js"></script>
|
||||||
|
<script>
|
||||||
dayjs.extend(window.dayjs_plugin_utc);
|
document.addEventListener('alpine:init', () => {
|
||||||
dayjs.extend(window.dayjs_plugin_relativeTime);
|
let timers = {};
|
||||||
|
|
||||||
Alpine.data('elapsedTime', (uuid, status, created_at, updated_at) => ({
|
dayjs.extend(window.dayjs_plugin_utc);
|
||||||
finished_time: 'calculating...',
|
dayjs.extend(window.dayjs_plugin_relativeTime);
|
||||||
started_time: 'calculating...',
|
|
||||||
init() {
|
Alpine.data('elapsedTime', (uuid, status, created_at, updated_at) => ({
|
||||||
if (timers[uuid]) {
|
finished_time: 'calculating...',
|
||||||
clearInterval(timers[uuid]);
|
started_time: 'calculating...',
|
||||||
}
|
init() {
|
||||||
if (status === 'in_progress') {
|
if (timers[uuid]) {
|
||||||
timers[uuid] = setInterval(() => {
|
clearInterval(timers[uuid]);
|
||||||
this.finished_time = dayjs().diff(dayjs.utc(created_at),
|
}
|
||||||
'second') + 's'
|
if (status === 'in_progress') {
|
||||||
}, 1000);
|
timers[uuid] = setInterval(() => {
|
||||||
} else {
|
this.finished_time = dayjs().diff(dayjs.utc(created_at),
|
||||||
let seconds = dayjs.utc(updated_at).diff(dayjs.utc(created_at), 'second')
|
'second') + 's'
|
||||||
this.finished_time = seconds + 's';
|
}, 1000);
|
||||||
}
|
} else {
|
||||||
},
|
let seconds = dayjs.utc(updated_at).diff(dayjs.utc(created_at), 'second')
|
||||||
measure_finished_time() {
|
this.finished_time = seconds + 's';
|
||||||
return this.finished_time;
|
}
|
||||||
},
|
},
|
||||||
measure_since_started() {
|
measure_finished_time() {
|
||||||
return dayjs.utc(created_at).fromNow();
|
return this.finished_time;
|
||||||
}
|
},
|
||||||
}))
|
measure_since_started() {
|
||||||
})
|
return dayjs.utc(created_at).fromNow();
|
||||||
</script>
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
</script>
|
||||||
</div>
|
</div>
|
||||||
|
@ -83,6 +83,12 @@
|
|||||||
Preview
|
Preview
|
||||||
</x-forms.button>
|
</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
|
<a
|
||||||
|
href="{{ route('project.application.deployments', [...$parameters, 'pull_request_id' => data_get($preview, 'pull_request_id')]) }}">
|
||||||
|
<x-forms.button>
|
||||||
|
Get Deployment Logs
|
||||||
|
</x-forms.button>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
<div x-data="{ fullscreen: false }" :class="fullscreen ? 'fullscreen' : 'container w-full pt-4 mx-auto'">
|
<div x-data="{ fullscreen: false }" :class="fullscreen ? 'fullscreen' : 'container w-full pt-4 mx-auto'">
|
||||||
<div
|
<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]'">
|
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="absolute top-2 right-2" x-on:click="fullscreen = !fullscreen"><svg
|
<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="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
|
<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" />
|
stroke-width="2" d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />
|
||||||
</svg></button>
|
</svg></button>
|
||||||
<button title="Fullscreen" x-show="!fullscreen" class="absolute top-2 right-2" x-on:click="fullscreen = !fullscreen"><svg
|
<button title="Fullscreen" x-show="!fullscreen" class="absolute top-2 right-8" x-on:click="fullscreen = !fullscreen"><svg
|
||||||
class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
class="fixed icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g fill="none">
|
<g fill="none">
|
||||||
<path
|
<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" />
|
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" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user