From c7474aff14f0fcfc08579343f811741ddd4455a4 Mon Sep 17 00:00:00 2001 From: Maksim Vorozhtsov Date: Sat, 9 Mar 2024 18:56:57 +0300 Subject: [PATCH 1/7] Ask for a namespace of when more one path --- src/Tools/Console/Command/DiffCommand.php | 19 +- src/Tools/Console/Command/DoctrineCommand.php | 38 ++++ .../Console/Command/DumpSchemaCommand.php | 13 +- src/Tools/Console/Command/GenerateCommand.php | 20 +- .../Tools/Console/Command/DiffCommandTest.php | 41 ++++ .../Console/Command/DoctrineCommandTest.php | 175 ++++++++++++++++++ .../Console/Command/DumpSchemaCommandTest.php | 57 +++++- .../Console/Command/GenerateCommandTest.php | 51 ++++- 8 files changed, 345 insertions(+), 69 deletions(-) diff --git a/src/Tools/Console/Command/DiffCommand.php b/src/Tools/Console/Command/DiffCommand.php index 6ec790128..3bebfc578 100644 --- a/src/Tools/Console/Command/DiffCommand.php +++ b/src/Tools/Console/Command/DiffCommand.php @@ -9,19 +9,15 @@ use Doctrine\Migrations\Metadata\ExecutedMigrationsList; use Doctrine\Migrations\Tools\Console\Exception\InvalidOptionUsage; use Doctrine\SqlFormatter\SqlFormatter; -use OutOfBoundsException; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use function addslashes; -use function assert; use function class_exists; use function count; use function filter_var; -use function is_string; -use function key; use function sprintf; use const FILTER_VALIDATE_BOOLEAN; @@ -110,10 +106,6 @@ protected function execute( $allowEmptyDiff = $input->getOption('allow-empty-diff'); $checkDbPlatform = filter_var($input->getOption('check-database-platform'), FILTER_VALIDATE_BOOLEAN); $fromEmptySchema = $input->getOption('from-empty-schema'); - $namespace = $input->getOption('namespace'); - if ($namespace === '') { - $namespace = null; - } if ($formatted) { if (! class_exists(SqlFormatter::class)) { @@ -123,16 +115,7 @@ protected function execute( } } - $configuration = $this->getDependencyFactory()->getConfiguration(); - - $dirs = $configuration->getMigrationDirectories(); - if ($namespace === null) { - $namespace = key($dirs); - } elseif (! isset($dirs[$namespace])) { - throw new OutOfBoundsException(sprintf('Path not defined for the namespace %s', $namespace)); - } - - assert(is_string($namespace)); + $namespace = $this->getNamespace($input, $output); $statusCalculator = $this->getDependencyFactory()->getMigrationStatusCalculator(); $executedUnavailableMigrations = $statusCalculator->getExecutedUnavailableMigrations(); diff --git a/src/Tools/Console/Command/DoctrineCommand.php b/src/Tools/Console/Command/DoctrineCommand.php index cc908ec88..661f30d54 100644 --- a/src/Tools/Console/Command/DoctrineCommand.php +++ b/src/Tools/Console/Command/DoctrineCommand.php @@ -10,16 +10,22 @@ use Doctrine\Migrations\Tools\Console\ConsoleLogger; use Doctrine\Migrations\Tools\Console\Exception\DependenciesNotSatisfied; use Doctrine\Migrations\Tools\Console\Exception\InvalidOptionUsage; +use Exception; use Psr\Log\LoggerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ChoiceQuestion; use Symfony\Component\Console\Style\StyleInterface; use Symfony\Component\Console\Style\SymfonyStyle; +use function array_keys; use function assert; +use function count; use function is_string; +use function key; +use function sprintf; /** * The DoctrineCommand class provides base functionality for the other migrations commands to extend from. @@ -138,4 +144,36 @@ private function setNamedEmOrConnection(InputInterface $input): void return; } } + + final protected function getNamespace(InputInterface $input, OutputInterface $output): string + { + $configuration = $this->getDependencyFactory()->getConfiguration(); + + $namespace = $input->getOption('namespace'); + if ($namespace === '') { + $namespace = null; + } + + $dirs = $configuration->getMigrationDirectories(); + if ($namespace === null && count($dirs) === 1) { + $namespace = key($dirs); + } elseif ($namespace === null && count($dirs) > 1) { + $helper = $this->getHelper('question'); + $question = new ChoiceQuestion( + 'Please choose a namespace (defaults to the first one)', + array_keys($dirs), + 0, + ); + $namespace = $helper->ask($input, $output, $question); + $this->io->text(sprintf('You have selected the "%s" namespace', $namespace)); + } + + if (! isset($dirs[$namespace])) { + throw new Exception(sprintf('Path not defined for the namespace "%s"', $namespace)); + } + + assert(is_string($namespace)); + + return $namespace; + } } diff --git a/src/Tools/Console/Command/DumpSchemaCommand.php b/src/Tools/Console/Command/DumpSchemaCommand.php index dd927db82..724d0cf34 100644 --- a/src/Tools/Console/Command/DumpSchemaCommand.php +++ b/src/Tools/Console/Command/DumpSchemaCommand.php @@ -13,10 +13,7 @@ use Symfony\Component\Console\Output\OutputInterface; use function addslashes; -use function assert; use function class_exists; -use function is_string; -use function key; use function sprintf; use function str_contains; @@ -91,15 +88,7 @@ public function execute( } } - $configuration = $this->getDependencyFactory()->getConfiguration(); - - $namespace = $input->getOption('namespace'); - if ($namespace === null) { - $dirs = $configuration->getMigrationDirectories(); - $namespace = key($dirs); - } - - assert(is_string($namespace)); + $namespace = $this->getNamespace($input, $output); $this->checkNoPreviousDumpExistsForNamespace($namespace); diff --git a/src/Tools/Console/Command/GenerateCommand.php b/src/Tools/Console/Command/GenerateCommand.php index 3bddf9e4e..043d6485e 100644 --- a/src/Tools/Console/Command/GenerateCommand.php +++ b/src/Tools/Console/Command/GenerateCommand.php @@ -4,15 +4,11 @@ namespace Doctrine\Migrations\Tools\Console\Command; -use Exception; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use function assert; -use function is_string; -use function key; use function sprintf; /** @@ -47,23 +43,9 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $configuration = $this->getDependencyFactory()->getConfiguration(); - $migrationGenerator = $this->getDependencyFactory()->getMigrationGenerator(); - $namespace = $input->getOption('namespace'); - if ($namespace === '') { - $namespace = null; - } - - $dirs = $configuration->getMigrationDirectories(); - if ($namespace === null) { - $namespace = key($dirs); - } elseif (! isset($dirs[$namespace])) { - throw new Exception(sprintf('Path not defined for the namespace %s', $namespace)); - } - - assert(is_string($namespace)); + $namespace = $this->getNamespace($input, $output); $fqcn = $this->getDependencyFactory()->getClassNameGenerator()->generateClassName($namespace); diff --git a/tests/Tools/Console/Command/DiffCommandTest.php b/tests/Tools/Console/Command/DiffCommandTest.php index 366f27e3f..c568e1b3f 100644 --- a/tests/Tools/Console/Command/DiffCommandTest.php +++ b/tests/Tools/Console/Command/DiffCommandTest.php @@ -18,10 +18,13 @@ use Doctrine\Migrations\Version\Version; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Tester\CommandTester; use function array_map; use function explode; +use function sprintf; use function sys_get_temp_dir; use function trim; @@ -139,6 +142,44 @@ public function testExecutedUnavailableMigrationsCancel(): void self::assertSame(3, $statusCode); } + /** @return array */ + public static function getSelectedNamespace(): array + { + return [ + 'no' => [null, 'FooNs'], + 'first' => [0, 'FooNs'], + 'two' => [1, 'FooNs2'], + ]; + } + + /** @dataProvider getSelectedNamespace */ + public function testExecuteWithMultipleDirectories(int|null $input, string $namespace): void + { + $this->migrationStatusCalculator + ->method('getNewMigrations') + ->willReturn(new AvailableMigrationsList([])); + + $this->migrationStatusCalculator + ->method('getExecutedUnavailableMigrations') + ->willReturn(new ExecutedMigrationsList([])); + + $this->configuration->addMigrationsDirectory('FooNs2', sys_get_temp_dir()); + + $this->diffCommand->setHelperSet(new HelperSet(['question' => new QuestionHelper()])); + + $this->migrationDiffGenerator->expects(self::once())->method('generate'); + + $this->diffCommandTester->setInputs([$input]); + $this->diffCommandTester->execute([]); + + $output = $this->diffCommandTester->getDisplay(true); + + self::assertStringContainsString('Please choose a namespace (defaults to the first one)', $output); + self::assertStringContainsString('[0] FooNs', $output); + self::assertStringContainsString('[1] FooNs2', $output); + self::assertStringContainsString(sprintf('You have selected the "%s" namespace', $namespace), $output); + } + protected function setUp(): void { $this->migrationDiffGenerator = $this->createMock(DiffGenerator::class); diff --git a/tests/Tools/Console/Command/DoctrineCommandTest.php b/tests/Tools/Console/Command/DoctrineCommandTest.php index 9b9520d8e..d192644a9 100644 --- a/tests/Tools/Console/Command/DoctrineCommandTest.php +++ b/tests/Tools/Console/Command/DoctrineCommandTest.php @@ -16,7 +16,10 @@ use Doctrine\Migrations\Tools\Console\Command\DoctrineCommand; use Doctrine\Migrations\Tools\Console\Exception\InvalidOptionUsage; use Doctrine\ORM\EntityManager; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Tester\CommandTester; @@ -207,4 +210,176 @@ protected function execute(InputInterface $input, OutputInterface $output): int ['interactive' => false], ); } + + public function testNamespaceFromOption(): void + { + $configuration = new Configuration(); + $configuration->addMigrationsDirectory('DoctrineMigrations', sys_get_temp_dir()); + + $conn = $this->createMock(Connection::class); + $connLoader = new ExistingConnection($conn); + + $dependencyFactory = DependencyFactory::fromConnection( + new ExistingConfiguration($configuration), + $connLoader, + ); + + $command = new class ($dependencyFactory) extends DoctrineCommand + { + protected function configure(): void + { + parent::configure(); + + $this + ->addOption( + 'namespace', + null, + InputOption::VALUE_REQUIRED, + 'The namespace to use for the migration (must be in the list of configured namespaces)', + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + DoctrineCommandTest::assertSame($this->getNamespace($input, $output), 'DoctrineMigrations'); + + return DoctrineCommand::SUCCESS; + } + }; + + $commandTester = new CommandTester($command); + $commandTester->execute( + ['--namespace' => 'DoctrineMigrations'], + ['interactive' => false], + ); + } + + public function testNamespaceUnknown(): void + { + $configuration = new Configuration(); + $configuration->addMigrationsDirectory('DoctrineMigrations', sys_get_temp_dir()); + + $conn = $this->createMock(Connection::class); + $connLoader = new ExistingConnection($conn); + + $dependencyFactory = DependencyFactory::fromConnection( + new ExistingConfiguration($configuration), + $connLoader, + ); + + $command = new class ($dependencyFactory) extends DoctrineCommand + { + protected function configure(): void + { + parent::configure(); + + $this + ->addOption( + 'namespace', + null, + InputOption::VALUE_REQUIRED, + 'The namespace to use for the migration (must be in the list of configured namespaces)', + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->getNamespace($input, $output); + + return DoctrineCommand::SUCCESS; + } + }; + + $commandTester = new CommandTester($command); + + $this->expectExceptionMessage('Path not defined for the namespace "Unknown"'); + $commandTester->execute( + ['--namespace' => 'Unknown'], + ['interactive' => false], + ); + } + + public function testNamespaceFirstFromDirectories(): void + { + $configuration = new Configuration(); + $configuration->addMigrationsDirectory('DoctrineMigrations', sys_get_temp_dir()); + + $conn = $this->createMock(Connection::class); + $connLoader = new ExistingConnection($conn); + + $dependencyFactory = DependencyFactory::fromConnection( + new ExistingConfiguration($configuration), + $connLoader, + ); + + $command = new class ($dependencyFactory) extends DoctrineCommand + { + protected function configure(): void + { + parent::configure(); + + $this + ->addOption( + 'namespace', + null, + InputOption::VALUE_REQUIRED, + 'The namespace to use for the migration (must be in the list of configured namespaces)', + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + DoctrineCommandTest::assertSame($this->getNamespace($input, $output), 'DoctrineMigrations'); + + return DoctrineCommand::SUCCESS; + } + }; + + $commandTester = new CommandTester($command); + $commandTester->execute([]); + } + + public function testNamespaceChoiceTwoDirectories(): void + { + $configuration = new Configuration(); + $configuration->addMigrationsDirectory('DoctrineMigrationsFirst', sys_get_temp_dir()); + $configuration->addMigrationsDirectory('DoctrineMigrationsTwo', sys_get_temp_dir()); + + $conn = $this->createMock(Connection::class); + $connLoader = new ExistingConnection($conn); + + $dependencyFactory = DependencyFactory::fromConnection( + new ExistingConfiguration($configuration), + $connLoader, + ); + + $command = new class ($dependencyFactory) extends DoctrineCommand + { + protected function configure(): void + { + parent::configure(); + + $this + ->addOption( + 'namespace', + null, + InputOption::VALUE_REQUIRED, + 'The namespace to use for the migration (must be in the list of configured namespaces)', + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + DoctrineCommandTest::assertSame($this->getNamespace($input, $output), 'DoctrineMigrationsTwo'); + + return DoctrineCommand::SUCCESS; + } + }; + + $command->setHelperSet(new HelperSet(['question' => new QuestionHelper()])); + + $commandTester = new CommandTester($command); + $commandTester->setInputs([1]); + $commandTester->execute([]); + } } diff --git a/tests/Tools/Console/Command/DumpSchemaCommandTest.php b/tests/Tools/Console/Command/DumpSchemaCommandTest.php index e1d682c65..b37fca641 100644 --- a/tests/Tools/Console/Command/DumpSchemaCommandTest.php +++ b/tests/Tools/Console/Command/DumpSchemaCommandTest.php @@ -18,10 +18,13 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use RuntimeException; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Tester\CommandTester; use function array_map; use function explode; +use function sprintf; use function sys_get_temp_dir; use function trim; @@ -62,6 +65,16 @@ public function testExecute(): void ->method('getMigrations') ->willReturn(new AvailableMigrationsSet([])); + $classNameGenerator = $this->createMock(ClassNameGenerator::class); + $classNameGenerator + ->method('generateClassName') + ->with('FooNs') + ->willReturn('FooNs\\Version1234'); + + $this->dependencyFactory->expects(self::any()) + ->method('getClassNameGenerator') + ->willReturn($classNameGenerator); + $this->schemaDumper->expects(self::once()) ->method('dump') ->with('FooNs\\Version1234', ['/foo/'], true, 80); @@ -88,6 +101,40 @@ public function testExecute(): void ); } + /** @return array> */ + public static function getNamespaceSelected(): array + { + return [ + 'no' => [null, 'FooNs'], + 'first' => [0, 'FooNs'], + 'two' => [1, 'FooNs2'], + ]; + } + + /** @dataProvider getNamespaceSelected */ + public function testExecuteWithMultipleDirectories(int|null $input, string $namespace): void + { + $this->migrationRepository->expects(self::once()) + ->method('getMigrations') + ->willReturn(new AvailableMigrationsSet([])); + + $this->configuration->addMigrationsDirectory('FooNs2', sys_get_temp_dir()); + + $this->dumpSchemaCommand->setHelperSet(new HelperSet(['question' => new QuestionHelper()])); + + $this->schemaDumper->expects(self::once())->method('dump'); + + $this->dumpSchemaCommandTester->setInputs([$input]); + $this->dumpSchemaCommandTester->execute([]); + + $output = $this->dumpSchemaCommandTester->getDisplay(true); + + self::assertStringContainsString('Please choose a namespace (defaults to the first one)', $output); + self::assertStringContainsString('[0] FooNs', $output); + self::assertStringContainsString('[1] FooNs2', $output); + self::assertStringContainsString(sprintf('You have selected the "%s" namespace', $namespace), $output); + } + protected function setUp(): void { $this->configuration = new Configuration(); @@ -97,16 +144,6 @@ protected function setUp(): void $this->migrationRepository = $this->createMock(FilesystemMigrationsRepository::class); $this->schemaDumper = $this->createMock(SchemaDumper::class); - $classNameGenerator = $this->createMock(ClassNameGenerator::class); - $classNameGenerator->expects(self::any()) - ->method('generateClassName') - ->with('FooNs') - ->willReturn('FooNs\\Version1234'); - - $this->dependencyFactory->expects(self::any()) - ->method('getClassNameGenerator') - ->willReturn($classNameGenerator); - $this->dependencyFactory->expects(self::any()) ->method('getSchemaDumper') ->willReturn($this->schemaDumper); diff --git a/tests/Tools/Console/Command/GenerateCommandTest.php b/tests/Tools/Console/Command/GenerateCommandTest.php index 6f8a05a01..27cba6a37 100644 --- a/tests/Tools/Console/Command/GenerateCommandTest.php +++ b/tests/Tools/Console/Command/GenerateCommandTest.php @@ -11,10 +11,13 @@ use Doctrine\Migrations\Tools\Console\Command\GenerateCommand; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\Helper\HelperSet; +use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Tester\CommandTester; use function array_map; use function explode; +use function sprintf; use function sys_get_temp_dir; use function trim; @@ -39,6 +42,16 @@ public function testExecute(): void ->with('FooNs\\Version1234') ->willReturn('/path/to/migration.php'); + $classNameGenerator = $this->createMock(ClassNameGenerator::class); + $classNameGenerator->expects(self::once()) + ->method('generateClassName') + ->with('FooNs') + ->willReturn('FooNs\\Version1234'); + + $this->dependencyFactory->expects(self::once()) + ->method('getClassNameGenerator') + ->willReturn($classNameGenerator); + $this->generateCommandTest->execute([]); $output = $this->generateCommandTest->getDisplay(true); @@ -51,6 +64,34 @@ public function testExecute(): void ], array_map(trim(...), explode("\n", trim($output)))); } + /** @return array> */ + public static function getNamespaceSelected(): array + { + return [ + 'no' => [null, 'FooNs'], + 'first' => [0, 'FooNs'], + 'two' => [1, 'FooNs2'], + ]; + } + + /** @dataProvider getNamespaceSelected */ + public function testExecuteWithMultipleDirectories(int|null $input, string $namespace): void + { + $this->configuration->addMigrationsDirectory('FooNs2', sys_get_temp_dir()); + + $this->generateCommand->setHelperSet(new HelperSet(['question' => new QuestionHelper()])); + + $this->generateCommandTest->setInputs([$input]); + $this->generateCommandTest->execute([]); + + $output = $this->generateCommandTest->getDisplay(true); + + self::assertStringContainsString('Please choose a namespace (defaults to the first one)', $output); + self::assertStringContainsString('[0] FooNs', $output); + self::assertStringContainsString('[1] FooNs2', $output); + self::assertStringContainsString(sprintf('You have selected the "%s" namespace', $namespace), $output); + } + protected function setUp(): void { $this->configuration = new Configuration(); @@ -59,16 +100,6 @@ protected function setUp(): void $this->dependencyFactory = $this->createMock(DependencyFactory::class); $this->migrationGenerator = $this->createMock(Generator::class); - $classNameGenerator = $this->createMock(ClassNameGenerator::class); - $classNameGenerator->expects(self::once()) - ->method('generateClassName') - ->with('FooNs') - ->willReturn('FooNs\\Version1234'); - - $this->dependencyFactory->expects(self::once()) - ->method('getClassNameGenerator') - ->willReturn($classNameGenerator); - $this->dependencyFactory->expects(self::any()) ->method('getConfiguration') ->willReturn($this->configuration); From 2c74be666865d0cb8d9af2c823be88aa715222b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 5 May 2024 20:01:24 +0200 Subject: [PATCH 2/7] Upgrade codecov/codecov-action --- .github/workflows/continuous-integration.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 18fc9eb19..6cfe460dd 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -97,6 +97,8 @@ jobs: path: "reports" - name: "Upload to Codecov" - uses: "codecov/codecov-action@v3" + uses: "codecov/codecov-action@v4" with: directory: reports + env: + CODECOV_TOKEN: "${{ secrets.CODECOV_TOKEN }}" From 240fbf9d7387e33615693f3722c4247c10720bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Sun, 5 May 2024 20:01:41 +0200 Subject: [PATCH 3/7] Setup Dependabot for Github actions --- .github/dependabot.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..15bd17299 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + labels: + - "CI" From c65bc170c05843178fa4052019a52f86c1893847 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 May 2024 20:44:13 +0200 Subject: [PATCH 4/7] Bump ramsey/composer-install from 2 to 3 (#1428) Bumps [ramsey/composer-install](https://github.com/ramsey/composer-install) from 2 to 3. - [Release notes](https://github.com/ramsey/composer-install/releases) - [Commits](https://github.com/ramsey/composer-install/compare/v2...v3) --- updated-dependencies: - dependency-name: ramsey/composer-install dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/continuous-integration.yml | 2 +- .github/workflows/static-analysis.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 6cfe460dd..fb9d5d20c 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -65,7 +65,7 @@ jobs: run: "composer config minimum-stability dev" - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: dependency-versions: "${{ matrix.dependencies }}" composer-options: "--ignore-platform-req=php+" diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 91f70da4c..8f99f95f2 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -43,7 +43,7 @@ jobs: extensions: "pdo_sqlite" - name: "Install dependencies with Composer" - uses: "ramsey/composer-install@v2" + uses: "ramsey/composer-install@v3" with: dependency-versions: "${{ matrix.dependencies }}" From 704de5e6e470a8031aadee7a242913a3222db6d5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 5 May 2024 20:44:37 +0200 Subject: [PATCH 5/7] Bump doctrine/.github from 4.0.0 to 5.0.1 (#1429) Bumps [doctrine/.github](https://github.com/doctrine/.github) from 4.0.0 to 5.0.1. - [Release notes](https://github.com/doctrine/.github/releases) - [Commits](https://github.com/doctrine/.github/compare/4.0.0...5.0.1) --- updated-dependencies: - dependency-name: doctrine/.github dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/coding-standards.yml | 2 +- .github/workflows/composer-lint.yml | 2 +- .github/workflows/release-on-milestone-closed.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 278be295d..93e19e921 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -26,4 +26,4 @@ on: jobs: coding-standards: name: "Coding Standards" - uses: "doctrine/.github/.github/workflows/coding-standards.yml@4.0.0" + uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.0.1" diff --git a/.github/workflows/composer-lint.yml b/.github/workflows/composer-lint.yml index 3707dcc02..d40d8a583 100644 --- a/.github/workflows/composer-lint.yml +++ b/.github/workflows/composer-lint.yml @@ -17,4 +17,4 @@ on: jobs: composer-lint: name: "Composer Lint" - uses: "doctrine/.github/.github/workflows/composer-lint.yml@4.0.0" + uses: "doctrine/.github/.github/workflows/composer-lint.yml@5.0.1" diff --git a/.github/workflows/release-on-milestone-closed.yml b/.github/workflows/release-on-milestone-closed.yml index 34abfbcfd..3cac620a3 100644 --- a/.github/workflows/release-on-milestone-closed.yml +++ b/.github/workflows/release-on-milestone-closed.yml @@ -8,7 +8,7 @@ on: jobs: release: name: "Git tag, release & create merge-up PR" - uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@4.0.0" + uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.0.1" secrets: GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }} GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }} From 44d34d635ea05a72a7332df02a70087865624e80 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 23 May 2024 10:15:20 +0200 Subject: [PATCH 6/7] Freeze migrations before executing sql --- src/AbstractMigration.php | 12 ++++++++++++ src/Exception/FrozenMigration.php | 15 +++++++++++++++ src/Version/DbalExecutor.php | 2 ++ tests/AbstractMigrationTest.php | 10 ++++++++++ 4 files changed, 39 insertions(+) create mode 100644 src/Exception/FrozenMigration.php diff --git a/src/AbstractMigration.php b/src/AbstractMigration.php index 098eb620a..e13045c88 100644 --- a/src/AbstractMigration.php +++ b/src/AbstractMigration.php @@ -10,6 +10,7 @@ use Doctrine\DBAL\Schema\AbstractSchemaManager; use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\Exception\AbortMigration; +use Doctrine\Migrations\Exception\FrozenMigration; use Doctrine\Migrations\Exception\IrreversibleMigration; use Doctrine\Migrations\Exception\MigrationException; use Doctrine\Migrations\Exception\SkipMigration; @@ -36,6 +37,8 @@ abstract class AbstractMigration /** @var Query[] */ private array $plannedSql = []; + private bool $frozen = false; + public function __construct(Connection $connection, private readonly LoggerInterface $logger) { $this->connection = $connection; @@ -125,6 +128,10 @@ protected function addSql( array $params = [], array $types = [], ): void { + if ($this->frozen) { + throw FrozenMigration::new(); + } + $this->plannedSql[] = new Query($sql, $params, $types); } @@ -134,6 +141,11 @@ public function getSql(): array return $this->plannedSql; } + public function freeze(): void + { + $this->frozen = true; + } + protected function write(string $message): void { $this->logger->notice($message, ['migration' => $this]); diff --git a/src/Exception/FrozenMigration.php b/src/Exception/FrozenMigration.php new file mode 100644 index 000000000..45c0c1a91 --- /dev/null +++ b/src/Exception/FrozenMigration.php @@ -0,0 +1,15 @@ +addSql(new Query($sql)); } + $migration->freeze(); + if (count($this->sql) !== 0) { if (! $configuration->isDryRun()) { $this->executeResult($configuration); diff --git a/tests/AbstractMigrationTest.php b/tests/AbstractMigrationTest.php index 027511f76..24d952b58 100644 --- a/tests/AbstractMigrationTest.php +++ b/tests/AbstractMigrationTest.php @@ -6,6 +6,7 @@ use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\Exception\AbortMigration; +use Doctrine\Migrations\Exception\FrozenMigration; use Doctrine\Migrations\Exception\IrreversibleMigration; use Doctrine\Migrations\Exception\SkipMigration; use Doctrine\Migrations\Query\Query; @@ -47,6 +48,15 @@ public function testAddSql(): void self::assertEquals([new Query('SELECT 1', [1], [2])], $this->migration->getSql()); } + public function testThrowFrozenMigrationException(): void + { + $this->expectException(FrozenMigration::class); + $this->expectExceptionMessage('The migration is frozen and cannot be edited anymore.'); + + $this->migration->freeze(); + $this->migration->exposedAddSql('SELECT 1', [1], [2]); + } + public function testWarnIfOutputMessage(): void { $this->migration->warnIf(true, 'Warning was thrown'); From b60aed2461d19e25654a1bc9c93ba2f165cf54b7 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Wed, 26 Jun 2024 10:33:58 +0200 Subject: [PATCH 7/7] PHPStan: Remove obsolete rule (#1437) --- phpstan.neon.dist | 3 --- 1 file changed, 3 deletions(-) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index e5dad18c0..2a4a5616b 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -20,9 +20,6 @@ parameters: - message: '~^Call to an undefined method Symfony\\Component\\Console\\Output\\OutputInterface\:\:getErrorOutput\(\)\.$~' path: lib/Doctrine/Migrations/Tools/Console/ConsoleLogger.php - - - message: '~^Method Doctrine\\Migrations\\Tests\\Stub\\DoctrineRegistry::getService\(\) should return Doctrine\\Persistence\\ObjectManager but returns Doctrine\\DBAL\\Connection\|Doctrine\\ORM\\EntityManager~' - path: tests/Doctrine/Migrations/Tests/Stub/DoctrineRegistry.php # https://github.com/phpstan/phpstan/issues/5982 -