diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac25f8c..1643321 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,14 +1,65 @@ name: CI -on: [push] +on: [push, pull_request] jobs: - build-test: + tests: + name: PHP ${{ matrix.php }} (${{ matrix.dependencies }}${{ matrix.dbal && ', dbal ' || '' }}${{ matrix.dbal }}) runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ['8.2', '8.3', '8.4', '8.5'] + dependencies: [highest, lowest] + include: + - php: '8.2' + dependencies: highest + dbal: '^3.0' + - php: '8.4' + dependencies: highest + dbal: '^4.0' steps: - uses: actions/checkout@v6 - - uses: php-actions/composer@v6 # or alternative dependency management - - uses: php-actions/phpunit@v4 - - name: Run PHP CS Fixer - run: php vendor/bin/php-cs-fixer fix --dry-run --diff \ No newline at end of file + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + tools: composer + - name: Pin doctrine/dbal + if: matrix.dbal + run: composer require --no-update "doctrine/dbal:${{ matrix.dbal }}" + - name: Install dependencies + run: composer update --no-interaction --no-progress --prefer-stable ${{ matrix.dependencies == 'lowest' && '--prefer-lowest' || '' }} + - name: Run PHPUnit + run: vendor/bin/phpunit + + static-analysis: + name: Code style & static analysis + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: none + tools: composer + - run: composer update --no-interaction --no-progress --prefer-stable + - name: PHP CS Fixer + run: vendor/bin/php-cs-fixer fix --dry-run --diff + - name: PHPStan + run: vendor/bin/phpstan analyse --no-progress + + rector: + name: Rector + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: none + tools: composer + - run: composer update --no-interaction --no-progress --prefer-stable + - name: Rector + run: vendor/bin/rector process --dry-run --no-progress-bar diff --git a/CHANGELOG.md b/CHANGELOG.md index 679d424..5628010 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +# Version 6.0.4 +* Fix compatibility with `monolog/monolog` < 3.8 (`StreamHandler` `fileOpenMode` argument) in `JobProcessor` +* Require `monolog/monolog` `^3.0` (`^2.0` was never installable with `symfony/monolog-bridge` 7.4) +* Use the `#[Frequency]` attribute on `ScheduledDataflow::$frequency` +* Fix PHPStan errors (`DateTimeInterface` clone in `ScheduledDataflowManager`, status types in `JobRepository`) +* Add `league/flysystem` to dev dependencies +* CI: test on PHP 8.2 to 8.5 with lowest and highest dependencies, and with `doctrine/dbal` 3 and 4; add PHPStan and Rector checks + # Version 6.0.3 * Fix job error count display using `Job::getExceptionCount()` when available in `JobShowCommand` * Fix exceptions retrieval when only a stream is available in `JobShowCommand` diff --git a/composer.json b/composer.json index 350d0b9..3dcbba6 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "ext-json": "*", "doctrine/dbal": "^3.0||^4.0", "doctrine/doctrine-bundle": "^2.0", - "monolog/monolog": "^2.0||^3.0", + "monolog/monolog": "^3.0", "psr/log": "^1.1||^2.0||^3.0", "symfony/config": "^7.4", "symfony/console": "^7.4", @@ -62,6 +62,7 @@ "amphp/amp": "^2.5", "ekino/phpstan-banned-code": "^3.2", "friendsofphp/php-cs-fixer": "^3.75", + "league/flysystem": "^3.0", "phpunit/phpunit": "^11", "portphp/portphp": "^1.9", "rector/rector": "^2.0", diff --git a/rector.php b/rector.php index 5beb0f7..0e2b1b7 100644 --- a/rector.php +++ b/rector.php @@ -15,7 +15,6 @@ ->withSets([ SymfonySetList::SYMFONY_CODE_QUALITY, SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION, - SymfonySetList::SYMFONY_73, LevelSetList::UP_TO_PHP_82, ]) ; diff --git a/src/Command/AddScheduledDataflowCommand.php b/src/Command/AddScheduledDataflowCommand.php index fe20282..638fdf8 100644 --- a/src/Command/AddScheduledDataflowCommand.php +++ b/src/Command/AddScheduledDataflowCommand.php @@ -67,9 +67,7 @@ public function __invoke( if (!$firstRun) { $firstRun = $io->ask('When is the first execution of the scheduled dataflow (format: Y-m-d H:i:s)?'); } - if ($enabled === null) { - $enabled = $io->confirm('Enable the scheduled dataflow?'); - } + $enabled ??= $io->confirm('Enable the scheduled dataflow?'); $newScheduledDataflow = ScheduledDataflow::createFromArray([ 'id' => null, diff --git a/src/Command/ChangeScheduleStatusCommand.php b/src/Command/ChangeScheduleStatusCommand.php index 2e665b4..93987a7 100644 --- a/src/Command/ChangeScheduleStatusCommand.php +++ b/src/Command/ChangeScheduleStatusCommand.php @@ -55,7 +55,7 @@ public function __invoke( return 3; } - $enable = $enable ?? !$disable; + $enable ??= !$disable; try { $schedule->setEnabled($enable); diff --git a/src/Command/ExecuteDataflowCommand.php b/src/Command/ExecuteDataflowCommand.php index 3a9c38c..d15a100 100644 --- a/src/Command/ExecuteDataflowCommand.php +++ b/src/Command/ExecuteDataflowCommand.php @@ -50,7 +50,7 @@ public function __invoke( $dataflowType->setRepository($this->jobRepository); } - if ($dataflowType instanceof LoggerAwareInterface && isset($this->logger)) { + if (isset($this->logger)) { $dataflowType->setLogger($this->logger); } diff --git a/src/DataflowType/Dataflow/AMPAsyncDataflow.php b/src/DataflowType/Dataflow/AMPAsyncDataflow.php index cdacac3..0801317 100644 --- a/src/DataflowType/Dataflow/AMPAsyncDataflow.php +++ b/src/DataflowType/Dataflow/AMPAsyncDataflow.php @@ -114,9 +114,7 @@ private function processState(mixed $state, int &$count, int &$countExceptions): { [$readIndex, $stepIndex, $item] = $state; if ($stepIndex < \count($this->steps)) { - if (!isset($this->stepsJobs[$stepIndex])) { - $this->stepsJobs[$stepIndex] = []; - } + $this->stepsJobs[$stepIndex] ??= []; [$step, $scale] = $this->steps[$stepIndex]; if ((is_countable($this->stepsJobs[$stepIndex]) ? \count($this->stepsJobs[$stepIndex]) : 0) < $scale && !isset($this->stepsJobs[$stepIndex][$readIndex])) { $this->stepsJobs[$stepIndex][$readIndex] = true; diff --git a/src/DataflowType/Result.php b/src/DataflowType/Result.php index 5a31ab6..f7ae9e9 100644 --- a/src/DataflowType/Result.php +++ b/src/DataflowType/Result.php @@ -11,7 +11,7 @@ class Result { private readonly \DateInterval $elapsed; - private int $successCount; + private readonly int $successCount; /** @var array */ private array $exceptions = []; diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 9c21b1c..465d3c2 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -48,7 +48,7 @@ public function getConfigTreeBuilder(): TreeBuilder ->end() ->end() ->validate() - ->ifTrue(static fn ($v): bool => $v['type'] === 'file' && !class_exists('\League\Flysystem\Filesystem')) + ->ifTrue(static fn ($v): bool => $v['type'] === 'file' && !class_exists(\League\Flysystem\Filesystem::class)) ->thenInvalid('You need "league/flysystem" to use Dataflow file exception mode.') ->end() ->end() diff --git a/src/Entity/ScheduledDataflow.php b/src/Entity/ScheduledDataflow.php index 161c806..43e6bcb 100644 --- a/src/Entity/ScheduledDataflow.php +++ b/src/Entity/ScheduledDataflow.php @@ -37,10 +37,8 @@ class ScheduledDataflow private ?array $options = null; - /** - * @Frequency() - */ #[Asserts\NotBlank] + #[Frequency] private ?string $frequency = null; private ?\DateTimeInterface $next = null; diff --git a/src/Manager/ScheduledDataflowManager.php b/src/Manager/ScheduledDataflowManager.php index 3b5baa1..60abb60 100644 --- a/src/Manager/ScheduledDataflowManager.php +++ b/src/Manager/ScheduledDataflowManager.php @@ -41,7 +41,7 @@ public function createJobsFromScheduledDataflows(): void private function updateScheduledDataflowNext(ScheduledDataflow $scheduled): void { $interval = \DateInterval::createFromDateString($scheduled->getFrequency()); - $next = clone $scheduled->getNext(); + $next = \DateTime::createFromInterface($scheduled->getNext()); $now = new \DateTime(); while ($next < $now) { diff --git a/src/Processor/JobProcessor.php b/src/Processor/JobProcessor.php index 18f4d26..75fdf9b 100644 --- a/src/Processor/JobProcessor.php +++ b/src/Processor/JobProcessor.php @@ -46,7 +46,7 @@ public function process(Job $job): void } $tempFile = tempnam(sys_get_temp_dir(), 'dataflow_'); - $handler = new StreamHandler($tempFile, fileOpenMode: 'w+'); + $handler = new StreamHandler(fopen($tempFile, 'w+')); $handler->setFormatter(new LineFormatter(self::FORMAT)); $loggers = [new Logger('dataflow_internal', [$bufferHandler = $handler])]; diff --git a/src/Repository/JobRepository.php b/src/Repository/JobRepository.php index f96c17b..c3dc9f4 100644 --- a/src/Repository/JobRepository.php +++ b/src/Repository/JobRepository.php @@ -175,7 +175,7 @@ public function deleteOld(int $days): array $qb = $this->connection->createQueryBuilder(); $ids = $qb->select('j.id') ->from(static::TABLE_NAME, 'j') - ->andWhere($qb->expr()->in('j.status', [Job::STATUS_COMPLETED, Job::STATUS_CRASHED])) + ->andWhere($qb->expr()->in('j.status', [(string) Job::STATUS_COMPLETED, (string) Job::STATUS_CRASHED])) ->andWhere('j.end_time < :date') ->setParameter('date', new \DateTime("- {$days} days"), 'datetime') ->executeQuery() @@ -184,7 +184,7 @@ public function deleteOld(int $days): array $qb = $this->connection->createQueryBuilder(); $qb->delete(static::TABLE_NAME.' j') - ->andWhere($qb->expr()->in('j.status', [Job::STATUS_COMPLETED, Job::STATUS_CRASHED])) + ->andWhere($qb->expr()->in('j.status', [(string) Job::STATUS_COMPLETED, (string) Job::STATUS_CRASHED])) ->andWhere('j.end_time < :date') ->setParameter('date', new \DateTime("- {$days} days"), 'datetime') ->executeStatement() diff --git a/src/Validator/Constraints/Frequency.php b/src/Validator/Constraints/Frequency.php index 231a3ef..9d7edcb 100644 --- a/src/Validator/Constraints/Frequency.php +++ b/src/Validator/Constraints/Frequency.php @@ -11,6 +11,7 @@ * * @codeCoverageIgnore */ +#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD)] class Frequency extends Constraint { public $message = 'The provided frequency "{{ string }}" must be a valid parameter for DateInterval::createFromDateString() and must not represent a negative value';