#!/usr/bin/env python3 import fileinput import glob import os import re import sys def validate_order(order: list[int], length: int) -> None: if len(order) != length: print("Please enter the sequence of all the conflicting files at once") sys.exit(1) for i in order: if i > length or i < 1 or order.count(i) > 1: print("Incorrect input") sys.exit(1) def renumber_migration(conflicts: list[str], order: list[int], last_correct_migration: str) -> None: stack: list[str] = [] for i in order: if conflicts[i - 1][0:4] not in stack: stack.append(conflicts[i - 1][0:4]) else: # Replace dependencies with the last correct migration with fileinput.FileInput("zerver/migrations/" + conflicts[i - 1], inplace=True) as file: for line in file: print(re.sub(r"[\d]+(_[a-z0-9]+)+", last_correct_migration, line), end="") # Rename the migration indexing at the end new_name = conflicts[i - 1].replace( conflicts[i - 1][0:4], f"{int(last_correct_migration[0:4]) + 1:04}" ) os.rename("zerver/migrations/" + conflicts[i - 1], "zerver/migrations/" + new_name) last_correct_migration = new_name.replace(".py", "") def resolve_conflicts(conflicts: list[str], files_list: list[str]) -> None: print("Conflicting migrations:") for i in range(len(conflicts)): print(str(i + 1) + ". " + conflicts[i]) order_input = input("Enter the order in which these migrations should be arranged: ") order = list(map(int, order_input.split())) validate_order(order, len(conflicts)) last_correct_migration = conflicts[order[0] - 1] last_correct_migration = last_correct_migration.replace(".py", "") renumber_migration(conflicts, order, last_correct_migration) if __name__ == "__main__": MIGRATIONS_TO_SKIP = {"0209", "0261", "0501", "0001"} while True: conflicts: list[str] = [] stack: list[str] = [] files_list = [os.path.basename(path) for path in glob.glob("zerver/migrations/????_*.py")] file_index = [file[0:4] for file in files_list] for file in file_index: migration_number = file[0:4] counter = file_index.count(migration_number) if ( counter > 1 and file[0:4] not in stack # When we need to backport migrations to a previous # release, we sometimes end up with multiple having # the same ID number (which isn't a problem; the # migrations graph structure, not the IDs, is what # matters). and migration_number not in MIGRATIONS_TO_SKIP ): conflicts += [ file_name for file_name in files_list if file_name.startswith(migration_number) ] stack.append(migration_number) if len(conflicts) > 0: resolve_conflicts(conflicts, files_list) else: break print("All conflicts resolved")