From: Dan Brown Date: Fri, 3 Mar 2023 02:44:08 +0000 (+0000) Subject: Outlined a general working backup approach X-Git-Url: http://source.bookstackapp.com/system-cli/commitdiff_plain/3641116721fcc138ca8cbfbf5de7c36dd8d6a19a Outlined a general working backup approach --- diff --git a/scripts/Commands/BackupCommand.php b/scripts/Commands/BackupCommand.php new file mode 100644 index 0000000..2b30385 --- /dev/null +++ b/scripts/Commands/BackupCommand.php @@ -0,0 +1,98 @@ +find('mysqldump'); + + $process = new Process([ + $mysqldumpPath, + '-h', $dbHost, + '-u', $dbUser, + '-p' . $dbPass, + '--single-transaction', + '--no-tablespaces', + $dbDatabase, + ]); + $process->start(); + + $errors = ""; + $dumpTempFile = tempnam(sys_get_temp_dir(), 'bsbackup'); + $dumpTempFileResource = fopen($dumpTempFile, 'w'); + foreach ($process as $type => $data) { + if ($process::OUT === $type) { + fwrite($dumpTempFileResource, $data); + } else { // $process::ERR === $type + $errors .= $data . "\n"; + } + } + fclose($dumpTempFileResource); + + + // Create a new ZIP file + $zipTempFile = tempnam(sys_get_temp_dir(), 'bsbackup'); + $zip = new ZipArchive(); + $sep = DIRECTORY_SEPARATOR; + $zip->open($zipTempFile, ZipArchive::CREATE); + $zip->addFile($this->appDir . $sep . '.env', '.env'); + $zip->addFile($dumpTempFile, 'db.sql'); + + $fileDirs = [ + $this->appDir . $sep . 'public' . $sep . 'uploads' => 'public/uploads', + $this->appDir . $sep . 'storage' . $sep . 'uploads' => 'storage/uploads', + ]; + + foreach ($fileDirs as $fullFileDir => $relativeFileDir) { + $dirIter = new RecursiveDirectoryIterator($fullFileDir); + $fileIter = new \RecursiveIteratorIterator($dirIter); + /** @var \SplFileInfo $file */ + foreach ($fileIter as $file) { + if (!$file->isDir()) { + $zip->addFile($file->getPathname(), $relativeFileDir . '/' . $fileIter->getSubPathname()); + } + } + } + + // Close off our zip and move it to the required location + $zip->close(); + rename($zipTempFile, $zipOutFile); + + // Delete our temporary DB dump file + unlink($dumpTempFile); + + // Announce end and display errors + echo "Finished"; + if ($errors) { + echo " with the following errors:\n" . $errors; + } + } +} diff --git a/scripts/compile.php b/scripts/compile.php index f1773d1..ff7d16c 100644 --- a/scripts/compile.php +++ b/scripts/compile.php @@ -30,6 +30,7 @@ try { // Add the rest of the apps files $phar->addFile(__DIR__ . '/run', 'run'); $phar->buildFromDirectory(__DIR__, '/vendor(.*)/'); + $phar->buildFromDirectory(__DIR__, '/Commands(.*)/'); // Customize the stub to add the shebang $stub = "#!/usr/bin/env php \n" . $defaultStub; diff --git a/scripts/composer.json b/scripts/composer.json index 27c6ef9..5bc28b4 100644 --- a/scripts/composer.json +++ b/scripts/composer.json @@ -1,8 +1,14 @@ { "require": { "minicli/minicli": "^3.2", + "symfony/process": "^6.0", "vlucas/phpdotenv": "^5.5" }, + "autoload": { + "psr-4": { + "Cli\\Commands\\": "Commands/" + } + }, "config": { "optimize-autoloader": true, "preferred-install": "dist", diff --git a/scripts/composer.lock b/scripts/composer.lock index 6cb3b99..3789592 100644 --- a/scripts/composer.lock +++ b/scripts/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d834df279b63d647dcaf31ae96468a27", + "content-hash": "b56ccc782d009482249ce5e3a4b62147", "packages": [ { "name": "graham-campbell/result-type", @@ -442,6 +442,67 @@ ], "time": "2022-11-03T14:55:06+00:00" }, + { + "name": "symfony/process", + "version": "v6.0.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "2114fd60f26a296cc403a7939ab91478475a33d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/2114fd60f26a296cc403a7939ab91478475a33d4", + "reference": "2114fd60f26a296cc403a7939ab91478475a33d4", + "shasum": "" + }, + "require": { + "php": ">=8.0.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.0.19" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-01T08:36:10+00:00" + }, { "name": "vlucas/phpdotenv", "version": "v5.5.0", diff --git a/scripts/run b/scripts/run index 435830f..ef25ce7 100644 --- a/scripts/run +++ b/scripts/run @@ -7,8 +7,8 @@ if (php_sapi_name() !== 'cli') { require __DIR__ . '/vendor/autoload.php'; +use Cli\Commands\BackupCommand; use Minicli\App; -use Minicli\Command\CommandCall; // Get the directory of the CLI "entrypoint", adjusted to be the real // location where running via a phar. @@ -16,22 +16,16 @@ $scriptDir = __DIR__; if (str_starts_with($scriptDir, 'phar://')) { $scriptDir = dirname(Phar::running(false)); } +$bsDir = dirname($scriptDir); // Load in our .env file from the parent directory -$dotenv = Dotenv\Dotenv::createImmutable(dirname($scriptDir)); +$dotenv = Dotenv\Dotenv::createImmutable($bsDir); $dotenv->safeLoad(); // Setup our CLI $app = new App(); -$app->setSignature('./run mycommand'); +$app->setSignature('./run'); -$app->registerCommand('mycommand', function (CommandCall $input) use ($scriptDir) { - echo "My Command!"; - echo "BS URL is: " . ($_SERVER['BS_URL'] ?? '') . "\n"; - echo "DB HOST is: " . ($_SERVER['DB_HOST'] ?? '') . "\n"; - echo dirname($scriptDir); - -// var_dump($input); -}); +$app->registerCommand('backup', [new BackupCommand($bsDir), 'handle']); $app->runCommand($argv);