Skip to content
Merged
3 changes: 1 addition & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@
},
"autoload-dev": {
"psr-4": {
"GT\\Database\\Test\\": "./test/phpunit",
"Gt\\Database\\Test\\": "./test/phpunit"
"GT\\Database\\Test\\": "./test/phpunit"
}
},

Expand Down
132 changes: 69 additions & 63 deletions composer.lock

Large diffs are not rendered by default.

145 changes: 138 additions & 7 deletions src/Cli/ExecuteCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
use Gt\Cli\Argument\ArgumentValueList;
use Gt\Cli\Command\Command;
use Gt\Cli\Parameter\Parameter;
use Gt\Config\Config;
use Gt\Config\ConfigFactory;
use GT\Database\Connection\Settings;
use GT\Database\Migration\DevMigrator;
use GT\Database\Migration\MigrationIntegrityException;
use GT\Database\Migration\Migrator;
use GT\Database\StatementExecutionException;
Expand All @@ -19,6 +21,7 @@ public function run(?ArgumentValueList $arguments = null):int {

$settings = $this->buildSettingsFromConfig($config, $repoBasePath, $arguments);
[$migrationPath, $migrationTable] = $this->getMigrationLocation($config, $repoBasePath, $arguments);
[$devMigrationPath, $devMigrationTable] = $this->getDevMigrationLocation($config, $repoBasePath, $arguments);

$migrator = new Migrator($settings, $migrationPath, $migrationTable);
$migrator->setOutput(
Expand All @@ -32,12 +35,22 @@ public function run(?ArgumentValueList $arguments = null):int {

$migrator->selectSchema();
$migrator->createMigrationTable();
$migrationCount = $migrator->getMigrationCount();
$migrationFileList = $migrator->getMigrationFileList();
$migrationCount = $migrator->getContiguousCompletedMigrationCount($migrationFileList);

$runFrom = $this->calculateResetNumber($arguments, $migrationFileList, $migrator, $migrationCount);

$this->executeMigrations($migrator, $migrationFileList, $runFrom);

if($this->isDevMerge($arguments)) {
$this->mergeDevMigrations($settings, $migrator, $migrationPath, $devMigrationPath, $devMigrationTable);
return 0;
}

if($this->isDev($arguments)) {
$this->executeDevMigrations($settings, $devMigrationPath, $devMigrationTable);
}

return 0;
}

Expand All @@ -46,9 +59,17 @@ private function isForced(?ArgumentValueList $arguments):bool {
return $arguments?->contains("force") ?? false;
}

private function isDev(?ArgumentValueList $arguments):bool {
return $arguments?->contains("dev") ?? false;
}

private function isDevMerge(?ArgumentValueList $arguments):bool {
return $arguments?->contains("dev-merge") ?? false;
}

/** Build Settings from config for the current repository. */
protected function buildSettingsFromConfig(
\Gt\Config\Config $config,
Config $config,
string $repoBasePath,
?ArgumentValueList $arguments = null
): Settings {
Expand Down Expand Up @@ -111,7 +132,7 @@ protected function buildSettingsFromConfig(
* @return list<string>
*/
protected function getMigrationLocation(
\Gt\Config\Config $config,
Config $config,
string $repoBasePath,
?ArgumentValueList $arguments = null
): array {
Expand All @@ -130,6 +151,34 @@ protected function getMigrationLocation(
return [$migrationPath, $migrationTable];
}

/**
* Return [devMigrationPath, devMigrationTable] derived from config.
*
* @return list<string>
*/
protected function getDevMigrationLocation(
Config $config,
string $repoBasePath,
?ArgumentValueList $arguments = null
): array {
$queryPath = $this->getOverrideOrConfigValue(
$config,
$arguments,
"base-directory",
"database.query_path",
"query"
);
$devMigrationPath = implode(DIRECTORY_SEPARATOR, [
$this->resolvePath($repoBasePath, $queryPath),
$config->get("database.dev_migration_path") ?? implode(DIRECTORY_SEPARATOR, [
"_migration",
"dev",
]),
]);
$devMigrationTable = $config->get("database.dev_migration_table") ?? "_migration_dev";
return [$devMigrationPath, $devMigrationTable];
}

/**
* Calculate the migration start point from --reset or current migration count.
*
Expand Down Expand Up @@ -159,7 +208,11 @@ private function calculateResetNumber(
*
* @param list<string> $migrationFileList
*/
private function executeMigrations(Migrator $migrator, array $migrationFileList, int $runFrom): void {
private function executeMigrations(
Migrator $migrator,
array $migrationFileList,
int $runFrom,
): void {
try {
$migrator->checkIntegrity($migrationFileList, $runFrom);
$migrator->performMigration($migrationFileList, $runFrom);
Expand All @@ -181,6 +234,72 @@ private function executeMigrations(Migrator $migrator, array $migrationFileList,
}
}

private function executeDevMigrations(
Settings $settings,
string $devMigrationPath,
string $devMigrationTable
): void {
$devMigrator = new DevMigrator(
$settings,
$devMigrationPath,
$devMigrationTable,
);
$devMigrator->setOutput(
$this->stream->getOutStream(),
$this->stream->getErrorStream()
);

$devMigrator->createMigrationTable();
$devMigrationFileList = $devMigrator->getMigrationFileList();

try {
$devMigrator->checkFileListOrder($devMigrationFileList);
$devMigrator->checkIntegrity($devMigrationFileList);
$devMigrator->performMigration($devMigrationFileList);
}
catch(MigrationIntegrityException $exception) {
$this->writeLine(
"There was an integrity error migrating dev file '"
. $exception->getMessage()
. "' - this dev migration is recorded to have been run already, "
. "but the contents of the file has changed."
);
}
catch(StatementPreparationException|StatementExecutionException $exception) {
$this->writeLine(
"There was an error executing dev migration file: "
. $exception->getMessage()
. "'"
);
}
}

private function mergeDevMigrations(
Settings $settings,
Migrator $migrator,
string $migrationPath,
string $devMigrationPath,
string $devMigrationTable
): void {
$devMigrator = new DevMigrator($settings, $devMigrationPath, $devMigrationTable);
$devMigrator->setOutput(
$this->stream->getOutStream(),
$this->stream->getErrorStream()
);
$devMigrator->createMigrationTable();

try {
$devMigrator->mergeIntoMainMigrationDirectory($migrator, $migrationPath);
}
catch(MigrationIntegrityException $exception) {
$this->writeLine(
"There was an integrity error merging dev migration file '"
. $exception->getMessage()
. "' - ensure the dev migration has already been run and not edited since."
);
}
}

public function getName():string {
return "execute";
}
Expand Down Expand Up @@ -245,6 +364,18 @@ public function getOptionalParameterList():array {
null,
"Override database.password for this command"
),
new Parameter(
false,
"dev",
null,
"Run branch-local migrations from the dev migration directory"
),
new Parameter(
false,
"dev-merge",
null,
"Promote branch-local dev migrations into canonical migrations"
),
new Parameter(
false,
"force",
Expand Down Expand Up @@ -281,9 +412,9 @@ private function getDefaultPath(string $repoBasePath):?string {
/**
* @param bool|string $repoBasePath
* @param string|null $defaultPath
* @return \Gt\Config\Config
* @return Config
*/
protected function getConfig(bool|string $repoBasePath, ?string $defaultPath):\Gt\Config\Config {
protected function getConfig(bool|string $repoBasePath, ?string $defaultPath):Config {
$config = ConfigFactory::createForProject($repoBasePath);

$default = $defaultPath
Expand All @@ -297,7 +428,7 @@ protected function getConfig(bool|string $repoBasePath, ?string $defaultPath):\G
}

protected function getOverrideOrConfigValue(
\Gt\Config\Config $config,
Config $config,
?ArgumentValueList $arguments,
string $argumentKey,
string $configKey,
Expand Down
Loading
Loading