Created
July 3, 2020 12:04
-
-
Save mlocati/8d05331afee8ff4cb810547c2061c0c7 to your computer and use it in GitHub Desktop.
Check concrete5 migration IDs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
set_error_handler( | |
static function ($code, $message, $file, $line): void | |
{ | |
throw new RuntimeException("{$message}\nFile: {$file}\nLine: {$line}"); | |
}, | |
-1 | |
); | |
function run(): int | |
{ | |
try { | |
$dir = $_SERVER['argv'][1] ?? null; | |
if ($dir === null) { | |
throw new RuntimeException("Symtax: {$_SERVER['argv'][0]} <concrete5 root git directory>"); | |
} | |
$dir = str_replace('/', DIRECTORY_SEPARATOR, $dir); | |
if (!is_file("{$dir}/.git/config")) { | |
throw new RuntimeException("Not a repo dir: {$dir}"); | |
} | |
$versions = listVersions($dir); | |
$versionInfos = []; | |
$previousVersionInfo = null; | |
foreach ($versions as $version) { | |
$versionInfo = parseVersion($dir, $version, $previousVersionInfo); | |
$previousVersionInfo = $versionInfo; | |
$versionInfo->inspect(); | |
} | |
} catch (Throwable $x) { | |
fwrite(STDERR, "{$x->getMessage()}\n"); | |
return 1; | |
} | |
return 0; | |
} | |
function listVersions(string $dir): array | |
{ | |
$lines = []; | |
$rc = -1; | |
exec('git -C ' . escapeshellarg($dir) . ' tag 2>&1', $lines, $rc); | |
if ($rc !== 0) { | |
throw new RuntimeException(implode("\n", $lines)); | |
} | |
$versions = []; | |
foreach ($lines as $line) { | |
if (preg_match('/^(\d+(?:\.\d+)+)$/', $line, $matches)) { | |
$versions[] = $matches[1]; | |
} | |
} | |
usort($versions, 'version_compare'); | |
return $versions; | |
} | |
class VersionInfo | |
{ | |
private $version; | |
private $versionDB; | |
private $migrationIDs; | |
private $previousVersionInfo; | |
public function __construct(string $version, string $versionDB, array $migrationIDs, ?self $previousVersionInfo) | |
{ | |
$this->version = $version; | |
$this->versionDB = $versionDB; | |
$this->migrationIDs = $migrationIDs; | |
$this->previousVersionInfo = $previousVersionInfo; | |
} | |
public function getVersion(): string | |
{ | |
return $this->version; | |
} | |
public function getVersionDB(): string | |
{ | |
return $this->versionDB; | |
} | |
public function getMigrationIDs(): array | |
{ | |
return $this->migrationIDs; | |
} | |
public function getLastMigrationDB(): string | |
{ | |
$migrationIDs = $this->getMigrationIDs(); | |
return $migrationIDs === [] ? '' : array_pop($migrationIDs); | |
} | |
public function getPreviousVersionInfo(): ?self | |
{ | |
return $this->previousVersionInfo; | |
} | |
public function inspect(): void | |
{ | |
echo "# Version: {$this->getVersion()}\n"; | |
if ($this->getVersionDB() === '') { | |
echo "- no migrations\n"; | |
return; | |
} | |
if ($this->getVersionDB() !== $this->getLastMigrationDB()) { | |
echo "- version_db: {$this->getVersionDB()}\n"; | |
echo "- last migration ID: {$this->getLastMigrationDB()}\n"; | |
echo "- ERROR: version_db doesn't match the last migration ID!!!!!!!!!!!\n"; | |
} else { | |
echo "- version_db: {$this->getVersionDB()}\n"; | |
} | |
$previousVersionInfo = $this->getPreviousVersionInfo(); | |
if ($previousVersionInfo === null) { | |
return; | |
} | |
if ($previousVersionInfo->getVersionDB() === '') { | |
return; | |
} | |
if ($this->getPreviousVersionInfo() < $previousVersionInfo->getVersionDB()) { | |
echo "- ERROR: version_db lower than the one of the previous version!!!!!!!!!!!\n"; | |
return; | |
} | |
$previousMigrations = $previousVersionInfo->getMigrationIDs(); | |
$newMigrations = array_diff($this->getMigrationIDs(), $previousVersionInfo->getMigrationIDs()); | |
$newWrongMigrations = array_filter( | |
$newMigrations, | |
static function (string $newMigrationID) use ($previousVersionInfo): bool | |
{ | |
return $newMigrationID <= $previousVersionInfo->getVersionDB(); | |
} | |
); | |
if ($newWrongMigrations !== []) { | |
echo "- ERROR!!!!!!! These migrations have an ID lower than the version_db of version {$previousVersionInfo->getVersion()}:\n - " . implode("\n - ", $newWrongMigrations) . "\n"; | |
} | |
} | |
} | |
function parseVersion(string $dir, string $version, ?VersionInfo $previousVersionInfo): VersionInfo | |
{ | |
$prefix = version_compare($version, '5.9999.9999.9999') <= 0 ? 'web/' : ''; | |
$versionDB = ''; | |
$lines = []; | |
$rc = -1; | |
exec('git -C ' . escapeshellarg($dir) . " show refs/tags/{$version}:{$prefix}concrete/config/concrete.php 2>&1", $lines, $rc); | |
if ($rc !== 0) { | |
throw new RuntimeException("Failed to retrieve the contents of concrete/config/concrete.php for version {$version}: " . trim(implode("\n", $lines))); | |
} | |
foreach ($lines as $line) { | |
if (preg_match('/(["\'])version_db\1\s*=>\s*(["\'])(\d+)\2/', $line, $matches)) { | |
if ($versionDB !== '') { | |
throw new RuntimeException("Duplicated version_db found when parsing version {$version}"); | |
} | |
$versionDB = $matches[3]; | |
} | |
} | |
$migrationIDs = []; | |
$lines = []; | |
$rc = -1; | |
exec('git -C ' . escapeshellarg($dir) . " ls-tree --name-only refs/tags/{$version} {$prefix}concrete/src/Updater/Migrations/Migrations/ 2>&1", $lines, $rc); | |
if ($rc !== 0) { | |
throw new RuntimeException("Failed to retrieve the list of migrations for version {$version}: " . trim(implode("\n", $lines))); | |
} | |
foreach ($lines as $line) { | |
if (preg_match('_/Version(\d+)\.php$_', $line, $matches)) { | |
$migrationID = $matches[1]; | |
if (strlen($migrationID) < strlen('YYYYMMDDhhmmss')) { | |
$contentLines = []; | |
$rc = -1; | |
exec('git -C ' . escapeshellarg($dir) . " show refs/tags/{$version}:{$line} 2>&1", $contentLines, $rc); | |
if ($rc !== 0) { | |
throw new RuntimeException("Failed to retrieve the contents of {$line} for version {$version}: " . trim(implode("\n", $contentLines))); | |
} | |
$contents = preg_replace('/\s+/', ' ', implode("\n", $contentLines)); | |
if (!preg_match('/function getName ?\( ?\) ?\{ ?return ?(["\'])(\d+)\1/', $contents, $matches)) { | |
throw new RuntimeException("Failed to exteact the actual migration ID from the contents of {$line} for version {$version}"); | |
} | |
$migrationID = $matches[2]; | |
} | |
if (strlen($migrationID) !== strlen('YYYYMMDDhhmmss')) { | |
throw new RuntimeException("Wrong migration ID ({$migrationID}) for file {$line} for version {$version}"); | |
} | |
$migrationIDs[] = $migrationID; | |
} | |
} | |
sort($migrationIDs); | |
if ($migrationIDs === [] && $versionDB !== '') { | |
throw new RuntimeException("Version {$version} has a version_db but without migrations"); | |
} | |
if ($versionDB === '' && $migrationIDs !== []) { | |
throw new RuntimeException("Version {$version} has migrations but without version_db"); | |
} | |
if ($previousVersionInfo !== null) { | |
if ($versionDB === '') { | |
if ($previousVersionInfo->getVersionDB() !== '') { | |
throw new RuntimeException("Version {$version} without version_db (but the previous version {$previousVersionInfo->getVersion()} has it)"); | |
} | |
} | |
} | |
return new VersionInfo($version, $versionDB, $migrationIDs, $previousVersionInfo); | |
} | |
exit(run()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment