fix: add finished_at to app deployment jobs
fix: show deployment job running measurements fix: terminal should not be wire:navigated
This commit is contained in:
@@ -316,6 +316,10 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
$this->fail($e);
|
$this->fail($e);
|
||||||
throw $e;
|
throw $e;
|
||||||
} finally {
|
} finally {
|
||||||
|
$this->application_deployment_queue->update([
|
||||||
|
'finished_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
if ($this->use_build_server) {
|
if ($this->use_build_server) {
|
||||||
$this->server = $this->build_server;
|
$this->server = $this->build_server;
|
||||||
} else {
|
} else {
|
||||||
|
@@ -18,7 +18,7 @@ class Index extends Component
|
|||||||
|
|
||||||
public int $skip = 0;
|
public int $skip = 0;
|
||||||
|
|
||||||
public int $default_take = 40;
|
public int $default_take = 10;
|
||||||
|
|
||||||
public bool $show_next = false;
|
public bool $show_next = false;
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ class Index extends Component
|
|||||||
if (! $application) {
|
if (! $application) {
|
||||||
return redirect()->route('dashboard');
|
return redirect()->route('dashboard');
|
||||||
}
|
}
|
||||||
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, 40);
|
['deployments' => $deployments, 'count' => $count] = $application->deployments(0, $this->default_take);
|
||||||
$this->application = $application;
|
$this->application = $application;
|
||||||
$this->deployments = $deployments;
|
$this->deployments = $deployments;
|
||||||
$this->deployments_count = $count;
|
$this->deployments_count = $count;
|
||||||
|
@@ -0,0 +1,22 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('application_deployment_queues', function (Blueprint $table) {
|
||||||
|
$table->timestamp('finished_at')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::table('application_deployment_queues', function (Blueprint $table) {
|
||||||
|
$table->dropColumn('finished_at');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
1
public/js/dayjs-plugin-relativeTime.js
Normal file
1
public/js/dayjs-plugin-relativeTime.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(r,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(r="undefined"!=typeof globalThis?globalThis:r||self).dayjs_plugin_relativeTime=e()}(this,(function(){"use strict";return function(r,e,t){r=r||{};var n=e.prototype,o={future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"};function i(r,e,t,o){return n.fromToBase(r,e,t,o)}t.en.relativeTime=o,n.fromToBase=function(e,n,i,d,u){for(var f,a,s,l=i.$locale().relativeTime||o,h=r.thresholds||[{l:"s",r:44,d:"second"},{l:"m",r:89},{l:"mm",r:44,d:"minute"},{l:"h",r:89},{l:"hh",r:21,d:"hour"},{l:"d",r:35},{l:"dd",r:25,d:"day"},{l:"M",r:45},{l:"MM",r:10,d:"month"},{l:"y",r:17},{l:"yy",d:"year"}],m=h.length,c=0;c<m;c+=1){var y=h[c];y.d&&(f=d?t(e).diff(i,y.d,!0):i.diff(e,y.d,!0));var p=(r.rounding||Math.round)(Math.abs(f));if(s=f>0,p<=y.r||!y.r){p<=1&&c>0&&(y=h[c-1]);var v=l[y.l];u&&(p=u(""+p)),a="string"==typeof v?v.replace("%d",p):v(p,n,y.l,s);break}}if(n)return a;var M=s?l.future:l.past;return"function"==typeof M?M(a):M.replace("%s",a)},n.to=function(r,e){return i(r,e,this,!0)},n.from=function(r,e){return i(r,e,this)};var d=function(r){return r.$u?t.utc():t()};n.toNow=function(r){return this.to(d(this),r)},n.fromNow=function(r){return this.from(d(this),r)}}}));
|
1
public/js/dayjs-plugin-utc.js
Normal file
1
public/js/dayjs-plugin-utc.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(t,i){"object"==typeof exports&&"undefined"!=typeof module?module.exports=i():"function"==typeof define&&define.amd?define(i):(t="undefined"!=typeof globalThis?globalThis:t||self).dayjs_plugin_utc=i()}(this,(function(){"use strict";var t="minute",i=/[+-]\d\d(?::?\d\d)?/g,e=/([+-]|\d\d)/g;return function(s,f,n){var u=f.prototype;n.utc=function(t){var i={date:t,utc:!0,args:arguments};return new f(i)},u.utc=function(i){var e=n(this.toDate(),{locale:this.$L,utc:!0});return i?e.add(this.utcOffset(),t):e},u.local=function(){return n(this.toDate(),{locale:this.$L,utc:!1})};var o=u.parse;u.parse=function(t){t.utc&&(this.$u=!0),this.$utils().u(t.$offset)||(this.$offset=t.$offset),o.call(this,t)};var r=u.init;u.init=function(){if(this.$u){var t=this.$d;this.$y=t.getUTCFullYear(),this.$M=t.getUTCMonth(),this.$D=t.getUTCDate(),this.$W=t.getUTCDay(),this.$H=t.getUTCHours(),this.$m=t.getUTCMinutes(),this.$s=t.getUTCSeconds(),this.$ms=t.getUTCMilliseconds()}else r.call(this)};var a=u.utcOffset;u.utcOffset=function(s,f){var n=this.$utils().u;if(n(s))return this.$u?0:n(this.$offset)?a.call(this):this.$offset;if("string"==typeof s&&(s=function(t){void 0===t&&(t="");var s=t.match(i);if(!s)return null;var f=(""+s[0]).match(e)||["-",0,0],n=f[0],u=60*+f[1]+ +f[2];return 0===u?0:"+"===n?u:-u}(s),null===s))return this;var u=Math.abs(s)<=16?60*s:s,o=this;if(f)return o.$offset=u,o.$u=0===s,o;if(0!==s){var r=this.$u?this.toDate().getTimezoneOffset():-1*this.utcOffset();(o=this.local().add(u+r,t)).$offset=u,o.$x.$localOffset=r}else o=this.utc();return o};var h=u.format;u.format=function(t){var i=t||(this.$u?"YYYY-MM-DDTHH:mm:ss[Z]":"");return h.call(this,i)},u.valueOf=function(){var t=this.$utils().u(this.$offset)?0:this.$offset+(this.$x.$localOffset||this.$d.getTimezoneOffset());return this.$d.valueOf()-6e4*t},u.isUTC=function(){return!!this.$u},u.toISOString=function(){return this.toDate().toISOString()},u.toString=function(){return this.toDate().toUTCString()};var l=u.toDate;u.toDate=function(t){return"s"===t&&this.$offset?n(this.format("YYYY-MM-DD HH:mm:ss:SSS")).toDate():l.call(this)};var c=u.diff;u.diff=function(t,i,e){if(t&&this.$u===t.$u)return c.call(this,t,i,e);var s=this.local(),f=n(t).local();return c.call(s,f,i,e)}}}));
|
1
public/js/dayjs.min.js
vendored
Normal file
1
public/js/dayjs.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -261,7 +261,7 @@
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a title="Terminal" wire:navigate
|
<a title="Terminal"
|
||||||
class="{{ request()->is('terminal*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
class="{{ request()->is('terminal*') ? 'menu-item-active menu-item' : 'menu-item' }}"
|
||||||
href="{{ route('terminal') }}">
|
href="{{ route('terminal') }}">
|
||||||
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
<svg class="icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
|
||||||
|
@@ -39,7 +39,7 @@
|
|||||||
]) }}">
|
]) }}">
|
||||||
<button>Resources</button>
|
<button>Resources</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('server.command') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('server.command') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('server.command', [
|
href="{{ route('server.command', [
|
||||||
'server_uuid' => data_get($server, 'uuid'),
|
'server_uuid' => data_get($server, 'uuid'),
|
||||||
]) }}">
|
]) }}">
|
||||||
|
@@ -40,7 +40,9 @@
|
|||||||
<script type="text/javascript" src="{{ URL::asset('js/echo.js') }}"></script>
|
<script type="text/javascript" src="{{ URL::asset('js/echo.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ URL::asset('js/pusher.js') }}"></script>
|
<script type="text/javascript" src="{{ URL::asset('js/pusher.js') }}"></script>
|
||||||
<script type="text/javascript" src="{{ URL::asset('js/apexcharts.js') }}"></script>
|
<script type="text/javascript" src="{{ URL::asset('js/apexcharts.js') }}"></script>
|
||||||
|
<script type="text/javascript" src="{{ URL::asset('js/dayjs.min.js') }}"></script>
|
||||||
|
<script type="text/javascript" src="{{ URL::asset('js/dayjs-plugin-utc.js') }}"></script>
|
||||||
|
<script type="text/javascript" src="{{ URL::asset('js/dayjs-plugin-relativeTime.js') }}"></script>
|
||||||
@endauth
|
@endauth
|
||||||
</head>
|
</head>
|
||||||
@section('body')
|
@section('body')
|
||||||
|
@@ -5,12 +5,11 @@
|
|||||||
<h1>Deployments</h1>
|
<h1>Deployments</h1>
|
||||||
<livewire:project.shared.configuration-checker :resource="$application" />
|
<livewire:project.shared.configuration-checker :resource="$application" />
|
||||||
<livewire:project.application.heading :application="$application" />
|
<livewire:project.application.heading :application="$application" />
|
||||||
{{-- <livewire:project.application.deployment.show :application="$application" :deployments="$deployments" :deployments_count="$deployments_count" /> --}}
|
|
||||||
<div class="flex flex-col gap-2 pb-10"
|
<div class="flex flex-col gap-2 pb-10"
|
||||||
@if ($skip == 0) wire:poll.5000ms='reload_deployments' @endif>
|
@if ($skip == 0) wire:poll.5000ms='reload_deployments' @endif>
|
||||||
<div class="flex items-end gap-2 pt-4">
|
<div class="flex items-end gap-2 pt-4">
|
||||||
<h2>Deployments <span class="text-xs">({{ $deployments_count }})</span></h2>
|
<h2>Deployments <span class="text-xs">({{ $deployments_count }})</span></h2>
|
||||||
@if ($deployments_count > 0)
|
@if ($deployments_count > 0 && $deployments_count > $default_take)
|
||||||
<x-forms.button disabled="{{ !$show_prev }}" wire:click="previous_page('{{ $default_take }}')"><svg
|
<x-forms.button disabled="{{ !$show_prev }}" wire:click="previous_page('{{ $default_take }}')"><svg
|
||||||
class="w-6 h-6" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
class="w-6 h-6" 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"
|
||||||
@@ -38,8 +37,8 @@
|
|||||||
'border-error border-dashed ' =>
|
'border-error border-dashed ' =>
|
||||||
data_get($deployment, 'status') === 'failed',
|
data_get($deployment, 'status') === 'failed',
|
||||||
'border-success' => data_get($deployment, 'status') === 'finished',
|
'border-success' => data_get($deployment, 'status') === 'finished',
|
||||||
])
|
]) wire:navigate
|
||||||
wire:navigate href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}">
|
href="{{ $current_url . '/' . data_get($deployment, 'deployment_uuid') }}">
|
||||||
<div class="flex flex-col justify-start">
|
<div class="flex flex-col justify-start">
|
||||||
<div class="flex gap-1">
|
<div class="flex gap-1">
|
||||||
{{ $deployment->created_at }} UTC
|
{{ $deployment->created_at }} UTC
|
||||||
@@ -58,8 +57,8 @@
|
|||||||
Pull Request #{{ data_get($deployment, 'pull_request_id') }}
|
Pull Request #{{ data_get($deployment, 'pull_request_id') }}
|
||||||
@endif
|
@endif
|
||||||
@if (data_get($deployment, 'commit'))
|
@if (data_get($deployment, 'commit'))
|
||||||
<div class="dark:hover:text-white"
|
<div class="dark:hover:text-white" wire:navigate.prevent
|
||||||
wire:navigate.prevent href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}">
|
href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}">
|
||||||
<div class="text-xs underline">
|
<div class="text-xs underline">
|
||||||
@if ($deployment->commitMessage())
|
@if ($deployment->commitMessage())
|
||||||
({{ data_get_str($deployment, 'commit')->limit(7) }} -
|
({{ data_get_str($deployment, 'commit')->limit(7) }} -
|
||||||
@@ -83,8 +82,8 @@
|
|||||||
@endif
|
@endif
|
||||||
@endif
|
@endif
|
||||||
@if (data_get($deployment, 'commit'))
|
@if (data_get($deployment, 'commit'))
|
||||||
<div class="dark:hover:text-white"
|
<div class="dark:hover:text-white" wire:navigate.prevent
|
||||||
wire:navigate.prevent href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}">
|
href="{{ $application->gitCommitLink(data_get($deployment, 'commit')) }}">
|
||||||
<div class="text-xs underline">
|
<div class="text-xs underline">
|
||||||
@if ($deployment->commitMessage())
|
@if ($deployment->commitMessage())
|
||||||
({{ data_get_str($deployment, 'commit')->limit(7) }} -
|
({{ data_get_str($deployment, 'commit')->limit(7) }} -
|
||||||
@@ -104,13 +103,12 @@
|
|||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-col" x-data="elapsedTime('{{ $deployment->deployment_uuid }}', '{{ $deployment->status }}', '{{ $deployment->created_at }}', '{{ $deployment->updated_at }}')">
|
<div class="flex flex-col" x-data="elapsedTime('{{ $deployment->deployment_uuid }}', '{{ $deployment->status }}', '{{ $deployment->created_at }}', '{{ $deployment->finished_at }}')">
|
||||||
<div>
|
<div>
|
||||||
@if ($deployment->status !== 'in_progress')
|
@if ($deployment->status !== 'in_progress')
|
||||||
Finished <span x-text="measure_since_started()">0s</span> ago in
|
<span x-html="measurementText()" />
|
||||||
<span class="font-bold" x-text="measure_finished_time()">0s</span>
|
|
||||||
@else
|
@else
|
||||||
Running for <span class="font-bold" x-text="measure_since_started()">0s</span>
|
Running for <span class="font-bold" x-text="measureSinceStarted()">0s</span>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -121,16 +119,13 @@
|
|||||||
@endforelse
|
@endforelse
|
||||||
|
|
||||||
@if ($deployments_count > 0)
|
@if ($deployments_count > 0)
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/dayjs.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/utc.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/dayjs@1/plugin/relativeTime.js"></script>
|
|
||||||
<script>
|
<script>
|
||||||
let timers = {};
|
let timers = {};
|
||||||
|
|
||||||
dayjs.extend(window.dayjs_plugin_utc);
|
dayjs.extend(window.dayjs_plugin_utc);
|
||||||
dayjs.extend(window.dayjs_plugin_relativeTime);
|
dayjs.extend(window.dayjs_plugin_relativeTime);
|
||||||
|
|
||||||
Alpine.data('elapsedTime', (uuid, status, created_at, updated_at) => ({
|
Alpine.data('elapsedTime', (uuid, status, created_at, finished_at) => ({
|
||||||
finished_time: 'calculating...',
|
finished_time: 'calculating...',
|
||||||
started_time: 'calculating...',
|
started_time: 'calculating...',
|
||||||
init() {
|
init() {
|
||||||
@@ -143,20 +138,29 @@
|
|||||||
'second') + 's'
|
'second') + 's'
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
let seconds = dayjs.utc(updated_at).diff(dayjs.utc(created_at), 'second')
|
this.finished_time = dayjs.utc(finished_at).diff(dayjs.utc(created_at), 'second')
|
||||||
this.finished_time = seconds + 's';
|
if (isNaN(this.finished_time)) {
|
||||||
|
this.finished_time = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
measure_finished_time() {
|
measureFinishedTime() {
|
||||||
if (this.finished_time > 2000) {
|
if (this.finished_time > 2000) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return this.finished_time;
|
return this.finished_time;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
measure_since_started() {
|
measureSinceStarted() {
|
||||||
return dayjs.utc(created_at).fromNow(true); // "true" prevents the "ago" suffix
|
return dayjs.utc(created_at).fromNow(true); // "true" prevents the "ago" suffix
|
||||||
},
|
},
|
||||||
|
measurementText() {
|
||||||
|
if (this.measureFinishedTime() === 0) {
|
||||||
|
return 'Finished <span x-text="measureSinceStarted()"></span> ago';
|
||||||
|
} else {
|
||||||
|
return 'Finished <span x-text="measureSinceStarted()"></span> ago in <span class="font-bold" x-text="measureFinishedTime()"></span><span class="font-bold">s</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
</script>
|
</script>
|
||||||
@endif
|
@endif
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
</a>
|
</a>
|
||||||
@if (!$application->destination->server->isSwarm())
|
@if (!$application->destination->server->isSwarm())
|
||||||
<a class="{{ request()->routeIs('project.application.command') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.application.command') ? 'dark:text-white' : '' }}"
|
||||||
wire:navigate href="{{ route('project.application.command', $parameters) }}">
|
href="{{ route('project.application.command', $parameters) }}">
|
||||||
<button>Terminal</button>
|
<button>Terminal</button>
|
||||||
</a>
|
</a>
|
||||||
@endif
|
@endif
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
href="{{ route('project.database.logs', $parameters) }}">
|
href="{{ route('project.database.logs', $parameters) }}">
|
||||||
<button>Logs</button>
|
<button>Logs</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.database.command') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.database.command') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.database.command', $parameters) }}">
|
href="{{ route('project.database.command', $parameters) }}">
|
||||||
<button>Terminal</button>
|
<button>Terminal</button>
|
||||||
</a>
|
</a>
|
||||||
@@ -27,7 +27,8 @@
|
|||||||
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
$database->getMorphClass() === 'App\Models\StandaloneMongodb' ||
|
||||||
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
$database->getMorphClass() === 'App\Models\StandaloneMysql' ||
|
||||||
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
$database->getMorphClass() === 'App\Models\StandaloneMariadb')
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.database.backup.index') ? 'dark:text-white' : '' }}"
|
<a wire:navigate
|
||||||
|
class="{{ request()->routeIs('project.database.backup.index') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.database.backup.index', $parameters) }}">
|
href="{{ route('project.database.backup.index', $parameters) }}">
|
||||||
<button>Backups</button>
|
<button>Backups</button>
|
||||||
</a>
|
</a>
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
href="{{ route('project.service.logs', $parameters) }}">
|
href="{{ route('project.service.logs', $parameters) }}">
|
||||||
<button>Logs</button>
|
<button>Logs</button>
|
||||||
</a>
|
</a>
|
||||||
<a wire:navigate class="{{ request()->routeIs('project.service.command') ? 'dark:text-white' : '' }}"
|
<a class="{{ request()->routeIs('project.service.command') ? 'dark:text-white' : '' }}"
|
||||||
href="{{ route('project.service.command', $parameters) }}">
|
href="{{ route('project.service.command', $parameters) }}">
|
||||||
<button>Terminal</button>
|
<button>Terminal</button>
|
||||||
</a>
|
</a>
|
||||||
|
Reference in New Issue
Block a user