Fassung 1.7.0: verlässliche Installation, übersichtliche Verwaltung und bedienbare Bewegung
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s

This commit is contained in:
kantarci committed 2026-09-14 10:49:54 +02:00
1 parent b164c4edfe
commit 495b26896e
55 files changed
+1632 -423

No files matched your search

+54 -5
View File
@@ -19,6 +19,8 @@ use Doctrine\DBAL\Connection;
*/
final class Updater
{
private array $environmentBefore = [];
private ?string $backupDirectory = null;
public const MAINTENANCE_FILE = 'var/maintenance';
public const LOG_FILE = 'var/updates.log';
@@ -231,8 +233,22 @@ final class Updater
$step($steps, 'Wartungszustand', true, 'Die Website antwortet mit 503, die Verwaltung bleibt erreichbar.');
$migrated = false;
foreach (['.env', '.env.local'] as $file) {
if (is_file($this->root.'/'.$file)) {
$this->environmentBefore[$file] = (string) file_get_contents($this->root.'/'.$file);
}
}
try {
// Older repositories tracked this file. Snapshot above, clear legacy index flags,
// then let Git replace its own copy; restore the installation before booting PHP.
if ($this->git(['ls-files', '--error-unmatch', '.env'])['ok']) {
$this->git(['update-index', '--no-assume-unchanged', '--no-skip-worktree', '.env']);
if (!$this->git(['checkout', '--', '.env'])['ok']) {
throw new \RuntimeException('Die alte Git-Konfiguration konnte nicht für den Updatewechsel vorbereitet werden.');
}
}
$checkout = $this->git(['checkout', '--force', $target]);
$this->restoreEnvironment();
if (!$step($steps, 'Code wechseln', $checkout['ok'], $checkout['ok'] ? 'Auf '.$target.' gewechselt.' : $checkout['output'])) {
throw new \RuntimeException('checkout');
}
@@ -320,15 +336,27 @@ final class Updater
private function rollback(array &$steps, string $commit, ?string $backup, callable $step): void
{
$back = $this->git(['checkout', '--force', $commit]);
try {
$this->restoreEnvironment();
} catch (\RuntimeException $e) {
$step($steps, 'Konfiguration wiederherstellen', false, $e->getMessage());
return;
}
$step($steps, 'Zurück auf den alten Stand', $back['ok'], $back['ok'] ? 'Auf '.substr($commit, 0, 8).' zurückgesetzt.' : $back['output']);
if (null !== $backup && null !== $this->databaseFile()) {
$file = $this->databaseFile();
$copy = dirname($backup).'/'.basename((string) $file);
$copy = $this->backupDirectory.'/'.basename((string) $file);
$restored = is_readable($copy) && @copy($copy, (string) $file);
$step($steps, 'Datenbank zurückschreiben', $restored, $restored
? 'Die Sicherung von vor der Migration ist wieder eingespielt.'
: 'Die Sicherung ließ sich nicht zurückschreiben. Die Datenbank liegt unter '.dirname((string) $backup).'.');
: 'Die Sicherung ließ sich nicht zurückschreiben. Die Datenbank liegt unter '.$this->backupDirectory.'.');
if (!$restored) {
$step($steps, 'Wartungszustand bleibt', false, 'Die Datenbank konnte nicht wiederhergestellt werden. Der Betrieb bleibt angehalten.');
return;
}
}
$composer = $this->runner->resolve('composer', $this->root);
@@ -346,11 +374,27 @@ final class Updater
$step($steps, 'Wartungszustand bleibt', false, 'Auch der alte Stand antwortet nicht. Die Website bleibt im Wartungszustand, damit niemand eine kaputte Seite sieht. Ausgabe: '.$this->lastLine($health['output']));
}
/** Legacy tags tracked .env; preserve the installation even when checking out those tags. */
private function restoreEnvironment(): void
{
foreach ($this->environmentBefore as $file => $content) {
$path = $this->root.'/'.$file;
if (is_file($path) && file_get_contents($path) === $content) {
continue;
}
if (false === @file_put_contents($path, $content, LOCK_EX)) {
throw new \RuntimeException('Die Installationskonfiguration konnte nicht wiederhergestellt werden. Sie liegt in der Updatesicherung unter var/backups.');
}
@chmod($path, 0o600);
}
}
/** Copies the database and the environment files, and returns a sentence about it. */
private function backup(): ?string
{
$directory = $this->root.'/var/backups/update-'.gmdate('Ymd-His');
if (!is_dir($directory) && !@mkdir($directory, 0o775, true)) {
$this->backupDirectory = null;
$directory = $this->root.'/var/backups/update-'.gmdate('Ymd-His').'-'.bin2hex(random_bytes(4));
if (!@mkdir($directory, 0o700, true)) {
return null;
}
$file = $this->databaseFile();
@@ -361,10 +405,15 @@ final class Updater
}
foreach (['.env', '.env.local'] as $environment) {
if (is_readable($this->root.'/'.$environment)) {
@copy($this->root.'/'.$environment, $directory.'/'.$environment);
if (!@copy($this->root.'/'.$environment, $directory.'/'.$environment)) {
return null;
}
@chmod($directory.'/'.$environment, 0o600);
}
}
$this->backupDirectory = $directory;
return sprintf(
'%s (%s). Der Medienordner ist nicht dabei – er ist zu groß, und kein Update schreibt darin.',
str_replace('\\', '/', substr($directory, strlen($this->root) + 1)),