Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to migrate all migrations with one "mark_migrated" call #125

Merged
merged 4 commits into from
Sep 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
80 changes: 79 additions & 1 deletion src/Command/MarkMigrated.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,37 @@ 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}
*/
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,
Expand All @@ -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
Expand All @@ -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(
Expand All @@ -72,4 +103,51 @@ protected function execute(InputInterface $input, OutputInterface $output)
$output->writeln(sprintf('<error>An error occurred : %s</error>', $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('<info>No migrations were found. Nothing to mark as migrated.</info>');
return;
}

$adapter->beginTransaction();
foreach ($migrations as $version => $migration) {
if ($manager->isMigrated($version)) {
$output->writeln(sprintf('<info>Skipping migration `%s` (already migrated).</info>', $version));
} else {
try {
$this->getManager()->markMigrated($version, $path);
$output->writeln(
sprintf('<info>Migration `%s` successfully marked migrated !</info>', $version)
);
} catch (\Exception $e) {
$adapter->rollbackTransaction();
$output->writeln(
sprintf(
'<error>An error occurred while marking migration `%s` as migrated : %s</error>',
$version,
$e->getMessage()
)
);
$output->writeln('<error>All marked migrations during this process were unmarked.</error>');
return;
}
}
}
$adapter->commitTransaction();
}
}
89 changes: 89 additions & 0 deletions tests/TestCase/Command/MarkMigratedTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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()
);
}
}
3 changes: 1 addition & 2 deletions tests/TestCase/MigrationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down