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

Add Mark migrated command #700

Merged
merged 4 commits into from
Mar 27, 2024
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
142 changes: 142 additions & 0 deletions src/Command/MarkMigratedCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
<?php
declare(strict_types=1);

/**
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* @link https://cakephp.org CakePHP(tm) Project
* @license https://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Migrations\Command;

use Cake\Command\Command;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Console\ConsoleOptionParser;
use InvalidArgumentException;
use Migrations\Config\ConfigInterface;
use Migrations\Migration\ManagerFactory;

/**
* MarkMigrated command marks migrations as run when they haven't been.
*/
class MarkMigratedCommand extends Command
{
/**
* The default name added to the application command list
*
* @return string
*/
public static function defaultName(): string
{
return 'migrations mark_migrated';
}

/**
* Configure the option parser
*
* @param \Cake\Console\ConsoleOptionParser $parser The option parser to configure
* @return \Cake\Console\ConsoleOptionParser
*/
public function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser
{
$parser->setDescription([
'Mark a migration as applied',
'',
'Can mark one or more migrations as applied without applying the changes in the migration.',
'',
'<info>migrations mark_migrated --connection secondary</info>',
'Mark all migrations as applied',
'',
'<info>migrations mark_migrated --connection secondary --target 003</info>',
'mark migrations as applied up to the 003',
'',
'<info>migrations mark_migrated --target 003 --only</info>',
'mark only 003 as applied.',
'',
'<info>migrations mark_migrated --target 003 --exclude</info>',
'mark up to 003, but not 003 as applied.',
])->addOption('plugin', [
'short' => 'p',
'help' => 'The plugin to mark migrations for',
])->addOption('connection', [
'short' => 'c',
'help' => 'The datasource connection to use',
'default' => 'default',
])->addOption('source', [
'short' => 's',
'default' => ConfigInterface::DEFAULT_MIGRATION_FOLDER,
'help' => 'The folder where your migrations are',
])->addOption('target', [
'short' => 't',
'help' => 'Migrations from the beginning to the provided version will be marked as applied.',
])->addOption('only', [
'short' => 'o',
'help' => 'If present, only the target migration will be marked as applied.',
'boolean' => true,
])->addOption('exclude', [
'short' => 'x',
'help' => 'If present, migrations from the beginning until the target version but not including the target will be marked as applied.',
'boolean' => true,
]);

return $parser;
}

/**
* Checks for an invalid use of `--exclude` or `--only`
*
* @param \Cake\Console\Arguments $args The console arguments
* @return bool Returns true when it is an invalid use of `--exclude` or `--only` otherwise false
*/
protected function invalidOnlyOrExclude(Arguments $args): bool
{
return ($args->getOption('exclude') && $args->getOption('only')) ||
($args->getOption('exclude') || $args->getOption('only')) &&
$args->getOption('target') === null;
}

/**
* Execute the command.
*
* @param \Cake\Console\Arguments $args The command arguments.
* @param \Cake\Console\ConsoleIo $io The console io
* @return int|null The exit code or null for success
*/
public function execute(Arguments $args, ConsoleIo $io): ?int
{
$factory = new ManagerFactory([
'plugin' => $args->getOption('plugin'),
'source' => $args->getOption('source'),
'connection' => $args->getOption('connection'),
]);
$manager = $factory->createManager($io);
$config = $manager->getConfig();
$migrationPaths = $config->getMigrationPaths();
$path = array_pop($migrationPaths);

if ($this->invalidOnlyOrExclude($args)) {
$io->err(
'<error>You should use `--exclude` OR `--only` (not both) along with a `--target` !</error>'
);

return self::CODE_ERROR;
}

try {
$versions = $manager->getVersionsToMark($args);
} catch (InvalidArgumentException $e) {
$io->err(sprintf('<error>%s</error>', $e->getMessage()));

return self::CODE_ERROR;
}

$manager->markVersionsAsMigrated($path, $versions, $io);

return self::CODE_SUCCESS;
}
}
33 changes: 15 additions & 18 deletions src/Migration/Manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

namespace Migrations\Migration;

use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use DateTime;
use Exception;
Expand All @@ -22,8 +23,6 @@
use Psr\Container\ContainerInterface;
use RuntimeException;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Manager
{
Expand Down Expand Up @@ -291,36 +290,36 @@
/**
* Decides which versions it should mark as migrated
*
* @param \Symfony\Component\Console\Input\InputInterface $input Input interface from which argument and options
* will be extracted to determine which versions to be marked as migrated
* @param \Cake\Console\Arguments $args Console arguments will be extracted
* to determine which versions to be marked as migrated
* @return array<int> Array of versions that should be marked as migrated
* @throws \InvalidArgumentException If the `--exclude` or `--only` options are used without `--target`
* or version not found
*/
public function getVersionsToMark(InputInterface $input): array
public function getVersionsToMark(Arguments $args): array
{
$migrations = $this->getMigrations();
$versions = array_keys($migrations);

// TODO use console arguments
$versionArg = $input->getArgument('version');
$targetArg = $input->getOption('target');
$versionArg = $args->getArgument('version');
$targetArg = $args->getOption('target');
$hasAllVersion = in_array($versionArg, ['all', '*'], true);
if ((empty($versionArg) && empty($targetArg)) || $hasAllVersion) {
return $versions;
}

$version = (int)$targetArg ?: (int)$versionArg;

if ($input->getOption('only') || !empty($versionArg)) {
if ($args->getOption('only') || !empty($versionArg)) {
if (!in_array($version, $versions)) {
throw new InvalidArgumentException("Migration `$version` was not found !");
}

return [$version];
}

$lengthIncrease = $input->getOption('exclude') ? 0 : 1;
$lengthIncrease = $args->getOption('exclude') ? 0 : 1;
$index = array_search($version, $versions);

if ($index === false) {
Expand All @@ -337,43 +336,41 @@
*
* @param string $path Path where to look for migrations
* @param array<int> $versions Versions which should be marked
* @param \Symfony\Component\Console\Output\OutputInterface $output OutputInterface used to store
* the command output
* @param \Cake\Console\ConsoleIo $io ConsoleIo to write output too
* @return void
*/
public function markVersionsAsMigrated(string $path, array $versions, OutputInterface $output): void
public function markVersionsAsMigrated(string $path, array $versions, ConsoleIo $io): void
{
// TODO fix output interface usage here
$adapter = $this->getEnvironment()->getAdapter();

if (!$versions) {
$output->writeln('<info>No migrations were found. Nothing to mark as migrated.</info>');
$io->out('<info>No migrations were found. Nothing to mark as migrated.</info>');

Check warning on line 347 in src/Migration/Manager.php

View check run for this annotation

Codecov / codecov/patch

src/Migration/Manager.php#L347

Added line #L347 was not covered by tests

return;
}

$adapter->beginTransaction();
foreach ($versions as $version) {
if ($this->isMigrated($version)) {
$output->writeln(sprintf('<info>Skipping migration `%s` (already migrated).</info>', $version));
$io->out(sprintf('<info>Skipping migration `%s` (already migrated).</info>', $version));
continue;
}

try {
$this->markMigrated($version, $path);
$output->writeln(
$io->out(
sprintf('<info>Migration `%s` successfully marked migrated !</info>', $version)
);
} catch (Exception $e) {
$adapter->rollbackTransaction();
$output->writeln(
$io->out(

Check warning on line 366 in src/Migration/Manager.php

View check run for this annotation

Codecov / codecov/patch

src/Migration/Manager.php#L366

Added line #L366 was not covered by tests
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>');
$io->out('<error>All marked migrations during this process were unmarked.</error>');

Check warning on line 373 in src/Migration/Manager.php

View check run for this annotation

Codecov / codecov/patch

src/Migration/Manager.php#L373

Added line #L373 was not covered by tests

return;
}
Expand Down
2 changes: 2 additions & 0 deletions src/MigrationsPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
use Migrations\Command\BakeMigrationSnapshotCommand;
use Migrations\Command\BakeSeedCommand;
use Migrations\Command\DumpCommand;
use Migrations\Command\MarkMigratedCommand;
use Migrations\Command\MigrateCommand;
use Migrations\Command\MigrationsCacheBuildCommand;
use Migrations\Command\MigrationsCacheClearCommand;
Expand Down Expand Up @@ -94,6 +95,7 @@ public function console(CommandCollection $commands): CommandCollection
if (Configure::read('Migrations.backend') == 'builtin') {
$classes = [
StatusCommand::class,
MarkMigratedCommand::class,
MigrateCommand::class,
DumpCommand::class,
];
Expand Down
2 changes: 1 addition & 1 deletion tests/TestCase/Command/CompletionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function testMigrationsSubcommands()
{
$this->exec('completion subcommands migrations.migrations');
$expected = [
'dump migrate orm-cache-build orm-cache-clear create mark_migrated rollback seed status',
'dump mark_migrated migrate orm-cache-build orm-cache-clear create rollback seed status',
];
$actual = $this->_out->messages();
$this->assertEquals($expected, $actual);
Expand Down
Loading
Loading