From bfc1484611d3620edee2abfc712dc955838cd891 Mon Sep 17 00:00:00 2001 From: E98Developer Date: Sun, 16 Aug 2026 07:20:34 +0200 Subject: [PATCH] =?UTF-8?q?ADD=20Deployment=20csomagol=C3=B3=20phase3=20na?= =?UTF-8?q?pl=C3=B3,=20k=C3=B6rnyezetk=C3=B6vet=C3=A9s,=20ZIP=20let=C3=B6l?= =?UTF-8?q?t=C3=A9s=20=C3=A9s=20t=C3=B6rl=C5=91=20szkript?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- app/Filament/Pages/DeploymentPackage.php | 181 +++++++++++++++++- app/Models/DeploymentPackage.php | 48 +++++ .../Deployment/DeploymentPackageBuilder.php | 94 ++++++++- app/Services/Deployment/GitRepository.php | 23 +++ ...90000_create_deployment_packages_table.php | 46 +++++ .../pages/deployment-package.blade.php | 131 +++++++++++++ tests/Feature/DeploymentPackageTest.php | 99 ++++++++++ 7 files changed, 619 insertions(+), 3 deletions(-) create mode 100644 app/Models/DeploymentPackage.php create mode 100644 database/migrations/2026_08_16_090000_create_deployment_packages_table.php diff --git a/app/Filament/Pages/DeploymentPackage.php b/app/Filament/Pages/DeploymentPackage.php index a9ddf7b..ed10f0f 100644 --- a/app/Filament/Pages/DeploymentPackage.php +++ b/app/Filament/Pages/DeploymentPackage.php @@ -2,6 +2,7 @@ namespace App\Filament\Pages; +use App\Models\DeploymentPackage as DeploymentPackageRecord; use App\Services\Deployment\ChangeSetAnalyzer; use App\Services\Deployment\DeploymentGuard; use App\Services\Deployment\DeploymentPackageBuilder; @@ -15,8 +16,12 @@ use Filament\Pages\Page; use Filament\Schemas\Components\Grid; use Filament\Schemas\Schema; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Http; use Livewire\Attributes\Computed; use RuntimeException; +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Throwable; /** * Deployment csomagoló: célkörnyezet, commit-tartomány, diff-előnézet és csomagolás. @@ -57,6 +62,9 @@ class DeploymentPackage extends Page implements HasForms /** @var array|null az utoljára elkészített csomag adatai */ public ?array $lastPackage = null; + /** @var array> targetenként a szerverről lekérdezett élő verzió */ + public array $liveVersions = []; + public static function canAccess(): bool { return app(DeploymentGuard::class)->isAllowed(auth()->user()); @@ -69,12 +77,37 @@ public function mount(): void /** @var array $limits */ $limits = (array) config('deployment.commit_limits', [50]); + $target = app(DeploymentTargets::class)->defaultKey(); + $this->form->fill([ - 'target' => app(DeploymentTargets::class)->defaultKey(), + 'target' => $target, 'branch' => $this->defaultBranch(), 'limit' => $limits[0] ?? 50, 'search' => null, ]); + + $this->preloadFromCommit($target); + } + + /** + * A kezdő commit előtöltése az adott környezetre utoljára kirakott csomagból. + * + * Ez a funkció lényegi része: a "hol tart ez a környezet" kérdésre nem emlékezetből + * kell válaszolni, így nem marad ki fájl a következő csomagból. + */ + private function preloadFromCommit(?string $targetKey): void + { + if (! $targetKey) { + return; + } + + $lastDeployed = DeploymentPackageRecord::lastDeployedFor($targetKey); + + // Rebase/force push után a korábban rögzített hash már nem létezik - ilyenkor + // inkább nem töltünk elő semmit, mint hogy hamis tartományt mutassunk. + $this->fromCommit = $lastDeployed + ? app(GitRepository::class)->resolveCommit($lastDeployed->to_commit) + : null; } public function form(Schema $form): Schema @@ -91,6 +124,7 @@ public function form(Schema $form): Schema ->options(app(DeploymentTargets::class)->options()) ->required() ->live() + ->afterStateUpdated(fn (?string $state) => $this->preloadFromCommit($state)) ->helperText(fn (): ?string => $this->target()['remote_path'] ?? null), Select::make('branch') ->label('Branch') @@ -191,6 +225,20 @@ public function createPackage(): void return; } + DeploymentPackageRecord::create([ + 'name' => $this->lastPackage['name'], + 'target' => $this->lastPackage['target'], + 'branch' => $this->lastPackage['branch'], + 'from_commit' => $this->lastPackage['from'], + 'to_commit' => $this->lastPackage['to'], + 'folder' => $this->lastPackage['path'], + 'zip_path' => $this->lastPackage['zip'], + 'copied_count' => $this->lastPackage['counts']['copied'], + 'deleted_count' => $this->lastPackage['counts']['deleted'], + 'skipped_count' => $this->lastPackage['counts']['skipped'], + 'missing_count' => $this->lastPackage['counts']['missing'], + ]); + Notification::make() ->title('A csomag elkészült') ->body(sprintf( @@ -202,6 +250,137 @@ public function createPackage(): void ->send(); } + /** + * A csomag ZIP-jének letöltése. + */ + public function downloadPackage(int $id): ?BinaryFileResponse + { + app(DeploymentGuard::class)->ensureAllowed(auth()->user()); + + $package = DeploymentPackageRecord::find($id); + $zipPath = $package?->zip_path; + + // Az útvonal adatbázisból jön, ezért kiírás előtt ellenőrizzük, hogy a csomagoló + // saját kimeneti mappáján belül van - egy elrontott vagy módosított rekord így sem + // tud tetszőleges fájlt letölthetővé tenni. + if (! $zipPath || ! is_file($zipPath) || ! $this->isInsideOutputPath($zipPath)) { + Notification::make() + ->title('A ZIP nem érhető el') + ->body('A csomag ZIP fájlja már nincs a lemezen.') + ->danger() + ->send(); + + return null; + } + + return response()->download($zipPath); + } + + /** + * A csomag megjelölése kirakottként - innentől ez lesz a következő tartomány kezdete. + */ + public function markDeployed(int $id): void + { + app(DeploymentGuard::class)->ensureAllowed(auth()->user()); + + $package = DeploymentPackageRecord::find($id); + + if (! $package) { + return; + } + + $package->deployed_at = now(); + $package->save(); + + $this->preloadFromCommit($package->target); + + Notification::make() + ->title('Megjelölve kirakottként') + ->body(sprintf('A(z) %s környezet innentől a %s commiton áll.', $package->target, substr((string) $package->to_commit, 0, 7))) + ->success() + ->send(); + } + + /** + * Az élő állapot lekérdezése a célkörnyezetről (deploy-version.json). + * + * Szándékosan gombra fut és nem minden rendereléskor: így nem indul kimenő kérés + * magától, és nem lassítja az oldalt, ha egy környezet nem válaszol. + */ + public function fetchLiveVersion(string $targetKey): void + { + app(DeploymentGuard::class)->ensureAllowed(auth()->user()); + + $target = app(DeploymentTargets::class)->find($targetKey); + + if (! $target || ! $target['domain']) { + return; + } + + try { + $response = Http::connectTimeout(3)->timeout(5)->get('https://'.$target['domain'].'/deploy-version.json'); + + $this->liveVersions[$targetKey] = $response->successful() + ? ['ok' => true, 'data' => $response->json()] + : ['ok' => false, 'error' => 'HTTP '.$response->status()]; + } catch (Throwable $exception) { + $this->liveVersions[$targetKey] = ['ok' => false, 'error' => $exception->getMessage()]; + } + } + + /** + * Környezetenkénti állapot: mi van kirakva és mennyi a lemaradás. + * + * @return array> + */ + #[Computed] + public function environmentStatuses(): array + { + $branch = $this->data['branch'] ?? null; + $statuses = []; + + foreach (app(DeploymentTargets::class)->all() as $key => $target) { + $lastDeployed = DeploymentPackageRecord::lastDeployedFor($key); + $live = $this->liveVersions[$key] ?? null; + $liveCommit = $live['ok'] ?? false ? ($live['data']['commit'] ?? null) : null; + + $statuses[] = [ + 'target' => $target, + 'package' => $lastDeployed, + 'behind' => $lastDeployed && $branch + ? app(GitRepository::class)->countCommitsBetween($lastDeployed->to_commit, $branch) + : null, + 'live' => $live, + // A napló azt mondja meg, mit jelöltünk kirakottnak; a szerver azt, mi fut + // valójában. Ha a kettő eltér, az elmaradt vagy félbemaradt feltöltés jele. + 'mismatch' => $liveCommit && $lastDeployed && $liveCommit !== $lastDeployed->to_commit, + ]; + } + + return $statuses; + } + + /** + * @return \Illuminate\Support\Collection + */ + #[Computed] + public function recentPackages(): Collection + { + return DeploymentPackageRecord::query() + ->when($this->data['target'] ?? null, fn ($query, $target) => $query->forTarget($target)) + ->latest('id') + ->limit(10) + ->get(); + } + + private function isInsideOutputPath(string $path): bool + { + $base = realpath((string) config('deployment.output_path')); + $real = realpath($path); + + return $base !== false && $real !== false && str_starts_with($real, $base); + } + /** * @return array{key:string, label:string, domain:string, remote_path:string, expected_branch:?string}|null */ diff --git a/app/Models/DeploymentPackage.php b/app/Models/DeploymentPackage.php new file mode 100644 index 0000000..c5e27d6 --- /dev/null +++ b/app/Models/DeploymentPackage.php @@ -0,0 +1,48 @@ + 'datetime', + 'copied_count' => 'integer', + 'deleted_count' => 'integer', + 'skipped_count' => 'integer', + 'missing_count' => 'integer', + ]; + + public function scopeForTarget(Builder $query, string $target): Builder + { + return $query->where('target', $target); + } + + /** + * Az adott környezetre utoljára kirakottként megjelölt csomag. + */ + public static function lastDeployedFor(string $target): ?self + { + return static::query() + ->forTarget($target) + ->whereNotNull('deployed_at') + ->latest('deployed_at') + ->first(); + } + + public function isDeployed(): bool + { + return $this->deployed_at !== null; + } +} diff --git a/app/Services/Deployment/DeploymentPackageBuilder.php b/app/Services/Deployment/DeploymentPackageBuilder.php index b49b54d..db2f0df 100644 --- a/app/Services/Deployment/DeploymentPackageBuilder.php +++ b/app/Services/Deployment/DeploymentPackageBuilder.php @@ -5,6 +5,7 @@ use Illuminate\Filesystem\Filesystem; use Illuminate\Support\Carbon; use RuntimeException; +use ZipArchive; /** * A kijelölt commit-tartományból állítja elő a feltölthető csomagot. @@ -29,7 +30,7 @@ public function __construct( /** * @param array $skippedPaths a felületen kézzel kivett fájlok * @return array{ - * name:string, path:string, target:string, branch:string, + * name:string, path:string, zip:?string, target:string, branch:string, * from:string, to:string, created_at:string, created_by:string, * counts:array{copied:int, deleted:int, excluded:int, skipped:int, missing:int}, * files:array, @@ -122,8 +123,13 @@ public function build( $this->writeVersionMarker($path, $manifest, $target); $this->writeManifest($path, $manifest, $target); $this->writeDeleteList($path, $manifest, $target); + $this->writeDeleteScript($path, $manifest, $target); $this->writeInstructions($path, $manifest, $target); + // A ZIP készül utoljára, hogy a kísérő fájlokat is tartalmazza. A fájllistát + // előbb gyűjtjük össze, mint ahogy a zip létrejön, így nem tudja magát becsomagolni. + $manifest['zip'] = $this->createZip($path, $manifest['name']); + return $manifest; } @@ -292,7 +298,11 @@ private function writeInstructions(string $path, array $manifest, array $target) ]; if ($manifest['deleted'] !== []) { - $steps[] = sprintf('Töröld a `_TORLENDO.txt`-ben felsorolt %d fájlt a célgépen.', $manifest['counts']['deleted']); + $steps[] = sprintf( + 'Töröld a `_TORLENDO.txt`-ben felsorolt %d fájlt a célgépen. Nagyobb mennyiségnél a mellékelt ' + .'szkript is használható **átnézés után**: `bash _torles.sh` kilistázza, `bash _torles.sh --confirm` törli.', + $manifest['counts']['deleted'], + ); } foreach ($manifest['warnings'] as $warning) { @@ -338,6 +348,86 @@ private function writeInstructions(string $path, array $manifest, array $target) $this->put($path.'/_TEENDOK.md', implode("\n", $lines)."\n"); } + /** + * Törlő szkript nagyobb mennyiséghez - a kézi törlés (_TORLENDO.txt) marad az alapeset. + * + * Ezért alapból csak kilistáz, a tényleges törléshez explicit --confirm kell; és mivel + * Windowson generáljuk Linux célgépre, LF sorvéggel és BOM nélkül kell kiírni, különben + * a shebang sor törik el. + * + * @param array $manifest + * @param array $target + */ + private function writeDeleteScript(string $path, array $manifest, array $target): void + { + $lines = [ + '#!/usr/bin/env bash', + '# '.$manifest['name'].' - a célgépen törlendő fájlok ('.$target['label'].')', + '# Futtatás az app gyökeréből:', + '# bash _torles.sh -> csak kilistázza (dry run)', + '# bash _torles.sh --confirm -> ténylegesen töröl', + 'set -euo pipefail', + '', + '[[ -f artisan && -d app ]] || { echo "HIBA: nem az app gyökerében futsz"; exit 1; }', + '', + 'CONFIRM=0', + '[[ "${1:-}" == "--confirm" ]] && CONFIRM=1', + '', + 'FILES=(', + ]; + + foreach ($manifest['deleted'] as $deleted) { + $lines[] = ' "'.$deleted.'"'; + } + + $lines = array_merge($lines, [ + ')', + '', + 'if (( ${#FILES[@]} == 0 )); then echo "Nincs törlendő fájl."; exit 0; fi', + '', + 'for f in "${FILES[@]}"; do', + ' case "$f" in /*|*..*) echo "KIHAGYVA (gyanús útvonal): $f"; continue ;; esac', + ' [[ -f "$f" ]] || { echo "NINCS MEG: $f"; continue; }', + ' if (( CONFIRM )); then rm -f -- "$f"; echo "TÖRÖLVE: $f"; else echo "TÖRÖLNÉ: $f"; fi', + 'done', + '', + 'if (( ! CONFIRM )); then echo; echo "Ez csak lista volt. Tényleges törlés: bash _torles.sh --confirm"; fi', + ]); + + $this->put($path.'/_torles.sh', implode("\n", $lines)."\n"); + } + + /** + * A kész mappa ZIP-be csomagolása letöltéshez és archiváláshoz. + */ + private function createZip(string $path, string $name): ?string + { + $entries = []; + + foreach ($this->files->allFiles($path, true) as $file) { + $entries[str_replace('\\', '/', $file->getRelativePathname())] = $file->getPathname(); + } + + if ($entries === []) { + return null; + } + + $zipPath = $path.DIRECTORY_SEPARATOR.$name.'.zip'; + $zip = new ZipArchive; + + if ($zip->open($zipPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { + throw new RuntimeException('A ZIP fájl nem hozható létre.'); + } + + foreach ($entries as $relative => $absolute) { + $zip->addFile($absolute, $relative); + } + + $zip->close(); + + return $zipPath; + } + /** * Mindig LF sorvéggel írunk: a csomag Windowson készül, de Linux célgépre megy. */ diff --git a/app/Services/Deployment/GitRepository.php b/app/Services/Deployment/GitRepository.php index 1fa9e10..45d1614 100644 --- a/app/Services/Deployment/GitRepository.php +++ b/app/Services/Deployment/GitRepository.php @@ -228,6 +228,29 @@ public function changedFiles(string $from, string $to): array return $files; } + /** + * Hány commit van a kettő között (from kizárva, to beleértve). + * + * A környezetek lemaradásának kimutatásához: "az e2e 7 committal van elmaradva". + */ + public function countCommitsBetween(string $from, string $to): ?int + { + $fromHash = $this->resolveCommit($from); + + if (! $fromHash) { + return null; + } + + try { + $this->assertReference($to); + $count = trim($this->run(['rev-list', '--count', $fromHash.'..'.$to, '--'])); + } catch (RuntimeException) { + return null; + } + + return is_numeric($count) ? (int) $count : null; + } + /** * A megadott commit szerinti fájltartalmat írja ki a célmappába, relatív útvonalakat megőrizve. * diff --git a/database/migrations/2026_08_16_090000_create_deployment_packages_table.php b/database/migrations/2026_08_16_090000_create_deployment_packages_table.php new file mode 100644 index 0000000..9c07673 --- /dev/null +++ b/database/migrations/2026_08_16_090000_create_deployment_packages_table.php @@ -0,0 +1,46 @@ +id(); + $table->string('name'); + $table->string('target', 32); + $table->string('branch')->nullable(); + $table->string('from_commit', 40); + $table->string('to_commit', 40); + $table->string('folder', 1024); + $table->string('zip_path', 1024)->nullable(); + $table->unsignedInteger('copied_count')->default(0); + $table->unsignedInteger('deleted_count')->default(0); + $table->unsignedInteger('skipped_count')->default(0); + $table->unsignedInteger('missing_count')->default(0); + $table->text('note')->nullable(); + // A csomagolás és a tényleges kirakás két külön esemény: a deployed_at csak + // akkor kap értéket, amikor a fejlesztő megjelöli, hogy fel is töltötte. + $table->timestamp('deployed_at')->nullable(); + $table->userIdFields(); + $table->timestamps(); + $table->softDeletes(); + + $table->index(['target', 'deployed_at']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('deployment_packages'); + } +}; diff --git a/resources/views/filament/pages/deployment-package.blade.php b/resources/views/filament/pages/deployment-package.blade.php index 44d84c1..6b654f9 100644 --- a/resources/views/filament/pages/deployment-package.blade.php +++ b/resources/views/filament/pages/deployment-package.blade.php @@ -6,6 +6,8 @@ $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'], @@ -39,6 +41,66 @@

+
+
Környezetek állapota
+ +
+ @foreach ($environments as $environment) +
+
+
+

{{ $environment['target']['label'] }}

+

{{ $environment['target']['remote_path'] }}

+
+ +
+ +
+ @if ($environment['package']) +

+ Utoljára kirakva: + {{ substr($environment['package']->to_commit, 0, 7) }} + · {{ $environment['package']->deployed_at?->format('Y.m.d H:i') }} +

+ @if ($environment['behind'] !== null) +

+ Lemaradás a kiválasztott branchhez képest: + {{ $environment['behind'] }} commit +

+ @endif + @else +

Még nincs kirakottként megjelölt csomag.

+ @endif + + @if ($environment['live']) + @if ($environment['live']['ok']) +

+ A szerver szerint: + {{ substr($environment['live']['data']['commit'] ?? '?', 0, 7) }} + @if (($environment['live']['data']['packaged_at'] ?? null)) + · {{ \Illuminate\Support\Carbon::parse($environment['live']['data']['packaged_at'])->format('Y.m.d H:i') }} + @endif +

+ @else +

Élő állapot nem lekérdezhető: {{ $environment['live']['error'] }}

+ @endif + @endif + + @if ($environment['mismatch']) +

+ A szerveren futó commit eltér a naplózottól — elmaradt vagy félbemaradt feltöltés. +

+ @endif +
+
+ @endforeach +
+
+ @if ($this->lastPackage)

Elkészült: {{ $this->lastPackage['name'] }}

@@ -282,5 +344,74 @@ class="px-4 py-2 text-sm font-semibold text-white rounded bg-primary-600 hover:b
@endif + @if ($recentPackages->isNotEmpty()) +
+
+ Korábbi csomagok + + ({{ $target ? $target['label'] : 'összes' }}) + +
+ +
+ + + + + + + + + + + + + @foreach ($recentPackages as $package) + + + + + + + + + @endforeach + +
CsomagTartományFájlokKészültKirakvaMűveletek
{{ $package->name }} + {{ substr($package->from_commit, 0, 7) }} → {{ substr($package->to_commit, 0, 7) }} + + {{ $package->copied_count }} + @if ($package->deleted_count > 0) + / {{ $package->deleted_count }} törl. + @endif + + {{ $package->created_at?->format('Y.m.d H:i') }} + + @if ($package->isDeployed()) + + {{ $package->deployed_at?->format('Y.m.d H:i') }} + + @else + + @endif + + @if ($package->zip_path) + + @endif + @unless ($package->isDeployed()) + + @endunless +
+
+
+ @endif + diff --git a/tests/Feature/DeploymentPackageTest.php b/tests/Feature/DeploymentPackageTest.php index 42a040c..7fe09c7 100644 --- a/tests/Feature/DeploymentPackageTest.php +++ b/tests/Feature/DeploymentPackageTest.php @@ -1,6 +1,7 @@ actingAs(deploymentDeveloper()); + + Livewire::test(DeploymentPackage::class) + ->call('selectFrom', $first) + ->call('selectTo', $third) + ->call('createPackage'); + + $record = DeploymentPackageRecord::query()->latest('id')->first(); + + expect($record)->not->toBeNull() + ->and($record->target)->toBe('e2e') + ->and($record->to_commit)->toBe($third) + ->and($record->deployed_at)->toBeNull() + ->and(File::exists($record->zip_path))->toBeTrue(); + + Livewire::test(DeploymentPackage::class) + ->call('downloadPackage', $record->id) + ->assertFileDownloaded(); + + deploymentCleanup($repository['path']); +}); + +test('a kirakottként megjelölt csomag adja a következő tartomány kezdetét', function () { + deploymentAllowEnvironment(); + $repository = deploymentTestRepository(); + [$first, $second, $third] = $repository['commits']; + + $this->actingAs(deploymentDeveloper()); + + $record = DeploymentPackageRecord::create([ + 'name' => 'deploy_e2e_teszt', + 'target' => 'e2e', + 'branch' => 'main', + 'from_commit' => $first, + 'to_commit' => $second, + 'folder' => $repository['path'], + ]); + + Livewire::test(DeploymentPackage::class) + ->call('markDeployed', $record->id) + ->assertSet('fromCommit', $second); + + expect($record->refresh()->deployed_at)->not->toBeNull(); + + // Új oldalbetöltésnél is a kirakott commitról indul a tartomány. + Livewire::test(DeploymentPackage::class)->assertSet('fromCommit', $second); + + deploymentCleanup($repository['path']); +}); + +test('a kimeneti mappán kívüli ZIP útvonal nem tölthető le', function () { + deploymentAllowEnvironment(); + $repository = deploymentTestRepository(); + [$first, , $third] = $repository['commits']; + + $this->actingAs(deploymentDeveloper()); + + // Elrontott (vagy szándékosan átírt) rekord: a fájl létezik, de a csomagoló + // kimeneti mappáján kívül van. + $record = DeploymentPackageRecord::create([ + 'name' => 'deploy_e2e_hamis', + 'target' => 'e2e', + 'branch' => 'main', + 'from_commit' => $first, + 'to_commit' => $third, + 'folder' => $repository['path'], + 'zip_path' => base_path('composer.json'), + ]); + + Livewire::test(DeploymentPackage::class) + ->call('downloadPackage', $record->id) + ->assertNoFileDownloaded(); + + deploymentCleanup($repository['path']); +}); + +test('a törlő szkript dry runban fut és ellenőrzi az app gyökeret', function () { + deploymentAllowEnvironment(); + $repository = deploymentTestRepository(); + [$first, , $third] = $repository['commits']; + + $package = app(DeploymentPackageBuilder::class)->build('e2e', 'main', $first, $third); + $script = File::get($package['path'].'/_torles.sh'); + + expect($script)->toStartWith('#!/usr/bin/env bash') + ->and($script)->toContain('[[ -f artisan && -d app ]]') + ->and($script)->toContain('"app/Regi.php"') + // Windowson generáljuk, Linuxon fut: CRLF-fel a shebang sor törne el. + ->and($script)->not->toContain("\r\n"); + + deploymentCleanup($repository['path']); +}); + test('az oldal nem fogad el kamu commit azonosítót', function () { deploymentAllowEnvironment(); $repository = deploymentTestRepository();