Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/pages/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ export default defineConfig({
sidebar: [
{
text: "Introduction",
items: [{ text: "Getting started", link: "/getting-started.md" }],
items: [
{ text: "Getting started", link: "/getting-started.md" },
{ text: "Commands", link: "/commands.md" },
],
},
{
text: "Configuration",
Expand All @@ -26,6 +29,7 @@ export default defineConfig({
items: [
{ text: "Pipeline", link: "/pipeline.md" },
{ text: "Notifications", link: "/notifications.md" },
{ text: "Metadata", link: "/metadata.md" },
],
},
],
Expand Down
9 changes: 9 additions & 0 deletions docs/pages/commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Commands

Statamic backup comes with a few commands to help you manage backups from a cli.

| Command | Description |
| ---------------------------------------- | ------------------------------------------------------------ |
| `php artisan statamic:backup` | Run the backup process |
| `php artisan statamic:backup:temp-clean` | Clean up leftover temporary backup files, like upload chunks |
| `php artisan statamic:backup:restore` | Restore your site to chosen backup |
1 change: 1 addition & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="testing"/>
<env name="APP_ENV" value="testing"/>
<env name="APP_KEY" value="base64:2fl+Ktvkfl+Fuz4Qp/A75G2RTiWVA/ZoKZvp6fiiM10="/>
</php>
</phpunit>
10 changes: 5 additions & 5 deletions src/Console/Commands/BackupCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@
use Illuminate\Console\Command;
use Itiden\Backup\Facades\Backuper;

use function Laravel\Prompts\{info, spin};

/**
* Backup site
*/
class BackupCommand extends Command
{
protected $signature = 'statamic:backup';

protected $description = 'Backup your stuff';
protected $description = 'Run the backup pipeline';

public function handle()
{
$this->components->info('Backing up content');

$backup_location = Backuper::backup();
$backup = spin(fn () => Backuper::backup(), 'Backing up...');

$this->components->info('Backup saved to ' . $backup_location->path);
info('Backup saved to ' . $backup->path);
}
}
12 changes: 7 additions & 5 deletions src/Console/Commands/ClearFilesCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,27 @@
use Illuminate\Console\Command;
use Illuminate\Support\Facades\File;

use function Laravel\Prompts\info;

/**
* Clear the backup temp directory
*/
class ClearFilesCommand extends Command
{
protected $signature = 'statamic:backup:clear';
protected $signature = 'statamic:backup:temp-clear';

protected $description = 'Empty the temp directory';
protected $description = 'Clear the backup temp directory';

public function handle()
{
if (! File::exists(config('backup.temp_path'))) {
$this->components->info('Backup temp directory does not exist, no need to clear it.');
if (!File::exists(config('backup.temp_path'))) {
info('Backup temp directory does not exist, no need to clear it.');

return;
}

File::cleanDirectory(config('backup.temp_path'));

$this->components->info('Backup temp directory cleared successfully');
info('Backup temp directory cleared successfully');
}
}
41 changes: 29 additions & 12 deletions src/Console/Commands/RestoreCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,47 @@
namespace Itiden\Backup\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;
use Itiden\Backup\Contracts\Repositories\BackupRepository;
use Itiden\Backup\DataTransferObjects\BackupDto;
use Itiden\Backup\Facades\Restorer;

use function Laravel\Prompts\{confirm, spin, info, select};

/**
* Restore content from a directory / backup
*/
class RestoreCommand extends Command implements PromptsForMissingInput
final class RestoreCommand extends Command
{
protected $signature = 'statamic:backup:restore {path} {--force}';
protected $signature = 'statamic:backup:restore {--path=} {--force}';

protected $description = 'Reset or restore content from a directory / backup';

protected function promptForMissingArgumentsUsing()
public function handle(BackupRepository $repo)
{
return [
'path' => 'Which filepath does your backup have?',
];
}
/* @var BackupDto $backup */
$backup = match (true) {
(bool) $this->option('path') => BackupDto::fromAbsolutePath($this->option('path')),
default => BackupDto::fromFile(select(
label: 'Which backup do you want to restore to?',
scroll: 10,
options: $repo->all()->flatMap(
fn (BackupDto $backup) => [$backup->path => $backup->path]
),
required: true
)),
};

public function handle()
{
if ($this->option('force') || $this->confirm('Are you sure you want to restore your content?')) {
Restorer::restore(BackupDto::fromAbsolutePath($this->argument('path')));
if (
$this->option('force')
|| confirm(
label: "Are you sure you want to restore your content?",
hint: "This will overwrite your current content with state from {$backup->created_at->format('Y-m-d H:i:s')}",
required: true
)
) {
spin(fn () => Restorer::restore($backup), 'Restoring backup');

info('Backup restored!');
}
}
}
4 changes: 2 additions & 2 deletions tests/Feature/ClearTempTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

File::deleteDirectory($temp_path);

$this->artisan('statamic:backup:clear')->assertExitCode(0);
$this->artisan('statamic:backup:temp-clear')->assertExitCode(0);
});

it("will clear temp path when running backup clear command", function () {
Expand All @@ -21,7 +21,7 @@

expect(File::allFiles($temp_path))->toHaveCount(1);

$this->artisan('statamic:backup:clear');
$this->artisan('statamic:backup:temp-clear');

expect(File::allFiles($temp_path))->toHaveCount(0);
});
6 changes: 3 additions & 3 deletions tests/Feature/RestoreBackupTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,19 @@

File::cleanDirectory(config('backup.content_path'));

$this->artisan('statamic:backup:restore', ['path' => Storage::path($backup->path)])
$this->artisan('statamic:backup:restore', ['--path' => Storage::path($backup->path)])
->expectsConfirmation('Are you sure you want to restore your content?', 'no');

expect(File::isEmptyDirectory(config('backup.content_path')))->toBeTrue();

$this->artisan('statamic:backup:restore', ['path' => Storage::path($backup->path), '--force' => true])
$this->artisan('statamic:backup:restore', ['--path' => Storage::path($backup->path), '--force' => true])
->assertExitCode(0);
});

it('can restore from path command', function () {
$backup = Backuper::backup();

$this->artisan('statamic:backup:restore', ['path' => Storage::path($backup->path), '--force' => true])
$this->artisan('statamic:backup:restore', ['--path' => Storage::path($backup->path), '--force' => true])
->assertExitCode(0);

expect(File::isEmptyDirectory(config('backup.content_path')))->toBeFalse();
Expand Down
35 changes: 35 additions & 0 deletions tests/Feature/RestoreCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

use Illuminate\Support\Facades\Storage;
use Itiden\Backup\Contracts\Repositories\BackupRepository;
use Itiden\Backup\Facades\Backuper;

use function Pest\Laravel\artisan;

uses()->group('restore-command');

it('shows all available backups', function () {
app(BackupRepository::class)->empty();

Backuper::backup();

$backups = app(BackupRepository::class)->all();

artisan('statamic:backup:restore')
->expectsQuestion(
question: 'Which backup do you want to restore to?',
answer: $backups->first()->path
)
->expectsConfirmation('Are you sure you want to restore your content?')
->assertFailed();
});

it('can restore from a specific path', function () {
app(BackupRepository::class)->empty();

$backup = Backuper::backup();

artisan('statamic:backup:restore', ['--path' => Storage::disk(config('backup.destination.disk'))->path($backup->path)])
->expectsConfirmation('Are you sure you want to restore your content?')
->assertFailed();
});
27 changes: 26 additions & 1 deletion tests/Unit/PipeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
Assets::class,
]);

test('can skip a pipe', function () {
test('can skip a pipe with users', function () {
/** @var Users::class $pipe */
$pipe = app()->make(Users::class);

Expand All @@ -63,3 +63,28 @@

$zipper->close();
});

test('can skip a pipe with content', function () {
/** @var Users::class $pipe */
$pipe = app()->make(Content::class);

$callable = function ($z) {
return $z;
};

File::copyDirectory(config('backup.content_path'), config('backup.content_path') . '_backup');
File::deleteDirectory(config('backup.content_path'));

$zipper = Zipper::open(config('backup.temp_path') . '/backup.zip');

$pipe->backup(zip: $zipper, next: $callable);


expect($zipper->getMeta())->toHaveKey(Content::class);
expect($zipper->getMeta()[Content::class])->toHaveKey('skipped', 'Content directory didn\'t exist, is it configured correctly?');

$zipper->close();

File::copyDirectory(config('backup.content_path') . '_backup', config('backup.content_path'));
File::deleteDirectory(config('backup.content_path') . '_backup');
});
Loading