-version: "3.8"
+version: "3.9"
services:
app:
volumes:
- ./:/cli
depends_on:
- - db
+ db:
+ condition: service_completed_successfully
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: bookstack
MYSQL_USER: bookstack
- MYSQL_PASSWORD: bookstack
\ No newline at end of file
+ MYSQL_PASSWORD: bookstack
+ volumes:
+ - ./docker-mysql-init.sql:/docker-entrypoint-initdb.d/init.sql
\ No newline at end of file
--- /dev/null
+GRANT RELOAD ON *.* TO 'bookstack'@'%';
\ No newline at end of file
### Contributing
I welcome issues and PRs but keep in mind that I'd like to keep the feature-set narrow to limit support/maintenance burden.
-Therefore, I likely won't leave issues open long, or merge PRs, for requests to add new features or for changes that increase the scope of what this script already supports.
\ No newline at end of file
+Therefore, I likely won't leave issues open long, or merge PRs, for requests to add new features or for changes that increase the scope of what this script already supports.
+
+### Known Issues
+
+#### mysqldump - Couldn't execute 'FLUSH TABLES'
+
+mysqldump may produce the following:
+
+> mysqldump: Couldn't execute 'FLUSH TABLES': Access denied; you need (at least one of) the RELOAD or FLUSH_TABLES privilege(s) for this operation (1227)
+
+This was due to 8.0.32 mysqldump, changing the required permissions, and this should be largely [fixed as per 8.0.33](https://bugs.mysql.com/bug.php?id=109685).
+Temporary workaround is to provide the database user RELOAD permissions: `GRANT RELOAD ON *.* TO 'bookstack'@'%';`
\ No newline at end of file
$zip = new ZipArchive();
$zip->open($zipTempFile, ZipArchive::CREATE);
- // Add default files (.env config file and this CLI)
+ // Add default files (.env config file and this CLI if existing)
$zip->addFile($appDir . DIRECTORY_SEPARATOR . '.env', '.env');
- $zip->addFile($appDir . DIRECTORY_SEPARATOR . 'scripts' . DIRECTORY_SEPARATOR . 'run', 'run');
+ $cliPath = $appDir . DIRECTORY_SEPARATOR . 'bookstack-system-cli';
+ if (file_exists($cliPath)) {
+ $zip->addFile($cliPath, 'bookstack-system-cli');
+ }
if ($handleDatabase) {
$output->writeln("<info>Dumping the database via mysqldump...</info>");
fwrite($file, $data);
$hasOutput = true;
}, function ($error) use (&$errors) {
- $errors .= $error . "\n";
+ if (!str_contains($error, '[Warning] ')) {
+ $errors .= $error . "\n";
+ }
});
} catch (\Exception $exception) {
fclose($file);
public function assertSuccessfulExit(): void
{
- Assert::assertEquals(0, $this->tester->getStatusCode());
+ try {
+ $statusCode = $this->tester->getStatusCode();
+ } catch (\Exception $exception) {
+ $statusCode = 1;
+ }
+
+ Assert::assertEquals(0, $statusCode);
+ }
+
+ public function dumpError(): void
+ {
+ if ($this->error) {
+ echo $this->error->getMessage() . "\n" .
+ $this->error->getTraceAsString();
+ }
}
public function assertErrorExit(): void
--- /dev/null
+<?php declare(strict_types=1);
+
+namespace Tests\Commands;
+
+use Tests\TestCase;
+
+class BackupCommandTest extends TestCase
+{
+ static string $uniqueUserEmail = '';
+
+ public static function setUpBeforeClass(): void
+ {
+ static::$uniqueUserEmail = bin2hex(random_bytes(10)) . '@example.com';
+ chdir('/var/www/bookstack');
+ // Ensure the database is created and create an admin user we can look out for in the data.
+ exec('php artisan migrate --force');
+ exec('php artisan bookstack:create-admin --email="' . static::$uniqueUserEmail . '" --name="Bazza" --password="hunter200"');
+ }
+ public function test_command_does_full_backup_of_cwd_instance_by_default()
+ {
+ chdir('/var/www/bookstack');
+
+ $this->assertCount(0, glob('bookstack-backup-*.zip'));
+
+ file_put_contents('/var/www/bookstack/themes/test.txt', static::$uniqueUserEmail . '-themes');
+ file_put_contents('/var/www/bookstack/public/uploads/test.txt', static::$uniqueUserEmail . '-public-uploads');
+ file_put_contents('/var/www/bookstack/storage/uploads/test.txt', static::$uniqueUserEmail . '-storage-uploads');
+
+ $result = $this->runCommand('backup');
+ $result->dumpError();
+ $result->assertSuccessfulExit();
+ $result->assertStdoutContains("Backup finished.");
+
+ $this->assertCount(1, glob('bookstack-backup-*.zip'));
+ $zipFile = glob('bookstack-backup-*.zip')[0];
+
+ $zip = new \ZipArchive();
+ $zip->open($zipFile);
+ $dbDump = $zip->getFromName('db.sql');
+ $this->assertStringContainsString('APP_KEY=', $zip->getFromName('.env'));
+ $this->assertStringContainsString(static::$uniqueUserEmail, $dbDump);
+ $this->assertStringContainsString('page_revisions', $dbDump);
+ $this->assertStringContainsString(static::$uniqueUserEmail . '-public-uploads', $zip->getFromName('public/uploads/test.txt'));
+ $this->assertStringContainsString(static::$uniqueUserEmail . '-storage-uploads', $zip->getFromName('storage/uploads/test.txt'));
+ $this->assertStringContainsString(static::$uniqueUserEmail . '-themes', $zip->getFromName('themes/test.txt'));
+ }
+}
\ No newline at end of file