diff --git a/config/packages/draw_framework_extra.yaml b/config/packages/draw_framework_extra.yaml index 7168e5569..3e415f705 100644 --- a/config/packages/draw_framework_extra.yaml +++ b/config/packages/draw_framework_extra.yaml @@ -5,7 +5,9 @@ draw_framework_extra: configuration: ~ - console: ~ + console: + documentation: + ignored_commands: [ 'completion', 'help', 'list' ] doctrine_extra: ~ @@ -38,7 +40,7 @@ draw_framework_extra: doctrine_message_bus_hook: ~ retry: event_driven: - transports: ['async'] + transports: [ 'async' ] serializer_event_dispatcher: ~ versioning: ~ @@ -72,7 +74,7 @@ draw_framework_extra: all: tags: ~ tag: - tags: ['Tags'] + tags: [ 'Tags' ] sort_schema: true diff --git a/packages/console/Command/GenerateDocumentationCommand.php b/packages/console/Command/GenerateDocumentationCommand.php new file mode 100644 index 000000000..00926f323 --- /dev/null +++ b/packages/console/Command/GenerateDocumentationCommand.php @@ -0,0 +1,91 @@ +setDescription('Generate a documentation for all the command of the application.') + ->addArgument('path', InputArgument::REQUIRED, 'The path where the documentation will be generated.'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + $io->section('Generate documentation'); + + $filePath = $input->getArgument('path'); + + $filesystem = new Filesystem(); + + $directoryPath = \dirname($filePath); + + if (!$filesystem->exists($directoryPath)) { + $filesystem->mkdir($directoryPath); + } + + file_put_contents($filePath, ''); + + $file = fopen($filePath, 'w'); + + $commandOutput = new StreamOutput($file); + + $commands = $this->getApplication()->all(); + + $progress = $io->createProgressBar(\count($commands)); + $progress->setFormat(ProgressBar::FORMAT_DEBUG); + + $descriptionHelper = new DescriptorHelper(); + + foreach ($commands as $command) { + $event = new GenerateDocumentationEvent($command); + $this->eventDispatcher?->dispatch($event); + + if ($event->isIgnored()) { + continue; + } + + $descriptionHelper->describe( + $commandOutput, + $command, + [ + 'format' => 'md', + 'raw_text' => false, + ] + ); + + fwrite($file, "\n\n"); + + $progress->advance(); + } + + $progress->finish(); + + $io->newLine(2); + $io->success('Export completed'); + + return Command::SUCCESS; + } +} diff --git a/packages/console/Event/GenerateDocumentationEvent.php b/packages/console/Event/GenerateDocumentationEvent.php new file mode 100644 index 000000000..6cb9b2c89 --- /dev/null +++ b/packages/console/Event/GenerateDocumentationEvent.php @@ -0,0 +1,37 @@ +command; + } + + public function ignore(): void + { + $this->ignore = true; + $this->stopPropagation(); + } + + public function isIgnored(): bool + { + return $this->ignore; + } +} diff --git a/packages/framework-extra-bundle/Console/EventListener/DocumentationIgnoredCommandEventListener.php b/packages/framework-extra-bundle/Console/EventListener/DocumentationIgnoredCommandEventListener.php new file mode 100644 index 000000000..c291ad276 --- /dev/null +++ b/packages/framework-extra-bundle/Console/EventListener/DocumentationIgnoredCommandEventListener.php @@ -0,0 +1,21 @@ +getCommand()->getName(), $this->ignoredCommandNames)) { + $event->ignore(); + } + } +} diff --git a/packages/framework-extra-bundle/DependencyInjection/Integration/ConsoleIntegration.php b/packages/framework-extra-bundle/DependencyInjection/Integration/ConsoleIntegration.php index 0d7a00486..409f9a4b9 100644 --- a/packages/framework-extra-bundle/DependencyInjection/Integration/ConsoleIntegration.php +++ b/packages/framework-extra-bundle/DependencyInjection/Integration/ConsoleIntegration.php @@ -2,6 +2,8 @@ namespace Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration; +use Draw\Bundle\FrameworkExtraBundle\Console\EventListener\DocumentationIgnoredCommandEventListener; +use Draw\Bundle\FrameworkExtraBundle\DrawFrameworkExtraBundle; use Draw\Component\Console\Command\PurgeExecutionCommand; use Draw\Component\Console\Entity\Execution; use Draw\Component\Console\EventListener\CommandFlowListener; @@ -41,6 +43,22 @@ public function load(array $config, PhpFileLoader $loader, ContainerBuilder $con $namespace, 'draw.console.' ); + + $this->registerClasses( + $loader, + $namespace = 'Draw\\Bundle\\FrameworkExtraBundle\\Console\\', + \dirname((new \ReflectionClass(DrawFrameworkExtraBundle::class))->getFileName()).'/Console' + ); + + $container + ->getDefinition(DocumentationIgnoredCommandEventListener::class) + ->setArgument('$ignoredCommandNames', $config['documentation']['ignored_commands']); + + $this->renameDefinitions( + $container, + $namespace, + 'draw.console.' + ); } public function addConfiguration(ArrayNodeDefinition $node): void @@ -48,6 +66,15 @@ public function addConfiguration(ArrayNodeDefinition $node): void $node ->children() ->booleanNode('ignore_disabled_command')->defaultFalse()->end() + ->arrayNode('documentation') + ->addDefaultsIfNotSet() + ->children() + ->arrayNode('ignored_commands') + ->defaultValue([]) + ->scalarPrototype()->end() + ->end() + ->end() + ->end() ->end(); } diff --git a/packages/framework-extra-bundle/Tests/DependencyInjection/Integration/ConsoleIntegrationTest.php b/packages/framework-extra-bundle/Tests/DependencyInjection/Integration/ConsoleIntegrationTest.php index 2a3a46be8..8a860ef5d 100644 --- a/packages/framework-extra-bundle/Tests/DependencyInjection/Integration/ConsoleIntegrationTest.php +++ b/packages/framework-extra-bundle/Tests/DependencyInjection/Integration/ConsoleIntegrationTest.php @@ -3,8 +3,10 @@ namespace Draw\Bundle\FrameworkExtraBundle\Tests\DependencyInjection\Integration; use Doctrine\Bundle\DoctrineBundle\DependencyInjection\DoctrineExtension; +use Draw\Bundle\FrameworkExtraBundle\Console\EventListener\DocumentationIgnoredCommandEventListener; use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\ConsoleIntegration; use Draw\Bundle\FrameworkExtraBundle\DependencyInjection\Integration\IntegrationInterface; +use Draw\Component\Console\Command\GenerateDocumentationCommand; use Draw\Component\Console\Command\PurgeExecutionCommand; use Draw\Component\Console\Entity\Execution; use Draw\Component\Console\EventListener\CommandFlowListener; @@ -32,6 +34,9 @@ public function getDefaultConfiguration(): array { return [ 'ignore_disabled_command' => false, + 'documentation' => [ + 'ignored_commands' => [], + ], ]; } @@ -86,9 +91,20 @@ public static function provideTestLoad(): iterable [ [ 'ignore_disabled_command' => true, + 'documentation' => [ + 'ignored_commands' => [ + 'help', + ], + ], ], ], [ + new ServiceConfiguration( + 'draw.console.command.generate_documentation_command', + [ + GenerateDocumentationCommand::class, + ], + ), new ServiceConfiguration( 'draw.console.command.purge_execution_command', [ @@ -106,6 +122,18 @@ function (Definition $definition): void { ); } ), + new ServiceConfiguration( + 'draw.console.event_listener.documentation_ignored_command_event_listener', + [ + DocumentationIgnoredCommandEventListener::class, + ], + function (Definition $definition): void { + static::assertSame( + ['help'], + $definition->getArgument('$ignoredCommandNames') + ); + } + ), ], ]; } diff --git a/tests/Console/Command/GenerateDocumentationCommandTest.php b/tests/Console/Command/GenerateDocumentationCommandTest.php new file mode 100644 index 000000000..8bff3374e --- /dev/null +++ b/tests/Console/Command/GenerateDocumentationCommandTest.php @@ -0,0 +1,87 @@ +get(GenerateDocumentationCommand::class); + } + + public function getCommandName(): string + { + return 'draw:console:generate-documentation'; + } + + public static function provideTestArgument(): iterable + { + yield [ + 'path', + InputArgument::REQUIRED, + ]; + + } + + public static function provideTestOption(): iterable + { + return []; + } + + public function testExecute(): void + { + $filePath = tempnam(sys_get_temp_dir(), 'testExecute'); + + register_shutdown_function( + function () use ($filePath): void { + if (file_exists($filePath)) { + unlink($filePath); + } + } + ); + + $this->execute(['path' => $filePath]) + ->test( + CommandDataTester::create() + ->setExpectedDisplay([ + 'Generate documentation', + 'Export completed', + ]) + ->setExpectedErrorOutput(null) + ); + + if ($this->writeFile) { + file_put_contents( + __DIR__.'/fixtures/GenerateDocumentationCommandTest/testExecution_expectedExport.md', + file_get_contents($filePath) + ); + } + + static::assertFileEquals( + __DIR__.'/fixtures/GenerateDocumentationCommandTest/testExecution_expectedExport.md', + $filePath + ); + } + + public function testWriteFile(): void + { + static::assertFalse( + $this->writeFile, + 'Do not forget to put this variable back to false.' + ); + } +} diff --git a/tests/Console/Command/fixtures/GenerateDocumentationCommandTest/testExecution_expectedExport.md b/tests/Console/Command/fixtures/GenerateDocumentationCommandTest/testExecution_expectedExport.md new file mode 100644 index 000000000..32da58785 --- /dev/null +++ b/tests/Console/Command/fixtures/GenerateDocumentationCommandTest/testExecution_expectedExport.md @@ -0,0 +1,226 @@ +`_complete` +----------- + +Internal command to provide shell completion suggestions + +### Usage + +* `_complete [-s|--shell SHELL] [-i|--input INPUT] [-c|--current CURRENT] [-S|--symfony SYMFONY]` + +Internal command to provide shell completion suggestions + +### Options + +#### `--shell|-s` + +The shell type ("bash") + +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Is negatable: no +* Default: `NULL` + +#### `--input|-i` + +An array of input tokens (e.g. COMP_WORDS or argv) + +* Accept value: yes +* Is value required: yes +* Is multiple: yes +* Is negatable: no +* Default: `array ()` + +#### `--current|-c` + +The index of the "input" array that the cursor is in (e.g. COMP_CWORD) + +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Is negatable: no +* Default: `NULL` + +#### `--symfony|-S` + +The version of the completion script + +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Is negatable: no +* Default: `NULL` + +#### `--help|-h` + +Display help for the given command. When no command is given display help for the list command + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + +#### `--quiet|-q` + +Do not output any message + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + +#### `--verbose|-v|-vv|-vvv` + +Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + +#### `--version|-V` + +Display this application version + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + +#### `--ansi|--no-ansi` + +Force (or disable --no-ansi) ANSI output + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: yes +* Default: `NULL` + +#### `--no-interaction|-n` + +Do not ask any interactive question + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + +`draw:console:generate-documentation` +------------------------------------- + +Generate a documentation for all the command of the application. + +### Usage + +* `draw:console:generate-documentation [--aws-newest-instance-role AWS-NEWEST-INSTANCE-ROLE] [--draw-execution-id DRAW-EXECUTION-ID] [--draw-execution-ignore] [--] ` + +Generate a documentation for all the command of the application. + +### Arguments + +#### `path` + +The path where the documentation will be generated. + +* Is required: yes +* Is array: no +* Default: `NULL` + +### Options + +#### `--aws-newest-instance-role` + +The instance role the server must be the newest of to run the command. + +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Is negatable: no +* Default: `NULL` + +#### `--draw-execution-id` + +The existing execution id of the command. Use internally by the DrawCommandBundle. + +* Accept value: yes +* Is value required: yes +* Is multiple: no +* Is negatable: no +* Default: `NULL` + +#### `--draw-execution-ignore` + +Flag to ignore login of the execution to the databases. + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + +#### `--help|-h` + +Display help for the given command. When no command is given display help for the list command + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + +#### `--quiet|-q` + +Do not output any message + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + +#### `--verbose|-v|-vv|-vvv` + +Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + +#### `--version|-V` + +Display this application version + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + +#### `--ansi|--no-ansi` + +Force (or disable --no-ansi) ANSI output + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: yes +* Default: `NULL` + +#### `--no-interaction|-n` + +Do not ask any interactive question + +* Accept value: no +* Is value required: no +* Is multiple: no +* Is negatable: no +* Default: `false` + diff --git a/tests/FilteredCommandTestTrait.php b/tests/FilteredCommandTestTrait.php new file mode 100644 index 000000000..c536190b9 --- /dev/null +++ b/tests/FilteredCommandTestTrait.php @@ -0,0 +1,17 @@ +