isStale($archive)) { $this->build($archive); } return response() ->file($archive, [ 'Content-Type' => 'application/gzip', 'Content-Disposition' => 'attachment; filename="bootstrap.tar.gz"', ]); } /** * Neuer als alles, was drinsteckt? * * Über die Änderungszeit und nicht über einen Hash: das Verzeichnis ist * klein, der Vergleich läuft bei jedem Abruf, und ein Hash über alle Dateien * kostete bei jedem Abruf mehr als das Packen gelegentlich kostet. */ private function isStale(string $archive): bool { if (! is_file($archive)) { return true; } $built = filemtime($archive); foreach ($this->sourceFiles() as $file) { if (filemtime($file) > $built) { return true; } } return false; } /** @return list */ private function sourceFiles(): array { $root = base_path('deploy/bootstrap'); if (! is_dir($root)) { return []; } $files = []; $iterator = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($root, \FilesystemIterator::SKIP_DOTS) ); foreach ($iterator as $file) { if ($file->isFile()) { $files[] = $file->getPathname(); } } return $files; } /** * Mit GNU tar und nicht mit PharData: `phar.readonly` steht in diesem * Container auf 1, und das Ausführbar-Bit soll erhalten bleiben. * * Der oberste Eintrag heißt `bootstrap/`, damit die kopierte Befehlszeile * mit `tar xz -C /opt/clupilot` genau `/opt/clupilot/bootstrap` ergibt — * der Ort, an dem das Skript sich selbst und seine Bibliothek erwartet. */ private function build(string $archive): void { @mkdir(dirname($archive), 0755, true); $process = new Process( ['tar', 'czf', $archive, '-C', base_path('deploy'), 'bootstrap'], timeout: 60, ); $process->mustRun(); } }