d2d.emegrendeles.hu/resources/views/filament/pages/deployment-package.blade.php
E98Developer bfc1484611 ADD Deployment csomagoló phase3 napló, környezetkövetés, ZIP letöltés és törlő szkript
A csomagolás mostantól nyomot hagy: melyik környezetre mi készült, mi lett kirakva, és
mennyi a lemaradás - ez az, ami a "melyik verzió fut az e2e-n" kérdést kiveszi a fejből.

- deployment_packages tábla + modell (BaseAuditable): target, tartomány, darabszámok,
  mappa/ZIP útvonal, deployed_at. A csomagolás és a tényleges kirakás két külön esemény.
- Targetenkénti előtöltés: a kezdő commit az adott környezetre utoljára kirakottként
  megjelölt csomag to_commit-je. Rebase után nem létező hash esetén inkább üres marad,
  mint hogy hamis tartományt mutasson.
- "Környezetek állapota" panel: napló szerinti állapot + commit-lemaradás
  (git rev-list --count), és gombra a szerver deploy-version.json-jának lekérdezése.
  Ha a kettő eltér, az elmaradt vagy félbemaradt feltöltés jele. Szándékosan gombra fut,
  nem minden rendereléskor: így nem indul kimenő kérés magától.
- ZIP a kész mappából (a fájllista a zip létrejötte előtt készül, így nem csomagolja
  magát), letöltés a naplóból. A DB-ből jövő útvonalat kiírás előtt a kimeneti mappához
  kötjük, hogy egy módosított rekord se tehessen letölthetővé tetszőleges fájlt.
- _torles.sh: alapból dry run, --confirm kell a törléshez, app-gyökér ellenőrzéssel,
  abszolút útvonal és .. kiszűrésével, LF sorvéggel és BOM nélkül. Lefuttatva ellenőrizve.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 07:20:34 +02:00

418 lines
25 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@php
// A fázis 1 felülete csak olvas: a git történetet listázza és a kijelölt tartomány
// diffjét mutatja. A számított property-ket egy helyen kérjük le, hogy a hibabanner
// (gitError) már a renderelés elején beállított állapotot lássa.
$commits = $this->commits;
$changeSet = $this->changeSet;
$highlighted = $this->highlightedHashes;
$target = $this->target();
$environments = $this->environmentStatuses;
$recentPackages = $this->recentPackages;
$statusStyles = [
'A' => ['label' => 'új', 'class' => 'bg-emerald-100 text-emerald-800 dark:bg-emerald-900 dark:text-emerald-200'],
'M' => ['label' => 'módosult', 'class' => 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200'],
'D' => ['label' => 'törölt', 'class' => 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'],
'R' => ['label' => 'átnevezett', 'class' => 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200'],
'C' => ['label' => 'másolt', 'class' => 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200'],
];
$warningStyles = [
'danger' => 'border-red-300 bg-red-50 text-red-900 dark:border-red-800 dark:bg-red-950 dark:text-red-200',
'warning' => 'border-amber-300 bg-amber-50 text-amber-900 dark:border-amber-800 dark:bg-amber-950 dark:text-amber-200',
'info' => 'border-gray-300 bg-gray-50 text-gray-800 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300',
];
$cardClass = 'p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700';
$buttonClass = 'px-2 py-1 text-xs font-semibold rounded border transition';
@endphp
<x-filament-panels::page>
<div class="space-y-6">
<div class="{{ $cardClass }}">
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">Deployment csomagoló</h5>
<p class="font-normal text-gray-700 dark:text-gray-400">
Válaszd ki a célkörnyezetet, majd két commitot: a köztük változott fájlok kerülnek majd a csomagba
(a kezdő commit kizárva, a záró beleértve).
</p>
<p class="mt-2 text-sm text-gray-500 dark:text-gray-400">
A csomag ide készül: <code>{{ config('deployment.output_path') }}</code>
</p>
</div>
<div class="{{ $cardClass }}">
<h6 class="mb-4 text-lg font-semibold text-gray-900 dark:text-white">Környezetek állapota</h6>
<div class="grid gap-4 md:grid-cols-2">
@foreach ($environments as $environment)
<div class="p-4 border rounded-lg {{ $environment['mismatch'] ? $warningStyles['danger'] : 'border-gray-200 dark:border-gray-700' }}">
<div class="flex items-start justify-between gap-3">
<div>
<p class="font-semibold text-gray-900 dark:text-white">{{ $environment['target']['label'] }}</p>
<p class="text-xs font-mono text-gray-500 dark:text-gray-400">{{ $environment['target']['remote_path'] }}</p>
</div>
<button type="button" wire:click="fetchLiveVersion('{{ $environment['target']['key'] }}')"
wire:loading.attr="disabled" wire:target="fetchLiveVersion('{{ $environment['target']['key'] }}')"
class="{{ $buttonClass }} border-gray-300 text-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-gray-700">
Élő állapot
</button>
</div>
<div class="mt-3 space-y-1 text-sm text-gray-600 dark:text-gray-300">
@if ($environment['package'])
<p>
Utoljára kirakva:
<code>{{ substr($environment['package']->to_commit, 0, 7) }}</code>
· {{ $environment['package']->deployed_at?->format('Y.m.d H:i') }}
</p>
@if ($environment['behind'] !== null)
<p>
Lemaradás a kiválasztott branchhez képest:
<strong>{{ $environment['behind'] }}</strong> commit
</p>
@endif
@else
<p class="text-gray-400">Még nincs kirakottként megjelölt csomag.</p>
@endif
@if ($environment['live'])
@if ($environment['live']['ok'])
<p>
A szerver szerint:
<code>{{ substr($environment['live']['data']['commit'] ?? '?', 0, 7) }}</code>
@if (($environment['live']['data']['packaged_at'] ?? null))
· {{ \Illuminate\Support\Carbon::parse($environment['live']['data']['packaged_at'])->format('Y.m.d H:i') }}
@endif
</p>
@else
<p class="text-gray-400">Élő állapot nem lekérdezhető: {{ $environment['live']['error'] }}</p>
@endif
@endif
@if ($environment['mismatch'])
<p class="font-semibold">
A szerveren futó commit eltér a naplózottól elmaradt vagy félbemaradt feltöltés.
</p>
@endif
</div>
</div>
@endforeach
</div>
</div>
@if ($this->lastPackage)
<div class="p-4 border rounded-lg {{ $this->lastPackage['counts']['missing'] > 0 ? $warningStyles['warning'] : 'border-emerald-300 bg-emerald-50 text-emerald-900 dark:border-emerald-800 dark:bg-emerald-950 dark:text-emerald-200' }}">
<p class="font-semibold">Elkészült: {{ $this->lastPackage['name'] }}</p>
<p class="mt-1 text-sm">
{{ $this->lastPackage['counts']['copied'] }} fájl másolva,
{{ $this->lastPackage['counts']['deleted'] }} törlendő,
{{ $this->lastPackage['counts']['skipped'] }} kézzel kivéve.
</p>
<p class="mt-2 text-xs font-mono break-all">{{ $this->lastPackage['path'] }}</p>
@if ($this->lastPackage['counts']['missing'] > 0)
<p class="mt-2 text-sm font-semibold">
Figyelem: {{ $this->lastPackage['counts']['missing'] }} fájl nem került a csomagba,
a listájuk a _MANIFEST.txt végén van.
</p>
@endif
<p class="mt-2 text-sm">
A lépéseket a csomagban lévő <code>_TEENDOK.md</code> tartalmazza.
</p>
</div>
@endif
@if ($this->gitError)
<div class="p-4 border rounded-lg {{ $warningStyles['danger'] }}">
<p class="font-semibold">Git hiba</p>
<p class="mt-1 text-sm">{{ $this->gitError }}</p>
</div>
@elseif (! $this->isRepositoryAvailable())
<div class="p-4 border rounded-lg {{ $warningStyles['danger'] }}">
<p class="font-semibold">Nem érhető el git repó</p>
<p class="mt-1 text-sm">
A(z) <code>{{ config('deployment.repo_path') }}</code> útvonalon nincs git working copy,
vagy a git parancs nem található.
</p>
</div>
@endif
<div class="{{ $cardClass }}">
{{ $this->form }}
</div>
<div class="{{ $cardClass }}">
<div class="flex flex-wrap items-center justify-between gap-4">
<div class="flex flex-wrap items-center gap-3 text-sm">
<span class="text-gray-500 dark:text-gray-400">Tartomány:</span>
@if ($this->fromCommit)
<code class="px-2 py-1 rounded bg-gray-100 dark:bg-gray-900">{{ substr($this->fromCommit, 0, 7) }}</code>
@else
<span class="text-gray-400">nincs kezdő commit</span>
@endif
<span class="text-gray-400"></span>
@if ($this->toCommit)
<code class="px-2 py-1 rounded bg-gray-100 dark:bg-gray-900">{{ substr($this->toCommit, 0, 7) }}</code>
@else
<span class="text-gray-400">nincs záró commit</span>
@endif
@if ($changeSet)
<span class="text-gray-500 dark:text-gray-400">
· {{ $changeSet['counts']['copied'] }} másolandó
@if ($changeSet['counts']['deleted'] > 0)
, {{ $changeSet['counts']['deleted'] }} törlendő
@endif
</span>
@endif
</div>
<div class="flex items-center gap-2">
<button type="button" wire:click="swapRange"
class="{{ $buttonClass }} border-gray-300 text-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-gray-700">
Csere
</button>
<button type="button" wire:click="clearRange"
class="{{ $buttonClass }} border-gray-300 text-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-gray-700">
Törlés
</button>
</div>
</div>
@if ($target)
<p class="mt-3 text-sm text-gray-500 dark:text-gray-400">
Cél: <strong>{{ $target['label'] }}</strong> a fájlok ide kerülnek majd feltöltésre:
<code>{{ $target['remote_path'] }}</code>
</p>
@endif
</div>
<div class="{{ $cardClass }}">
<h6 class="mb-4 text-lg font-semibold text-gray-900 dark:text-white">
Commitok
<span class="ml-1 text-sm font-normal text-gray-500 dark:text-gray-400">({{ count($commits) }} db)</span>
</h6>
@if ($commits === [])
<p class="text-sm text-gray-500 dark:text-gray-400">Nincs megjeleníthető commit.</p>
@else
<div class="overflow-x-auto">
<table class="w-full text-sm text-left">
<thead class="text-xs uppercase text-gray-600 border-b border-gray-200 dark:text-gray-300 dark:border-gray-700">
<tr>
<th class="py-2 pr-4">Kijelölés</th>
<th class="py-2 pr-4">Hash</th>
<th class="py-2 pr-4">Dátum</th>
<th class="py-2 pr-4">Szerző</th>
<th class="py-2 pr-4">Üzenet</th>
<th class="py-2 text-right">Fájl</th>
</tr>
</thead>
<tbody>
@foreach ($commits as $commit)
@php
$isFrom = $this->fromCommit === $commit['hash'];
$isTo = $this->toCommit === $commit['hash'];
$inRange = in_array($commit['hash'], $highlighted, true);
@endphp
<tr wire:key="commit-{{ $commit['hash'] }}"
class="border-b border-gray-100 dark:border-gray-700 {{ $inRange ? 'bg-primary-50 dark:bg-gray-900' : '' }}">
<td class="py-2 pr-4 whitespace-nowrap">
<button type="button" wire:click="selectFrom('{{ $commit['hash'] }}')"
class="{{ $buttonClass }} {{ $isFrom ? 'border-primary-600 bg-primary-600 text-white' : 'border-gray-300 text-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-gray-700' }}">
Kezdő
</button>
<button type="button" wire:click="selectTo('{{ $commit['hash'] }}')"
class="{{ $buttonClass }} ml-1 {{ $isTo ? 'border-primary-600 bg-primary-600 text-white' : 'border-gray-300 text-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-gray-700' }}">
Záró
</button>
</td>
<td class="py-2 pr-4 font-mono text-xs whitespace-nowrap">{{ $commit['short'] }}</td>
<td class="py-2 pr-4 whitespace-nowrap text-gray-600 dark:text-gray-300">
{{ \Illuminate\Support\Carbon::parse($commit['date'])->format('Y.m.d H:i') }}
</td>
<td class="py-2 pr-4 whitespace-nowrap text-gray-600 dark:text-gray-300">{{ $commit['author'] }}</td>
<td class="py-2 pr-4 text-gray-800 dark:text-gray-100">{{ $commit['subject'] }}</td>
<td class="py-2 text-right text-gray-500 dark:text-gray-400">{{ $commit['file_count'] ?? '' }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
@if ($changeSet)
<div class="{{ $cardClass }}">
<h6 class="mb-4 text-lg font-semibold text-gray-900 dark:text-white">
Változott fájlok
<span class="ml-1 text-sm font-normal text-gray-500 dark:text-gray-400">
({{ $changeSet['counts']['total'] }} db)
</span>
</h6>
@foreach ($changeSet['warnings'] as $warning)
<div class="p-4 mb-3 border rounded-lg {{ $warningStyles[$warning['level']] ?? $warningStyles['info'] }}">
<p class="font-semibold">{{ $warning['title'] }}</p>
<p class="mt-1 text-sm">{{ $warning['body'] }}</p>
</div>
@endforeach
@if ($changeSet['files'] !== [])
<div class="overflow-x-auto">
<table class="w-full text-sm text-left">
<thead class="text-xs uppercase text-gray-600 border-b border-gray-200 dark:text-gray-300 dark:border-gray-700">
<tr>
<th class="py-2 pr-4">Csomagba</th>
<th class="py-2 pr-4">Állapot</th>
<th class="py-2 pr-4">Útvonal</th>
<th class="py-2">Művelet</th>
</tr>
</thead>
<tbody>
@foreach ($changeSet['files'] as $file)
@php
$style = $statusStyles[$file['status']] ?? ['label' => $file['status'], 'class' => 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-200'];
$isSkipped = in_array($file['path'], $this->skippedPaths, true);
@endphp
<tr wire:key="file-{{ $loop->index }}" class="border-b border-gray-100 dark:border-gray-700 {{ $file['excluded'] || $isSkipped ? 'opacity-50' : '' }}">
<td class="py-2 pr-4 whitespace-nowrap">
@if ($file['action'] === 'copy')
<input type="checkbox"
wire:click="toggleFile(@js($file['path']))"
@checked(! $isSkipped)
class="rounded border-gray-300 dark:border-gray-600">
@else
<span class="text-gray-300 dark:text-gray-600"></span>
@endif
</td>
<td class="py-2 pr-4 whitespace-nowrap">
<span class="px-2 py-0.5 text-xs font-semibold rounded {{ $style['class'] }}">
{{ $file['status'] }} · {{ $style['label'] }}
</span>
</td>
<td class="py-2 pr-4 font-mono text-xs break-all text-gray-800 dark:text-gray-100">
{{ $file['path'] }}
@if ($file['old_path'])
<span class="block text-gray-400"> {{ $file['old_path'] }}</span>
@endif
</td>
<td class="py-2 whitespace-nowrap text-gray-600 dark:text-gray-300">
@switch($file['action'])
@case('copy') {{ $isSkipped ? 'kézzel kivéve' : 'másolandó' }} @break
@case('delete') törlendő a célgépen @break
@default kihagyva (kizárási lista)
@endswitch
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
@php
$packageCount = count(array_diff($changeSet['copied'], $this->skippedPaths));
@endphp
<div class="flex flex-wrap items-center justify-between gap-4 pt-4 mt-4 border-t border-gray-200 dark:border-gray-700">
<p class="text-sm text-gray-600 dark:text-gray-300">
A csomagba <strong>{{ $packageCount }}</strong> fájl kerül
@if ($changeSet['counts']['deleted'] > 0)
, és {{ $changeSet['counts']['deleted'] }} fájl a törlendők listájára
@endif
@if ($target)
· cél: <strong>{{ $target['label'] }}</strong>
@endif
</p>
<button type="button"
wire:click="createPackage"
wire:confirm="Elkészítjük a csomagot: {{ $packageCount }} fájl{{ $target ? ' a(z) '.$target['label'].' környezethez' : '' }}. Folytatod?"
wire:loading.attr="disabled"
@disabled($packageCount === 0 && $changeSet['counts']['deleted'] === 0)
class="px-4 py-2 text-sm font-semibold text-white rounded bg-primary-600 hover:bg-primary-500 disabled:opacity-50 disabled:cursor-not-allowed">
<span wire:loading.remove wire:target="createPackage">Csomag készítése</span>
<span wire:loading wire:target="createPackage">Csomagolás…</span>
</button>
</div>
</div>
@endif
@if ($recentPackages->isNotEmpty())
<div class="{{ $cardClass }}">
<h6 class="mb-4 text-lg font-semibold text-gray-900 dark:text-white">
Korábbi csomagok
<span class="ml-1 text-sm font-normal text-gray-500 dark:text-gray-400">
({{ $target ? $target['label'] : 'összes' }})
</span>
</h6>
<div class="overflow-x-auto">
<table class="w-full text-sm text-left">
<thead class="text-xs uppercase text-gray-600 border-b border-gray-200 dark:text-gray-300 dark:border-gray-700">
<tr>
<th class="py-2 pr-4">Csomag</th>
<th class="py-2 pr-4">Tartomány</th>
<th class="py-2 pr-4">Fájlok</th>
<th class="py-2 pr-4">Készült</th>
<th class="py-2 pr-4">Kirakva</th>
<th class="py-2 text-right">Műveletek</th>
</tr>
</thead>
<tbody>
@foreach ($recentPackages as $package)
<tr wire:key="package-{{ $package->id }}" class="border-b border-gray-100 dark:border-gray-700">
<td class="py-2 pr-4 font-mono text-xs break-all text-gray-800 dark:text-gray-100">{{ $package->name }}</td>
<td class="py-2 pr-4 font-mono text-xs whitespace-nowrap text-gray-600 dark:text-gray-300">
{{ substr($package->from_commit, 0, 7) }} {{ substr($package->to_commit, 0, 7) }}
</td>
<td class="py-2 pr-4 whitespace-nowrap text-gray-600 dark:text-gray-300">
{{ $package->copied_count }}
@if ($package->deleted_count > 0)
<span class="text-gray-400">/ {{ $package->deleted_count }} törl.</span>
@endif
</td>
<td class="py-2 pr-4 whitespace-nowrap text-gray-600 dark:text-gray-300">
{{ $package->created_at?->format('Y.m.d H:i') }}
</td>
<td class="py-2 pr-4 whitespace-nowrap">
@if ($package->isDeployed())
<span class="px-2 py-0.5 text-xs font-semibold rounded bg-emerald-100 text-emerald-800 dark:bg-emerald-900 dark:text-emerald-200">
{{ $package->deployed_at?->format('Y.m.d H:i') }}
</span>
@else
<span class="text-gray-400"></span>
@endif
</td>
<td class="py-2 text-right whitespace-nowrap">
@if ($package->zip_path)
<button type="button" wire:click="downloadPackage({{ $package->id }})"
class="{{ $buttonClass }} border-gray-300 text-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-gray-700">
ZIP
</button>
@endif
@unless ($package->isDeployed())
<button type="button" wire:click="markDeployed({{ $package->id }})"
wire:confirm="Megjelöljük kirakottként? Innentől ez lesz a következő csomag kezdő commitja ezen a környezeten."
class="{{ $buttonClass }} ml-1 border-gray-300 text-gray-700 hover:bg-gray-100 dark:border-gray-600 dark:text-gray-200 dark:hover:bg-gray-700">
Kirakva
</button>
@endunless
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
</div>
</x-filament-panels::page>