Static code analysis.
This commit is contained in:
11
Makefile
Normal file
11
Makefile
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
analyze-all:
|
||||||
|
@echo PHPMessDetector
|
||||||
|
-./vendor/bin/phpmd src text cleancode,codesize,controversial,design,naming,unusedcode
|
||||||
|
@echo PHPStan
|
||||||
|
-./vendor/bin/phpstan analyze --level=7 src/ backup
|
||||||
|
@echo Psalm
|
||||||
|
-./vendor/bin/psalm
|
||||||
|
install:
|
||||||
|
php composer.phar install --no-dev
|
||||||
|
install-dev:
|
||||||
|
php composer.phar install
|
3
backup
3
backup
@ -22,9 +22,10 @@ if (! $autoload) {
|
|||||||
|
|
||||||
require $autoload;
|
require $autoload;
|
||||||
|
|
||||||
|
$package = \Composer\InstalledVersions::getRootPackage();
|
||||||
use Symfony\Component\Console\Application;
|
use Symfony\Component\Console\Application;
|
||||||
|
|
||||||
$application = new Application();
|
$application = new Application('backup', $package['version']);
|
||||||
|
|
||||||
$application->add(new App\CommandBackup());
|
$application->add(new App\CommandBackup());
|
||||||
$application->add(new App\CommandShow());
|
$application->add(new App\CommandShow());
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
{
|
{
|
||||||
|
"name": "furyfire/backup",
|
||||||
|
"description": "Wrapper for Rclone based backup",
|
||||||
|
"homepage": "https://jcktrue.dk",
|
||||||
|
"version": "0.1.0",
|
||||||
"require": {
|
"require": {
|
||||||
"symfony/console": "^5.4",
|
"symfony/console": "^5.4",
|
||||||
"symfony/yaml": "^5.4",
|
"symfony/yaml": "^5.4",
|
||||||
@ -13,6 +17,8 @@
|
|||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"squizlabs/php_codesniffer": "*",
|
"squizlabs/php_codesniffer": "*",
|
||||||
"phpstan/phpstan": "^1.10"
|
"phpstan/phpstan": "^1.10",
|
||||||
|
"vimeo/psalm": "^5.12",
|
||||||
|
"phpmd/phpmd": "^2.13"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1724
composer.lock
generated
1724
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,8 @@
|
|||||||
notification:
|
notification:
|
||||||
domain: ntfy.jcktrue.dk
|
domain: ntfy.jcktrue.dk
|
||||||
topic: backup
|
topic: backup
|
||||||
|
rclone:
|
||||||
|
bwlimit: 6M
|
||||||
backup:
|
backup:
|
||||||
- title: Example
|
- title: Example
|
||||||
source: test/src
|
source: test/src
|
||||||
|
17
psalm.xml
Normal file
17
psalm.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
<psalm
|
||||||
|
errorLevel="2"
|
||||||
|
resolveFromConfigFile="true"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns="https://getpsalm.org/schema/config"
|
||||||
|
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||||
|
findUnusedBaselineEntry="true"
|
||||||
|
>
|
||||||
|
<projectFiles>
|
||||||
|
<directory name="src/" />
|
||||||
|
<file name="backup" />
|
||||||
|
<ignoreFiles>
|
||||||
|
<directory name="vendor" />
|
||||||
|
</ignoreFiles>
|
||||||
|
</projectFiles>
|
||||||
|
</psalm>
|
@ -5,10 +5,15 @@ use Symfony\Component\Console\Command\Command;
|
|||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Symfony\Component\Yaml\Exception\ParseException;
|
use Symfony\Component\Yaml\Exception\ParseException;
|
||||||
|
|
||||||
|
use Twig\Environment;
|
||||||
|
use Twig\Loader\ArrayLoader;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
class CommandBackup extends Command
|
class CommandBackup extends Command
|
||||||
{
|
{
|
||||||
static $defaultName = "backup";
|
static $defaultName = "backup";
|
||||||
@ -21,38 +26,39 @@ class CommandBackup extends Command
|
|||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
$output->writeln('Start backup!');
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
$io->title('Start backup process');
|
||||||
|
|
||||||
$output->writeln('Opening: '.$input->getArgument('config'));
|
$io->info('Opening: '.$input->getArgument('config'));
|
||||||
try {
|
try {
|
||||||
$config = Yaml::parseFile($input->getArgument('config'));
|
$config = Yaml::parseFile($input->getArgument('config'));
|
||||||
} catch (ParseException $e) {
|
} catch (ParseException $e) {
|
||||||
$output->writeln('Unable to parse the YAML string: '. $e->getMessage());
|
$io->error('Unable to parse the YAML string: '. $e->getMessage());
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
$rclone = new Rclone\Rclone();
|
$rclone = new Rclone\Rclone();
|
||||||
$output->writeln("Rclone version: ". $rclone->getVersion());
|
$io->info("Rclone version: ". $rclone->getVersion());
|
||||||
$ntfy = new Ntfy\Ntfy($config['notification']['domain']);
|
$ntfy = new Ntfy\Ntfy($config['notification']['domain']);
|
||||||
|
|
||||||
$loader = new \Twig\Loader\ArrayLoader($config['templates']);
|
$loader = new ArrayLoader($config['templates']);
|
||||||
$twig = new \Twig\Environment($loader);
|
$twig = new Environment($loader);
|
||||||
$twig->addExtension(new Twig\AppExtension());
|
$twig->addExtension(new Twig\AppExtension());
|
||||||
|
|
||||||
foreach ($config['backup'] as $conf) {
|
foreach($io->progressIterate( $config['backup']) as $conf) {
|
||||||
try {
|
try {
|
||||||
|
$template = array();
|
||||||
$template['config'] = $conf;
|
$template['config'] = $conf;
|
||||||
$template['start'] = new \DateTime();
|
$template['start'] = new DateTime();
|
||||||
$template['source_size'] = $rclone->getSize($conf['source']);
|
$template['source_size'] = $rclone->getSize($conf['source']);
|
||||||
$template['destination_size_before'] = $rclone->getSize($conf['destination']);
|
$template['destination_size_before'] = $rclone->getSize($conf['destination']);
|
||||||
|
|
||||||
$rclone->copy($conf['source'], $conf['destination'], "6M");
|
$rclone->copy($conf['source'], $conf['destination'], "6M");
|
||||||
|
|
||||||
$template['destination_size_after'] = $rclone->getSize($conf['destination']);
|
$template['destination_size_after'] = $rclone->getSize($conf['destination']);
|
||||||
$template['end'] = new \DateTime();
|
$template['end'] = new DateTime();
|
||||||
|
|
||||||
$message = $twig->render('notify', $template);
|
$message = $twig->render('notify', $template);
|
||||||
echo $message;
|
|
||||||
|
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
$message = $e->getMessage();
|
$message = $e->getMessage();
|
||||||
@ -60,6 +66,7 @@ class CommandBackup extends Command
|
|||||||
$ntfy->send($config['notification']['topic'], $conf['title'], $message);
|
$ntfy->send($config['notification']['topic'], $conf['title'], $message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$io->success("Complete");
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ use Symfony\Component\Console\Command\Command;
|
|||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
use Symfony\Component\Console\Helper\Table;
|
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||||
|
|
||||||
use Symfony\Component\Yaml\Yaml;
|
use Symfony\Component\Yaml\Yaml;
|
||||||
use Symfony\Component\Yaml\Exception\ParseException;
|
use Symfony\Component\Yaml\Exception\ParseException;
|
||||||
@ -22,20 +22,20 @@ class CommandShow extends Command
|
|||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
$output->writeln('Reading from: '.$input->getArgument('config'));
|
$io = new SymfonyStyle($input, $output);
|
||||||
|
$io->title('List backup entities');
|
||||||
|
|
||||||
|
$io->note('Reading from: '.$input->getArgument('config'));
|
||||||
try {
|
try {
|
||||||
$config = Yaml::parseFile($input->getArgument('config'));
|
$config = Yaml::parseFile($input->getArgument('config'));
|
||||||
} catch (ParseException $e) {
|
} catch (ParseException $e) {
|
||||||
$output->writeln('Unable to parse the YAML string: '. $e->getMessage());
|
$io->error('Unable to parse the YAML string: '. $e->getMessage());
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
$table = new Table($output);
|
|
||||||
$table
|
|
||||||
->setHeaders(['Description', 'Source', 'Destination'])
|
|
||||||
->setRows($config['backup']);
|
|
||||||
;
|
|
||||||
$table->render();
|
|
||||||
|
|
||||||
|
$io->table(['Description', 'Source', 'Destination'], $config['backup']);
|
||||||
|
|
||||||
|
$io->success("Done");
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,24 +4,37 @@ namespace App\Rclone;
|
|||||||
use Symfony\Component\Process\Process;
|
use Symfony\Component\Process\Process;
|
||||||
use Symfony\Component\Process\Exception\ProcessFailedException;
|
use Symfony\Component\Process\Exception\ProcessFailedException;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
class Rclone
|
class Rclone
|
||||||
{
|
{
|
||||||
protected string $rclone_path;
|
protected string $rclonePath;
|
||||||
/**
|
/**
|
||||||
* Global options
|
* Global options
|
||||||
*
|
*
|
||||||
* @var array<string>
|
* @var array<string>
|
||||||
*/
|
*/
|
||||||
protected array $global_options = [];
|
protected array $globalOptions = [];
|
||||||
|
|
||||||
function __construct(string $rclone_path = "rclone")
|
protected string $version = "";
|
||||||
|
|
||||||
|
function __construct(string $rclonePath = "rclone")
|
||||||
{
|
{
|
||||||
$this->rclone_path = $rclone_path;
|
$this->rclonePath = $rclonePath;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$version = $this->exec('--version');
|
||||||
|
$this->version = explode("\n", $version)[0];
|
||||||
|
}
|
||||||
|
catch(ProcessFailedException $e)
|
||||||
|
{
|
||||||
|
throw new Exception("Check installation of rclone");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVersion(): string
|
function getVersion(): string
|
||||||
{
|
{
|
||||||
return $this->exec('--version');
|
return $this->version;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSize(string $path): int
|
function getSize(string $path): int
|
||||||
@ -52,8 +65,8 @@ class Rclone
|
|||||||
{
|
{
|
||||||
$process = new Process(
|
$process = new Process(
|
||||||
array_merge(
|
array_merge(
|
||||||
[$this->rclone_path],
|
[$this->rclonePath],
|
||||||
$this->global_options,
|
$this->globalOptions,
|
||||||
[$command],
|
[$command],
|
||||||
$options
|
$options
|
||||||
)
|
)
|
||||||
|
@ -24,7 +24,7 @@ class AppExtension extends AbstractExtension
|
|||||||
public function formatBytes($bytes, $precision = 2)
|
public function formatBytes($bytes, $precision = 2)
|
||||||
{
|
{
|
||||||
$size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
$size = ['B','kB','MB','GB','TB','PB','EB','ZB','YB'];
|
||||||
$fact = floor((strlen((string)$bytes) - 1) / 3);
|
$fact = (int)(floor((strlen((string)$bytes) - 1) / 3));
|
||||||
return sprintf("%.{$precision}f", $bytes / pow(1024, $fact)) . $size[$fact];
|
return sprintf("%.{$precision}f", $bytes / pow(1024, $fact)) . $size[$fact];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user