From 8efaef2042c186524a06342e9a0c0b3db10e38f3 Mon Sep 17 00:00:00 2001 From: ArthurErlich Date: Sat, 3 Oct 2026 23:26:21 +0200 Subject: [PATCH] feat(plugin): align with Shopware plugin guidelines - add dedicated monolog channel aeon_dump_manager and log dump jobs - migrate services.xml/routes.php to YAML - require PHP ^8.2 - add extension icon and .config/shopware-extension.yml - drop empty lifecycle overrides Co-Authored-By: Claude Sonnet 5.5 --- .config/shopware-extension.yml | 21 ++++ CLAUDE.md | 4 +- composer.json | 2 +- src/AeonDumpManager.php | 53 +++++----- .../Handler/CreateDumpHandler.php | 4 + src/Resources/config/packages/monolog.yaml | 2 + src/Resources/config/plugin.png | Bin 0 -> 48700 bytes src/Resources/config/routes.php | 7 -- src/Resources/config/routes.yaml | 3 + src/Resources/config/services.xml | 88 ----------------- src/Resources/config/services.yaml | 92 ++++++++++++++++++ src/Service/DumpService.php | 8 ++ 12 files changed, 155 insertions(+), 129 deletions(-) create mode 100644 .config/shopware-extension.yml create mode 100644 src/Resources/config/packages/monolog.yaml create mode 100644 src/Resources/config/plugin.png delete mode 100644 src/Resources/config/routes.php create mode 100644 src/Resources/config/routes.yaml delete mode 100755 src/Resources/config/services.xml create mode 100644 src/Resources/config/services.yaml diff --git a/.config/shopware-extension.yml b/.config/shopware-extension.yml new file mode 100644 index 0000000..f6ab11f --- /dev/null +++ b/.config/shopware-extension.yml @@ -0,0 +1,21 @@ +# shopware-cli extension configuration (build/package/validate) +build: + zip: + pack: + excludes: + paths: + - tests + - docs + - scripts + - phpunit.xml + - docker-compose.yml + - skills-lock.json + - AGENTS.md + - CLAUDE.md + - QWEN.md + - .claude + - .scratch + - .wayfinder + - src/Resources/config/plugin-transparent.png +validation: + ignore: [] diff --git a/CLAUDE.md b/CLAUDE.md index 284ecc7..634eee2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,7 +14,7 @@ the full build spec live under `.scratch/admin-dump-manager/` and `.scratch/dump - Plugin bootstrap class: `src/AeonDumpManager.php` (extends `Shopware\Core\Framework\Plugin`) — this is the entry point Shopware calls for install/uninstall/activate/deactivate/update lifecycle hooks. - Plugin class name is wired via `composer.json` → `extra.shopware-plugin-class`. -- Services are registered manually in `src/Resources/config/services.xml` (Symfony DI, XML format, +- Services are registered manually in `src/Resources/config/services.yaml` (Symfony DI, YAML format, autowiring is not configured — every new service/command/task must be added here explicitly with its tag, e.g. `console.command` or `shopware.scheduled.task`). - Plugin store-config UI fields (e.g. `maxDumps`, `retantionDays`) are defined in @@ -66,7 +66,7 @@ vendor/bin/phpunit tests/SomeTest.php # single file ## Adding functionality - New console commands go in `src/Command/`, new scheduled tasks in `src/ScheduledTask/` — both must - be registered in `src/Resources/config/services.xml` with the appropriate tag or Shopware will not + be registered in `src/Resources/config/services.yaml` with the appropriate tag or Shopware will not discover them. - Scheduled tasks need a matching handler (tagged `messenger.message_handler`) to actually run; a bare `ScheduledTask` subclass like `ExampleTask` only defines the schedule, not the behavior. diff --git a/composer.json b/composer.json index 2e90da2..2e2d71a 100755 --- a/composer.json +++ b/composer.json @@ -25,7 +25,7 @@ "source": "https://git.arthurerlich.de/haylan/AeonDumpManager" }, "require": { - "php": "^8.1", + "php": "^8.2", "shopware/core": "~6.6.0" }, "require-dev": { diff --git a/src/AeonDumpManager.php b/src/AeonDumpManager.php index 09ae91b..61710e2 100755 --- a/src/AeonDumpManager.php +++ b/src/AeonDumpManager.php @@ -4,17 +4,33 @@ namespace AeonDumpManager; use Doctrine\DBAL\Connection; use Shopware\Core\Framework\Plugin; -use Shopware\Core\Framework\Plugin\Context\ActivateContext; -use Shopware\Core\Framework\Plugin\Context\DeactivateContext; -use Shopware\Core\Framework\Plugin\Context\InstallContext; use Shopware\Core\Framework\Plugin\Context\UninstallContext; -use Shopware\Core\Framework\Plugin\Context\UpdateContext; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\Config\Loader\DelegatingLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; +use Symfony\Component\DependencyInjection\Loader\GlobFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; class AeonDumpManager extends Plugin { - public function install(InstallContext $installContext): void + public function build(ContainerBuilder $container): void { - // Do stuff such as creating a new payment method + parent::build($container); + + // Registers the dedicated monolog channel (Resources/config/packages/monolog.yaml) + $locator = new FileLocator('Resources/config'); + $resolver = new LoaderResolver([ + new YamlFileLoader($container, $locator), + new GlobFileLoader($container, $locator), + new DirectoryLoader($container, $locator), + ]); + + (new DelegatingLoader($resolver))->load( + \rtrim($this->getPath(), '/') . '/Resources/config/packages/*.yaml', + 'glob' + ); } public function uninstall(UninstallContext $uninstallContext): void @@ -29,29 +45,4 @@ class AeonDumpManager extends Plugin $connection = $this->container->get(Connection::class); $connection->executeStatement('DROP TABLE IF EXISTS `aeon_dump_manager_job`'); } - - public function activate(ActivateContext $activateContext): void - { - // Activate entities, such as a new payment method - // Or create new entities here, because now your plugin is installed and active for sure - } - - public function deactivate(DeactivateContext $deactivateContext): void - { - // Deactivate entities, such as a new payment method - // Or remove previously created entities - } - - public function update(UpdateContext $updateContext): void - { - // Update necessary stuff, mostly non-database related - } - - public function postInstall(InstallContext $installContext): void - { - } - - public function postUpdate(UpdateContext $updateContext): void - { - } } diff --git a/src/MessageQueue/Handler/CreateDumpHandler.php b/src/MessageQueue/Handler/CreateDumpHandler.php index 256df68..c9e6e47 100644 --- a/src/MessageQueue/Handler/CreateDumpHandler.php +++ b/src/MessageQueue/Handler/CreateDumpHandler.php @@ -5,6 +5,7 @@ namespace AeonDumpManager\MessageQueue\Handler; use AeonDumpManager\MessageQueue\Message\CreateDumpMessage; use AeonDumpManager\Service\DumpJobStatusService; use AeonDumpManager\Service\DumpService; +use Psr\Log\LoggerInterface; use Symfony\Component\Messenger\Attribute\AsMessageHandler; #[AsMessageHandler] @@ -13,6 +14,7 @@ class CreateDumpHandler public function __construct( private readonly DumpService $dumpService, private readonly DumpJobStatusService $jobStatus, + private readonly LoggerInterface $logger, ) { } @@ -20,10 +22,12 @@ class CreateDumpHandler { $jobId = $message->getJobId(); $this->jobStatus->markRunning($jobId); + $this->logger->info('Dump job started.', ['jobId' => $jobId]); try { $this->dumpService->createAsync($jobId, $this->jobStatus); } catch (\Throwable $exception) { + $this->logger->error('Dump job failed.', ['jobId' => $jobId, 'exception' => $exception]); $this->jobStatus->markFailed($jobId, $exception->getMessage()); } } diff --git a/src/Resources/config/packages/monolog.yaml b/src/Resources/config/packages/monolog.yaml new file mode 100644 index 0000000..e59b78f --- /dev/null +++ b/src/Resources/config/packages/monolog.yaml @@ -0,0 +1,2 @@ +monolog: + channels: ['aeon_dump_manager'] diff --git a/src/Resources/config/plugin.png b/src/Resources/config/plugin.png new file mode 100644 index 0000000000000000000000000000000000000000..cb3f9c9f42bcbaba042eed3f74692299c4e397e4 GIT binary patch literal 48700 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 . '/../../Controller/*Controller.php', 'attribute'); -}; diff --git a/src/Resources/config/routes.yaml b/src/Resources/config/routes.yaml new file mode 100644 index 0000000..5930254 --- /dev/null +++ b/src/Resources/config/routes.yaml @@ -0,0 +1,3 @@ +aeon_dump_manager_controllers: + resource: ../../Controller/*Controller.php + type: attribute diff --git a/src/Resources/config/services.xml b/src/Resources/config/services.xml deleted file mode 100755 index 1d8d433..0000000 --- a/src/Resources/config/services.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Resources/config/services.yaml b/src/Resources/config/services.yaml new file mode 100644 index 0000000..0fc47e3 --- /dev/null +++ b/src/Resources/config/services.yaml @@ -0,0 +1,92 @@ +services: + Symfony\Component\Process\ExecutableFinder: ~ + + aeon_dump_manager.dump_filename_parser: + class: AeonDumpManager\Service\DumpFilenameParser + + aeon_dump_manager.dump_binary_locator: + class: AeonDumpManager\Service\DumpBinaryLocator + arguments: + - '@Doctrine\DBAL\Connection' + - '@Shopware\Core\System\SystemConfig\SystemConfigService' + - '@Symfony\Component\Process\ExecutableFinder' + + aeon_dump_manager.dump_job_status_service: + class: AeonDumpManager\Service\DumpJobStatusService + arguments: + - '@Doctrine\DBAL\Connection' + + aeon_dump_manager.dump_lister: + class: AeonDumpManager\Service\DumpLister + arguments: + - '@aeon_dump_manager.filesystem.private' + - '@aeon_dump_manager.dump_filename_parser' + + aeon_dump_manager.dump_service: + class: AeonDumpManager\Service\DumpService + arguments: + - '@aeon_dump_manager.dump_lister' + - '@aeon_dump_manager.filesystem.private' + - '@aeon_dump_manager.dump_binary_locator' + - '@Doctrine\DBAL\Connection' + - '@Shopware\Core\System\SystemConfig\SystemConfigService' + - '@aeon_dump_manager.dump_filename_parser' + - '@monolog.logger.aeon_dump_manager' + + # Async create job + aeon_dump_manager.message_queue.create_dump_handler: + class: AeonDumpManager\MessageQueue\Handler\CreateDumpHandler + arguments: + - '@aeon_dump_manager.dump_service' + - '@aeon_dump_manager.dump_job_status_service' + - '@monolog.logger.aeon_dump_manager' + tags: + - { name: messenger.message_handler } + + # Retention + aeon_dump_manager.scheduled_task.purge_dumps_task: + class: AeonDumpManager\ScheduledTask\PurgeDumpsTask + tags: + - { name: shopware.scheduled.task } + + aeon_dump_manager.scheduled_task.purge_dumps_task_handler: + class: AeonDumpManager\ScheduledTask\PurgeDumpsTaskHandler + arguments: + - '@scheduled_task.repository' + - '@monolog.logger.aeon_dump_manager' + - '@aeon_dump_manager.dump_service' + - '@aeon_dump_manager.dump_job_status_service' + - '@Shopware\Core\System\SystemConfig\SystemConfigService' + tags: + - { name: messenger.message_handler, handles: AeonDumpManager\ScheduledTask\PurgeDumpsTask } + + # Console commands + aeon_dump_manager.command.dump_list: + class: AeonDumpManager\Command\DumpListCommand + arguments: + - '@aeon_dump_manager.dump_lister' + tags: + - { name: console.command } + + aeon_dump_manager.command.dump_create: + class: AeonDumpManager\Command\DumpCreateCommand + arguments: + - '@aeon_dump_manager.dump_service' + tags: + - { name: console.command } + + aeon_dump_manager.command.dump_remove: + class: AeonDumpManager\Command\DumpRemoveCommand + arguments: + - '@aeon_dump_manager.dump_service' + tags: + - { name: console.command } + + # Admin API controller + AeonDumpManager\Controller\DumpController: + public: true + arguments: + - '@aeon_dump_manager.dump_lister' + - '@aeon_dump_manager.dump_service' + - '@aeon_dump_manager.dump_job_status_service' + - '@messenger.default_bus' diff --git a/src/Service/DumpService.php b/src/Service/DumpService.php index 8725be6..d0cb382 100644 --- a/src/Service/DumpService.php +++ b/src/Service/DumpService.php @@ -6,6 +6,7 @@ use AeonDumpManager\Service\Exception\DumpNotFoundException; use AeonDumpManager\Struct\DumpFile; use Doctrine\DBAL\Connection; use League\Flysystem\FilesystemOperator; +use Psr\Log\LoggerInterface; use Shopware\Core\System\SystemConfig\SystemConfigService; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process; @@ -24,6 +25,7 @@ class DumpService private readonly Connection $connection, private readonly SystemConfigService $systemConfigService, private readonly DumpFilenameParser $filenameParser, + private readonly LoggerInterface $logger, ) { } @@ -69,6 +71,7 @@ class DumpService } $this->aeonDumpManagerFilesystemPrivate->delete($path); + $this->logger->info('Dump deleted.', ['filename' => $filename]); } /** @@ -100,6 +103,7 @@ class DumpService foreach ($excess as $dumpFile) { $this->aeonDumpManagerFilesystemPrivate->delete(DumpLister::path($dumpFile->filename)); + $this->logger->info('Dump removed: maxDumps exceeded.', ['filename' => $dumpFile->filename]); } } @@ -108,6 +112,7 @@ class DumpService foreach ($this->dumpLister->list() as $dumpFile) { if ($dumpFile->datetime < $threshold) { $this->aeonDumpManagerFilesystemPrivate->delete(DumpLister::path($dumpFile->filename)); + $this->logger->info('Dump removed: older than retention.', ['filename' => $dumpFile->filename]); } } } @@ -143,6 +148,7 @@ class DumpService if (!$process->isSuccessful()) { @unlink($tempPath); + $this->logger->error('Dump process failed.', ['exitCode' => $process->getExitCode()]); throw new ProcessFailedException($process); } @@ -153,6 +159,8 @@ class DumpService fclose($stream); @unlink($tempPath); + $this->logger->info('Dump created.', ['filename' => $filename, 'sizeBytes' => $sizeBytes]); + return new DumpFile($filename, $dateTime, $sizeBytes); }