diff --git a/README.md b/README.md index bd6e266d..85a80f7d 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,10 @@ bin/cake migrations status -c my_datasource # The following will mark targeted migration as marked without actually running it. # The expected argument is the migration version number bin/cake migrations mark_migrated 20150417223600 + +# Since Migrations 1.3.1, a new `all` special value for the version argumentwas added. +# The following will mark all migrations found as migrated. +bin/cake migrations mark_migrated all ``` ### Creating Migrations diff --git a/src/Command/MarkMigrated.php b/src/Command/MarkMigrated.php index 81932144..99194067 100644 --- a/src/Command/MarkMigrated.php +++ b/src/Command/MarkMigrated.php @@ -22,6 +22,25 @@ class MarkMigrated extends AbstractCommand use ConfigurationTrait; + /** + * The console output instance + * + * @var \Symfony\Component\Console\Output\OutputInterface + */ + protected $output; + + /** + * @param \Symfony\Component\Console\Output\OutputInterface $output + * @return mixed + */ + public function output(OutputInterface $output = null) + { + if ($output !== null) { + $this->output = $output; + } + return $this->output; + } + /** * {@inheritdoc} */ @@ -29,7 +48,11 @@ protected function configure() { $this->setName('mark_migrated') ->setDescription('Mark a migration as migrated') - ->addArgument('version', InputArgument::REQUIRED, 'What is the version of the migration?') + ->addArgument( + 'version', + InputArgument::REQUIRED, + 'What is the version of the migration? Use the special value `all` to mark all migrations as migrated.' + ) ->setHelp(sprintf( '%sMark a migration migrated based on its version number%s', PHP_EOL, @@ -42,6 +65,8 @@ protected function configure() /** * Mark a migration migrated + * If the `version` argument has the value `all`, all migrations found will be marked as + * migrated * * @param \Symfony\Component\Console\Input\InputInterface $input the input object * @param \Symfony\Component\Console\Output\OutputInterface $output the output object @@ -51,10 +76,16 @@ protected function execute(InputInterface $input, OutputInterface $output) { $this->setInput($input); $this->bootstrap($input, $output); + $this->output($output); $path = $this->getConfig()->getMigrationPath(); $version = $input->getArgument('version'); + if ($version === 'all' || $version === '*') { + $this->markAllMigrated($path); + return; + } + if ($this->getManager()->isMigrated($version)) { $output->writeln( sprintf( @@ -72,4 +103,51 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln(sprintf('An error occurred : %s', $e->getMessage())); } } + + /** + * Mark all migrations found in $path as migrated + * + * It will start a transaction and rollback in case one of the operation raises an exception + * + * @param string $path Path where to look for migrations + * @return void + */ + protected function markAllMigrated($path) + { + $manager = $this->getManager(); + $adapter = $manager->getEnvironment('default')->getAdapter(); + $migrations = $manager->getMigrations(); + $output = $this->output(); + + if (empty($migrations)) { + $output->writeln('No migrations were found. Nothing to mark as migrated.'); + return; + } + + $adapter->beginTransaction(); + foreach ($migrations as $version => $migration) { + if ($manager->isMigrated($version)) { + $output->writeln(sprintf('Skipping migration `%s` (already migrated).', $version)); + } else { + try { + $this->getManager()->markMigrated($version, $path); + $output->writeln( + sprintf('Migration `%s` successfully marked migrated !', $version) + ); + } catch (\Exception $e) { + $adapter->rollbackTransaction(); + $output->writeln( + sprintf( + 'An error occurred while marking migration `%s` as migrated : %s', + $version, + $e->getMessage() + ) + ); + $output->writeln('All marked migrations during this process were unmarked.'); + return; + } + } + } + $adapter->commitTransaction(); + } } diff --git a/tests/TestCase/Command/MarkMigratedTest.php b/tests/TestCase/Command/MarkMigratedTest.php index 24f9de8c..ad6531de 100644 --- a/tests/TestCase/Command/MarkMigratedTest.php +++ b/tests/TestCase/Command/MarkMigratedTest.php @@ -14,6 +14,7 @@ use Cake\Datasource\ConnectionManager; use Cake\TestSuite\TestCase; use Migrations\MigrationsDispatcher; +use Symfony\Component\Console\Output\StreamOutput; use Symfony\Component\Console\Tester\CommandTester; /** @@ -133,4 +134,92 @@ public function testExecute() $result = $this->Connection->newQuery()->select(['*'])->from('phinxlog')->execute()->count(); $this->assertEquals(1, $result); } + + /** + * Test executing "mark_migration" + * + * @return void + */ + public function testExecuteAll() + { + $this->commandTester->execute([ + 'command' => $this->command->getName(), + 'version' => 'all', + '--connection' => 'test', + '--source' => 'TestsMigrations' + ]); + + $this->assertContains( + 'Migration `20150826191400` successfully marked migrated !', + $this->commandTester->getDisplay() + ); + $this->assertContains( + 'Migration `20150724233100` successfully marked migrated !', + $this->commandTester->getDisplay() + ); + $this->assertContains( + 'Migration `20150704160200` successfully marked migrated !', + $this->commandTester->getDisplay() + ); + + $result = $this->Connection->newQuery()->select(['*'])->from('phinxlog')->execute()->fetchAll('assoc'); + $this->assertEquals('20150704160200', $result[0]['version']); + $this->assertEquals('20150724233100', $result[1]['version']); + $this->assertEquals('20150826191400', $result[2]['version']); + + $this->commandTester->execute([ + 'command' => $this->command->getName(), + 'version' => 'all', + '--connection' => 'test', + '--source' => 'TestsMigrations' + ]); + + $this->assertContains( + 'Skipping migration `20150704160200` (already migrated).', + $this->commandTester->getDisplay() + ); + $this->assertContains( + 'Skipping migration `20150724233100` (already migrated).', + $this->commandTester->getDisplay() + ); + $this->assertContains( + 'Skipping migration `20150826191400` (already migrated).', + $this->commandTester->getDisplay() + ); + + $config = $this->command->getConfig(); + $env = $this->command->getManager()->getEnvironment('default'); + $migrations = $this->command->getManager()->getMigrations(); + + $manager = $this->getMock( + '\Migrations\CakeManager', + ['getEnvironment', 'markMigrated'], + [$config, new StreamOutput(fopen('php://memory', 'a', false))] + ); + + $manager->expects($this->any()) + ->method('getEnvironment')->will($this->returnValue($env)); + $manager->expects($this->any()) + ->method('getMigrations')->will($this->returnValue($migrations)); + $manager + ->method('markMigrated')->will($this->throwException(new \Exception('Error during marking process'))); + + $this->Connection->execute('DELETE FROM phinxlog'); + + $application = new MigrationsDispatcher('testing'); + $buggyCommand = $application->find('mark_migrated'); + $buggyCommand->setManager($manager); + $buggyCommandTester = new CommandTester($buggyCommand); + $buggyCommandTester->execute([ + 'command' => $this->command->getName(), + 'version' => 'all', + '--connection' => 'test', + '--source' => 'TestsMigrations' + ]); + + $this->assertContains( + 'An error occurred while marking migration `20150704160200` as migrated : Error during marking process', + $buggyCommandTester->getDisplay() + ); + } } diff --git a/tests/TestCase/MigrationsTest.php b/tests/TestCase/MigrationsTest.php index 51d31fd7..e014c139 100644 --- a/tests/TestCase/MigrationsTest.php +++ b/tests/TestCase/MigrationsTest.php @@ -208,8 +208,7 @@ public function testMigrateErrors() */ public function testRollbackErrors() { - $this->migrations->markMigrated(20150704160200); - $this->migrations->markMigrated(20150724233100); + $this->migrations->markMigrated('all'); $this->migrations->rollback(); }