Full restore creates missing tables and suspends foreign keys; --install-apps; clearer messages

Backups now record each table's structure in the dump manifest. The full
restore creates tables that do not exist on the new instance (left behind
by apps that were removed or are switched off), lists all problems at
once, and no longer stops on apps that are missing. Foreign keys are not
checked while rows are loaded in name order, and checked again before the
restore commits (PostgreSQL: dropped and added back in the transaction).

--install-apps installs or enables the apps that were enabled at backup
time; --skip-missing-tables leaves out tables of older backups whose
structure is unknown. nextcloud-aio is never required.

backup:target:add without a key points to backup:key:import, and says that
connecting can take a while. Removing a location drops its cached index;
a migration removes the cache of locations that are gone.

The full-restore test now also runs on PostgreSQL with Mail (foreign keys)
and a disabled app.
This commit is contained in:
2026-10-18 01:45:01 +02:00
parent 5af712acc3
commit d8ef50732f
11 changed files with 359 additions and 46 deletions
+7 -1
View File
@@ -44,7 +44,10 @@ HELP);
protected function execute(InputInterface $input, OutputInterface $output): int {
if (!$this->keys->isInitialized()) {
$output->writeln('<error>No backup key yet: run occ backup:key:init first.</error>');
$output->writeln('<error>No backup key yet.</error>');
$output->writeln('Restoring from a location that already has backups (disaster recovery)? Import the key from the recovery kit first:');
$output->writeln(' occ backup:key:import /path/to/ng_backup-kit.json');
$output->writeln('Only for a new location without backups: create a new key with occ backup:key:init. A new key cannot read existing backups.');
return 1;
}
$options = [];
@@ -60,6 +63,9 @@ HELP);
$output->writeln('<error>--auth is required (see occ backup:target:backends)</error>');
return 1;
}
// Adopting an existing repository reads its whole index first, which takes a while on a
// large backup or a slow line; say so instead of looking stuck.
$output->writeln('Connecting to the location; with existing backups this reads their index first and can take a while ...');
try {
$r = $this->targets->add($input->getArgument('name'), $input->getArgument('backend'), $input->getOption('auth'), $options, (string)$input->getOption('path'));
} catch (\Throwable $e) {