Skip to content

Commit

Permalink
feat!: make commands non-schedulable and optimize input options
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdmlln committed Jan 14, 2025
1 parent 24c90df commit 0e6d18e
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 73 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: remindgmbh/[email protected]
phpcs:
static-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -18,4 +18,4 @@ jobs:
extensions: intl
tools: composer:v2
- run: composer install
- run: composer run-script phpcs
- run: composer run-script static-analysis
26 changes: 10 additions & 16 deletions Classes/Command/DeleteBackupCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class DeleteBackupCommand extends Command
private const INPUT_DIR = 'dir';
private const INPUT_FILE = 'file';
private const INPUT_KEEP_COUNT = 'keep-count';
private const INPUT_NO_COMPRESSION = 'no-compression';
private const INPUT_COMPRESSION = 'compression';

/**
* @var mixed[]
Expand All @@ -37,14 +37,14 @@ protected function configure(): void
self::INPUT_DIR,
'd',
InputOption::VALUE_OPTIONAL,
'',
'Directory where backups are stored',
$this->extensionConfiguration['defaultDir'],
)
->addOption(
self::INPUT_FILE,
'f',
InputOption::VALUE_OPTIONAL,
'',
'Filename of the backup without file extension',
$this->extensionConfiguration['defaultFile'],
)
->addOption(
Expand All @@ -55,34 +55,28 @@ protected function configure(): void
$this->extensionConfiguration['delete']['keepCount']
)
->addOption(
self::INPUT_NO_COMPRESSION,
self::INPUT_COMPRESSION,
null,
InputOption::VALUE_NONE,
'Only delete non-compressed backups',
InputOption::VALUE_OPTIONAL,
'Only delete compressed or uncompressed files',
$this->extensionConfiguration['compression'],
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!(bool) $this->extensionConfiguration['delete']['enable']) {
$output->writeln(
'Backup deletion has to be enabled with [\'EXTENSIONS\'][\'rmnd_backup\'][\'delete\'][\'enable\'] = 1'
);
return Command::SUCCESS;
}

$dir = $input->getOption(self::INPUT_DIR);
$file = $input->getOption(self::INPUT_FILE);
$noCompression = $input->getOption(self::INPUT_NO_COMPRESSION);
$compression = (bool) $input->getOption(self::INPUT_COMPRESSION);

if (!is_dir($dir)) {
$output->writeln(sprintf('Directory \'%s\' does not exist.', $dir));
return Command::FAILURE;
}

$files = array_values(array_diff(scandir($dir, SCANDIR_SORT_ASCENDING) ?: [], ['.', '..']));
$matches = array_filter($files, function (string $fileToCheck) use ($file, $noCompression) {
return (bool) preg_match(FileNamingUtility::getRegexPattern($file, !$noCompression), $fileToCheck);
$matches = array_filter($files, function (string $fileToCheck) use ($file, $compression) {
return (bool) preg_match(FileNamingUtility::getRegexPattern($file, $compression), $fileToCheck);
});

$filesToBeDeleted = array_slice($matches, 0, -(int)$input->getOption(self::INPUT_KEEP_COUNT));
Expand Down
60 changes: 30 additions & 30 deletions Classes/Command/ExportCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;

final class ExportCommand extends Command
{
Expand All @@ -20,8 +21,8 @@ final class ExportCommand extends Command
private const INPUT_NO_DATA = 'no-data';
private const INPUT_INCLUDE_CACHE_DATA = 'include-cache-data';
private const INPUT_INCLUDE_DEFAULT_NO_DATA = 'include-default-no-data';
private const INPUT_OMIT_TIMESTAMP = 'omit-timestamp';
private const INPUT_NO_COMPRESSION = 'no-compression';
private const INPUT_TIMESTAMP = 'timestamp';
private const INPUT_COMPRESSION = 'compression';
private const DEFAULT_NO_DATA = [
'be_sessions',
'fe_sessions',
Expand Down Expand Up @@ -68,53 +69,54 @@ protected function configure(): void
null,
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Table with data excluded',
[],
GeneralUtility::trimExplode(',', $this->extensionConfiguration['export']['noData']),
)
->addOption(
self::INPUT_INCLUDE_CACHE_DATA,
null,
InputOption::VALUE_NONE,
'Include cache tables data'
InputOption::VALUE_OPTIONAL,
'Include cache tables data',
$this->extensionConfiguration['export']['includeCacheData']
)
->addOption(
self::INPUT_INCLUDE_DEFAULT_NO_DATA,
null,
InputOption::VALUE_NONE,
InputOption::VALUE_OPTIONAL,
sprintf('Include data for tables %s', implode(', ', self::DEFAULT_NO_DATA)),
$this->extensionConfiguration['export']['includeDefaultNoData'],
)
->addOption(
self::INPUT_OMIT_TIMESTAMP,
self::INPUT_TIMESTAMP,
null,
InputOption::VALUE_NONE,
'Omit timestamp in filename'
InputOption::VALUE_OPTIONAL,
'Include timestamp in filename',
$this->extensionConfiguration['export']['timestamp'],
)
->addOption(
self::INPUT_NO_COMPRESSION,
self::INPUT_COMPRESSION,
null,
InputOption::VALUE_NONE,
'Do not compress the output file',
InputOption::VALUE_OPTIONAL,
'Compress the output file',
$this->extensionConfiguration['compression'],
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!(bool) $this->extensionConfiguration['export']['enable']) {
$output->writeln(
'Database export has to be enabled with [\'EXTENSIONS\'][\'rmnd_backup\'][\'export\'][\'enable\'] = 1'
);
return Command::SUCCESS;
}

$dir = $input->getOption(self::INPUT_DIR);

$noCompression = $input->getOption(self::INPUT_NO_COMPRESSION);
$compression = (bool) $input->getOption(self::INPUT_COMPRESSION);
$ignoreTables = $input->getOption(self::INPUT_NO_DATA);
$includeCacheData = (bool) $input->getOption(self::INPUT_INCLUDE_CACHE_DATA);
$includeDefaultNoData = (bool) $input->getOption(self::INPUT_INCLUDE_DEFAULT_NO_DATA);
$timestamp = (bool) $input->getOption(self::INPUT_TIMESTAMP);

$path = FileNamingUtility::buildPath(
$dir,
$input->getOption(self::INPUT_FILE),
!$input->getOption(self::INPUT_OMIT_TIMESTAMP),
$timestamp,
true,
!$noCompression,
$compression,
);

if (file_exists($path)) {
Expand All @@ -132,13 +134,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
return str_starts_with($table, 'cache_');
});

$ignoreTables = $input->getOption(self::INPUT_NO_DATA);

if (!$input->getOption(self::INPUT_INCLUDE_CACHE_DATA)) {
if (!$includeCacheData) {
array_push($ignoreTables, ...$cacheTables);
}

if (!$input->getOption(self::INPUT_INCLUDE_DEFAULT_NO_DATA)) {
if (!$includeDefaultNoData) {
array_push($ignoreTables, ...self::DEFAULT_NO_DATA);
}

Expand All @@ -151,23 +151,23 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$this->databaseService->mysqldump(['--single-transaction', '--no-create-info', ...$ignoreTableArgs]),
];

$file = $noCompression ? fopen($path, 'a') : gzopen($path, 'a');
$file = $compression ? gzopen($path, 'a') : fopen($path, 'a');

if ($file) {
foreach ($processes as $process) {
$exitCode = $process->run(function ($type, $buffer) use ($output, $file, $noCompression): void {
$exitCode = $process->run(function ($type, $buffer) use ($output, $file, $compression): void {
if ($type === Process::ERR) {
$output->writeln($buffer);
} else {
$noCompression ? fwrite($file, $buffer) : gzwrite($file, $buffer);
$compression ? gzwrite($file, $buffer) : fwrite($file, $buffer);
}
});

if ($exitCode !== 0) {
return Command::FAILURE;
}
}
$noCompression ? fclose($file) : gzclose($file);
$compression ? gzclose($file) : fclose($file);
}

return Command::SUCCESS;
Expand Down
26 changes: 10 additions & 16 deletions Classes/Command/ImportCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ final class ImportCommand extends Command
{
private const INPUT_DIR = 'dir';
private const INPUT_FILE = 'file';
private const INPUT_NO_COMPRESSION = 'no-compression';
private const INPUT_COMPRESSION = 'compression';

/**
* @var mixed[]
Expand Down Expand Up @@ -49,27 +49,21 @@ protected function configure(): void
$this->extensionConfiguration['defaultFile'],
)
->addOption(
self::INPUT_NO_COMPRESSION,
self::INPUT_COMPRESSION,
null,
InputOption::VALUE_NONE,
'Do not use compressed file',
InputOption::VALUE_OPTIONAL,
'Use compressed input file',
$this->extensionConfiguration['compression'],
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!(bool) $this->extensionConfiguration['import']['enable']) {
$output->writeln(
'Database import has to be enabled with [\'EXTENSIONS\'][\'rmnd_backup\'][\'import\'][\'enable\'] = 1'
);
return Command::SUCCESS;
}

$dir = $input->getOption(self::INPUT_DIR);
$file = $input->getOption(self::INPUT_FILE);
$noCompression = $input->getOption(self::INPUT_NO_COMPRESSION);
$compression = (bool) $input->getOption(self::INPUT_COMPRESSION);

$path = FileNamingUtility::buildPath($dir, $file, false, true, !$noCompression);
$path = FileNamingUtility::buildPath($dir, $file, false, true, $compression);

if (!file_exists($path)) {
if (!is_dir($dir)) {
Expand All @@ -78,8 +72,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}

$files = array_values(array_diff(scandir($dir, SCANDIR_SORT_ASCENDING) ?: [], ['.', '..']));
$matches = array_filter($files, function (string $fileToCheck) use ($file, $noCompression) {
return (bool) preg_match(FileNamingUtility::getRegexPattern($file, !$noCompression), $fileToCheck);
$matches = array_filter($files, function (string $fileToCheck) use ($file, $compression) {
return (bool) preg_match(FileNamingUtility::getRegexPattern($file, $compression), $fileToCheck);
});
if (!empty($matches)) {
$path = FileNamingUtility::buildPath($dir, array_pop($matches));
Expand All @@ -90,7 +84,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
}
}

$stream = $noCompression ? fopen($path, 'r') : gzopen($path, 'r');
$stream = $compression ? fopen($path, 'r') : gzopen($path, 'r');

$process = $this->databaseService->mysql($stream);

Expand Down
3 changes: 3 additions & 0 deletions Configuration/Services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ services:
- name: console.command
command: 'remind:backup:export'
description: 'Export database'
schedulable: false
Remind\Backup\Command\ImportCommand:
tags:
- name: console.command
command: 'remind:backup:import'
description: 'Import database'
schedulable: false
Remind\Backup\Command\DeleteBackupCommand:
tags:
- name: console.command
command: 'remind:backup:delete'
description: 'Delete database backup'
schedulable: false
23 changes: 14 additions & 9 deletions ext_conf_template.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,25 @@ defaultDir = /var/www/html/backup
# cat=basic/defaults; type=string; label=Default Filename: Default name of the database dump
defaultFile = db

delete {
# cat=basic/delete; type=boolean; label=Enable Backup Delete: Enable backup delete command
enable = 0
# cat=basic/defaults; type=boolean; label=Compression: Enable or disable compression of the database dump
compression = 1

delete {
# cat=basic/delete; type=int+; label=Keep Count: Define how many database backups should be kept
keepCount = 10
}

export {
# cat=basic/export; type=boolean; label=Enable Export: Enable database export command
enable = 0
}
# cat=basic/export; type=string; label=No Data: Table with data excluded, separated by comma
noData =

import {
# cat=basic/import; type=boolean; label=Enable Import: Enable database import command
enable = 0
# cat=basic/export; type=boolean; label=Include Cache Data: Include Data for Cache Tables
includeCacheData = 0

# cat=basic/export; type=boolean; label=Include Default No Data: Include data for tables that are empty by default
includeDefaultNoData = 0

# cat=basic/export; type=boolean; label=Timestamp: Include timestamp in file name
timestamp = 1
}

0 comments on commit 0e6d18e

Please sign in to comment.