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' }})
+
+
+
+
+
+
+
+ | Csomag |
+ Tartomány |
+ Fájlok |
+ Készült |
+ Kirakva |
+ Műveletek |
+
+
+
+ @foreach ($recentPackages as $package)
+
+ | {{ $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
+ |
+
+ @endforeach
+
+
+
+
+ @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();