Skip to content

Instantly share code, notes, and snippets.

@mlocati
Created July 3, 2020 12:04
Show Gist options
  • Save mlocati/8d05331afee8ff4cb810547c2061c0c7 to your computer and use it in GitHub Desktop.
Save mlocati/8d05331afee8ff4cb810547c2061c0c7 to your computer and use it in GitHub Desktop.
Check concrete5 migration IDs
<?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