diff --git a/src/Commands/System/EnvCommand.php b/src/Commands/System/EnvCommand.php index 6feabe9e..4abcc3b8 100644 --- a/src/Commands/System/EnvCommand.php +++ b/src/Commands/System/EnvCommand.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Commands\System; use App\Command; +use App\Libs\Config; use App\Libs\Routable; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -16,8 +17,55 @@ final class EnvCommand extends Command protected function configure(): void { + $configPath = after(Config::get('path') . '/config/', ROOT_PATH); + $this->setName(self::ROUTE) - ->setDescription('Dump loaded environment variables.'); + ->setDescription('Show loaded environment variables.') + ->setHelp( + replacer( + <<# Docker compose file + +You can load environment variables via [docker-compose.yaml] file by adding them under the [environment] key. + +## [ Example ] + +To enable import task, do the following: + +------------------------------- +version: '3.3' +services: + watchstate: + image: ghcr.io/arabcoders/watchstate:latest + restart: unless-stopped + container_name: watchstate + environment: + - WS_CRON_IMPORT=1 +------------------------------- + +# .env file + +We automatically look for [.env] in this path [{path}]. The file usually +does not exist unless you have created it. + +The file format is simple [KEY=VALUE] pair per line. + +## [ Example ] + +To enable import task add the following line to [.env] file + +------------------------------- +WS_CRON_IMPORT=1 +------------------------------- + +HELP, + ['path' => $configPath] + ) + ); } protected function runCommand(InputInterface $input, OutputInterface $output): int diff --git a/src/Commands/System/IndexCommand.php b/src/Commands/System/IndexCommand.php index b0d885e2..f5510789 100644 --- a/src/Commands/System/IndexCommand.php +++ b/src/Commands/System/IndexCommand.php @@ -26,18 +26,25 @@ final class IndexCommand extends Command protected function configure(): void { + $cmdContext = trim(commandContext()); + $this->setName(self::ROUTE) ->setDescription('Ensure database has correct indexes.') ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not commit changes.') ->addOption('force-reindex', 'f', InputOption::VALUE_NONE, 'Drop existing indexes, and re-create them.') ->setHelp( - << $cmdContext, 'route' => self::ROUTE] + ) ); } diff --git a/src/Commands/System/LogsCommand.php b/src/Commands/System/LogsCommand.php index cac2ea6d..37b69234 100644 --- a/src/Commands/System/LogsCommand.php +++ b/src/Commands/System/LogsCommand.php @@ -32,20 +32,22 @@ final class LogsCommand extends Command protected function configure(): void { + $defaultDate = makeDate()->format('Ymd'); + $this->setName(self::ROUTE) ->addOption( 'type', - null, + 't', InputOption::VALUE_REQUIRED, sprintf('Log type, can be [%s].', implode(', ', self::LOG_FILES)), self::LOG_FILES[0] ) ->addOption( 'date', - null, + 'd', InputOption::VALUE_REQUIRED, 'Which log date to open. Format is [YYYYMMDD].', - makeDate()->format('Ymd'), + $defaultDate, ) ->addOption('list', null, InputOption::VALUE_NONE, 'List All log files.') ->addOption( @@ -55,10 +57,68 @@ final class LogsCommand extends Command 'Show last X number of log lines.', self::DEFAULT_LIMIT ) - ->addOption('tail', 't', InputOption::VALUE_NONE, 'Tail logfile.') + ->addOption('follow', 'f', InputOption::VALUE_NONE, 'Follow log file.') ->addOption('clear', null, InputOption::VALUE_NONE, 'Clear log file') ->setAliases(['logs']) - ->setDescription('View current logs.'); + ->setDescription('View current logs.') + ->setHelp( + replacer( + <<[ Expected Values ] +------------------- + +type expects the value to be [{files}], By Default [{defaultLog}]. +date expects the value to be [(number){8}]. By Default [{defaultDate}]. +limit expects the value to be [(number)]. By Default [{defaultLimit}]. + +------- +[ FAQ ] +------- + +# How to see all log files? + +{cmd} {route} --list + +# How to follow log file? + +{cmd} {route} --follow + +# How to clear log file? + +You can clear log file by running this command, However clearing log file require interactive confirmation. + +{cmd} {route} --type {defaultLog} --date {defaultDate} --clear + +# How to increase/decrease the returned log lines? + +By default, we return the last [{defaultLimit}] log lines. However, you can increase/decrease +the limit however you like by using [-l, --limit] flag. For example, + +{cmd} {route} --limit 100 + +# Where log files stored? + +By default, We store logs at [{logsPath}] + +HELP, + [ + 'files' => implode( + ' or ', + array_map(fn($val) => '' . $val . '', self::LOG_FILES) + ), + 'defaultLog' => self::LOG_FILES[0], + 'defaultDate' => $defaultDate, + 'defaultLimit' => self::DEFAULT_LIMIT, + 'cmd' => trim(commandContext()), + 'route' => self::ROUTE, + 'logsPath' => Config::get('tmpDir') . '/logs', + ] + ) + ); } /** @@ -104,7 +164,7 @@ final class LogsCommand extends Command return $this->handleClearLog($file, $input, $output); } - if ($input->getOption('tail')) { + if ($input->getOption('follow')) { $p = $file->getRealPath(); $lastPos = 0; @@ -176,19 +236,20 @@ final class LogsCommand extends Command $isTable = $input->getOption('output') === 'table'; foreach (glob($path . '/*.*.log') as $file) { - preg_match('/(\w+)\.(\d+)\.log/i', basename($file), $matches); + preg_match('/(\w+)\.(\w+)\.log/i', basename($file), $matches); $size = filesize($file); $builder = [ - 'type' => $matches[1], - 'date' => $matches[2], + 'type' => $matches[1] ?? '??', + 'tag' => $matches[2] ?? '??', 'size' => $isTable ? fsize($size) : $size, 'modified' => makeDate(filemtime($file))->format('Y-m-d H:i:s T'), ]; if (!$isTable) { $builder['file'] = $file; + $builder['modified'] = makeDate(filemtime($file)); } $list[] = $builder; diff --git a/src/Commands/System/MaintenanceCommand.php b/src/Commands/System/MaintenanceCommand.php index b236cbc7..ec58c1bb 100644 --- a/src/Commands/System/MaintenanceCommand.php +++ b/src/Commands/System/MaintenanceCommand.php @@ -23,7 +23,15 @@ final class MaintenanceCommand extends Command protected function configure(): void { $this->setName(self::ROUTE) - ->setDescription('Run maintenance tasks on database.'); + ->setDescription('Run maintenance tasks on database.') + ->setHelp( + <<setName(self::ROUTE) - ->setDescription('Create database migration file.') - ->addArgument('filename', InputArgument::REQUIRED, 'Migration name.'); + ->setDescription('Create database schema migration file.') + ->addArgument('filename', InputArgument::REQUIRED, 'Migration name.') + ->setHelp( + replacer( + <<{migrationPath}]. + +The migration file name must be in [in_english] without spaces. + +HELP, + ['migrationPath' => after(realpath(__DIR__ . '/../../../migrations'), ROOT_PATH)] + ) + + ); } protected function runCommand(InputInterface $input, OutputInterface $output): int { $file = $this->db->makeMigration($input->getArgument('filename')); - $output->writeln(sprintf('Created new migration at \'%s\'.', $file)); + $output->writeln( + sprintf( + 'Created new migration at \'%s\'.', + after(realpath($file), ROOT_PATH), + ) + ); return self::SUCCESS; } diff --git a/src/Commands/System/MigrationsCommand.php b/src/Commands/System/MigrationsCommand.php index 2c4b0958..7d29e078 100644 --- a/src/Commands/System/MigrationsCommand.php +++ b/src/Commands/System/MigrationsCommand.php @@ -25,14 +25,22 @@ final class MigrationsCommand extends Command { $this->setName(self::ROUTE) ->setDescription('Run database migrations.') - ->addOption('fresh', 'f', InputOption::VALUE_NONE, 'Start migrations from start.'); + ->addOption('force', 'f', InputOption::VALUE_NONE, 'Start migrations from start.') + ->setHelp( + <<getOption('fresh')) { + if ($input->getOption('force')) { $opts['fresh'] = true; } diff --git a/src/Commands/System/PHPCommand.php b/src/Commands/System/PHPCommand.php index 732e823b..8f1119e4 100644 --- a/src/Commands/System/PHPCommand.php +++ b/src/Commands/System/PHPCommand.php @@ -20,7 +20,24 @@ final class PHPCommand extends Command { $this->setName(self::ROUTE) ->setDescription('Generate php config.') - ->addOption('fpm', null, InputOption::VALUE_NONE, 'Generate php-fpm config.'); + ->addOption('fpm', null, InputOption::VALUE_NONE, 'Generate php-fpm config.') + ->setHelp( + replacer( + <<php.ini] and [fpm] pool. + +To generate fpm values run: + +{cmd} {route} --fpm + +HELP, + [ + 'cmd' => trim(commandContext()), + 'route' => self::ROUTE, + ] + ) + ); } protected function runCommand(InputInterface $input, OutputInterface $output): int diff --git a/src/Commands/System/PruneCommand.php b/src/Commands/System/PruneCommand.php index a8e6c533..7b8b6a19 100644 --- a/src/Commands/System/PruneCommand.php +++ b/src/Commands/System/PruneCommand.php @@ -28,16 +28,25 @@ final class PruneCommand extends Command protected function configure(): void { $this->setName(self::ROUTE) - // -- @RELEASE remove option. - ->addOption( - 'older-than', - null, - InputOption::VALUE_REQUIRED, - 'Unused option. Will be removed at release.', - Config::get('logs.prune.after', '-3 DAYS') - ) - ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not take any action.') - ->setDescription('Remove automatically generated files.'); + ->addOption('dry-run', null, InputOption::VALUE_NONE, 'Do not perform any actions on files.') + ->setDescription('Remove automatically generated files.') + ->setHelp( + replacer( + << trim(commandContext()), + 'route' => self::ROUTE, + ] + ) + ); } protected function runCommand(InputInterface $input, OutputInterface $output): int diff --git a/src/Commands/System/RoutesCommand.php b/src/Commands/System/RoutesCommand.php index 10c5f4fa..638023f3 100644 --- a/src/Commands/System/RoutesCommand.php +++ b/src/Commands/System/RoutesCommand.php @@ -17,7 +17,14 @@ final class RoutesCommand extends Command protected function configure(): void { $this->setName(self::ROUTE) - ->setDescription('Generate commands routes.'); + ->setDescription('Generate commands routes.')->setHelp( + << '' . strtoupper($val) . '', @@ -50,55 +49,62 @@ final class TasksCommand extends Command ->addOption('live', null, InputOption::VALUE_NONE, 'See output in real time.') ->setDescription('List & Run scheduled tasks.') ->setHelp( - <<[ FAQ ] +------- --------------------------- # How run scheduled tasks? --------------------------- To run scheduled tasks, Do the following -{$cmdContext} --run +{cmd} {route} --run ---------------------------------- # How to force run specific task? ---------------------------------- -You have to combine both [--run] and [--task task_name], For example: +You have to combine both [--run] and [--task task_name], For example: -{$cmdContext} --run --task import +{cmd} {route} --task import --run Running task in force mode, bypass the task enabled check. -------------------------- # How to configure tasks? -------------------------- -All Prebuilt tasks have 3 environment variables assoicated with them. +All Prebuilt tasks have 3 environment variables associated with them. ## WS_CRON_{TASK}: This environment variable control whether the task is enabled or not, it auto cast the value to bool. For example, -to enable import task simply add new environment varaible called WS_CRON_IMPORT with value of true or 1. +to enable import task simply add new environment variable called [WS_CRON_IMPORT] with value of [true] or [1]. ## WS_CRON_{TASK}_AT: This environment variable control when the task should run, it accepts valid cron expression timer. For example, -to run import every two hours add new environment variable called WS_CRON_IMPORT_AT with value of 0 */2 * * *. +to run import every two hours add new environment variable called [WS_CRON_IMPORT_AT] with value of [0 */2 * * *]. ## WS_CRON_{TASK}_ARGS: This environment variable control the options passed to the executed command, For example to expand the information -logged during import run, add new environment variable called WS_CRON_IMPORT_ARGS with value of -vvv --context --trace. -Simply put, run help on the assoicated command, and you can use any Options listed there in this variable. +logged during import run, add new environment variable called [WS_CRON_IMPORT_ARGS] with value of [-vvv --context]. +Simply put, run help on the associated command, and you can use any Options listed there in this variable. -------------------------------------- +## {TASK} -Replace {TASK} which one of the following [ $tasksName ] -environment variables are in ALL CAPITAL LETTERS +Replace {TASK} tag in environment variables which one of the following [ {tasksList} ] +environment variables are in ALL CAPITAL LETTERS. -HELP +HELP, + [ + 'cmd' => trim(commandContext()), + 'route' => self::ROUTE, + 'tasksList' => $tasksName, + ] + ) ); }