create adhoc snapshot

Signed-off-by: Andy Miller <rhuk@mac.com>
This commit is contained in:
Andy Miller
2025-10-18 18:42:08 -06:00
parent 7325eb2cfe
commit c9640d7258
2 changed files with 93 additions and 0 deletions

View File

@@ -43,16 +43,21 @@ Usage:
bin/restore remove [<snapshot-id> ...] [--staging-root=/absolute/path]
Deletes one or more snapshots (interactive selection when no id provided).
bin/restore snapshot [--label=\"optional description\"] [--staging-root=/absolute/path]
Creates a manual snapshot of the current Grav core files.
bin/restore recovery [status|clear]
Shows the recovery flag context or clears it.
Options:
--staging-root Overrides the staging directory (defaults to configured value).
--label Optional label to store with the manual snapshot.
Examples:
bin/restore list
bin/restore apply stage-68eff31cc4104
bin/restore apply stage-68eff31cc4104 --staging-root=/var/grav-backups
bin/restore snapshot --label=\"Before plugin install\"
bin/restore recovery status
bin/restore recovery clear
USAGE;
@@ -274,6 +279,45 @@ function applySnapshot(string $snapshotId, array $options): void
exit(0);
}
/**
* @param array $options
* @return void
*/
function createManualSnapshot(array $options): void
{
$label = null;
if (isset($options['label']) && is_string($options['label'])) {
$label = trim($options['label']);
if ($label === '') {
$label = null;
}
}
try {
$service = createUpgradeService($options);
$manifest = $service->createSnapshot($label);
} catch (\Throwable $e) {
fwrite(STDERR, "Snapshot creation failed: " . $e->getMessage() . "\n");
exit(1);
}
$snapshotId = $manifest['id'] ?? null;
if (!$snapshotId) {
$snapshotId = 'unknown';
}
$version = $manifest['source_version'] ?? $manifest['target_version'] ?? 'unknown';
echo "Created snapshot {$snapshotId} (Grav {$version}).\n";
if ($label) {
echo "Label: {$label}\n";
}
if (!empty($manifest['backup_path'])) {
echo "Snapshot path: {$manifest['backup_path']}\n";
}
exit(0);
}
/**
* @param list<array{id:string,source_version:?string,target_version:?string,created_at:int}> $snapshots
* @return string|null
@@ -520,6 +564,10 @@ switch ($command) {
applySnapshot($snapshotId, $options);
break;
case 'snapshot':
createManualSnapshot($options);
break;
case 'recovery':
$action = strtolower($arguments[0] ?? 'status');
$manager = new RecoveryManager(GRAV_ROOT);

View File

@@ -210,6 +210,51 @@ class SafeUpgradeService
return $manifest;
}
/**
* Create a manual snapshot of the current Grav installation.
*
* @param string|null $label
* @return array
*/
public function createSnapshot(?string $label = null): array
{
$entries = $this->collectPackageEntries($this->rootPath);
if (!$entries) {
throw new RuntimeException('Unable to locate files to snapshot.');
}
$stageId = uniqid('snapshot-', false);
$backupPath = $this->stagingRoot . DIRECTORY_SEPARATOR . 'snapshot-' . $stageId;
$this->reportProgress('snapshot', 'Creating manual snapshot...', null, [
'operation' => 'snapshot',
'label' => $label,
'mode' => 'manual',
]);
$this->createBackupSnapshot($entries, $backupPath);
$manifest = $this->buildManifest($stageId, GRAV_VERSION, $this->rootPath, $backupPath, $entries);
$manifest['package_path'] = null;
if ($label !== null && $label !== '') {
$manifest['label'] = $label;
}
$manifest['operation'] = 'snapshot';
$manifest['mode'] = 'manual';
$this->persistManifest($manifest);
$this->pruneOldSnapshots();
$this->reportProgress('complete', sprintf('Snapshot %s created.', $stageId), 100, [
'operation' => 'snapshot',
'snapshot' => $stageId,
'version' => $manifest['target_version'] ?? null,
'mode' => 'manual',
]);
return $manifest;
}
private function collectPackageEntries(string $packagePath): array
{
$entries = [];