-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add rebasemigration as a management command
This is a precurser for merging pulpcore migrations. It helps to rebase the external dependencies in all of a plugins migrations onto a minimum migration in e.g. pulpcore. As with makemigrations this is soley useful for developers.
- Loading branch information
Showing
2 changed files
with
126 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from gettext import gettext as _ | ||
|
||
from django.db import connection | ||
from django.db.migrations.loader import MigrationLoader | ||
from django.core.management import BaseCommand | ||
|
||
|
||
class Command(BaseCommand): | ||
""" | ||
Django management command to dump static informations about a migration. | ||
""" | ||
|
||
help = _("Dump static informations about a migration.") | ||
|
||
def add_arguments(self, parser): | ||
parser.add_argument("app-label", help=_("App label of the migrations.")) | ||
parser.add_argument("prefix", help=_("Prefix of the migration.")) | ||
|
||
def handle(self, *args, **options): | ||
app_label = options["app-label"] | ||
prefix = options["prefix"] | ||
|
||
loader = MigrationLoader(connection) | ||
|
||
migration = loader.get_migration_by_prefix(app_label, prefix) | ||
|
||
print(_("Looking at migration {migration}.").format(migration=migration)) | ||
if migration.atomic is False: | ||
print(_("Migration is not atomic.")) | ||
print(_("Dependencies:")) | ||
for dep in migration.dependencies: | ||
print("- ", ".".join(dep)) | ||
print(_("Operation Summary:")) | ||
runlength = 0 | ||
op_type = "" | ||
for op in migration.operations: | ||
if op_type == op.__class__.__name__: | ||
runlength += 1 | ||
else: | ||
if runlength: | ||
print(f"{runlength} X {op_type}") | ||
op_type = op.__class__.__name__ | ||
runlength = 1 | ||
if runlength: | ||
print(f"{runlength} X {op_type}") |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
from gettext import gettext as _ | ||
|
||
from django.db import connection | ||
from django.db.migrations.loader import MigrationLoader | ||
from django.db.migrations.writer import MigrationWriter | ||
from django.core.management import BaseCommand | ||
|
||
|
||
class Command(BaseCommand): | ||
""" | ||
Django management command to adjust migration dependencies to rebase on another plugin. | ||
""" | ||
|
||
help = _("Adjust migration dependencies to rebase on another plugin.") | ||
|
||
def add_arguments(self, parser): | ||
parser.add_argument("--dry-run", action="store_true", help=_("Don't change anything.")) | ||
parser.add_argument("app-label", help=_("App label of the migrations to rewire.")) | ||
parser.add_argument("dependency-app-label", help=_("App label of the dependency.")) | ||
parser.add_argument("dependency-migration", help=_("Prefix of the dependency migration.")) | ||
|
||
def handle(self, *args, **options): | ||
dry_run = options.get("dry_run", False) | ||
app_label = options["app-label"] | ||
dependency_app_label = options["dependency-app-label"] | ||
dependency_migration_prefix = options["dependency-migration"] | ||
|
||
loader = MigrationLoader(connection, replace_migrations=False) | ||
|
||
dependency_migration = loader.get_migration_by_prefix( | ||
dependency_app_label, dependency_migration_prefix | ||
) | ||
new_dependency = (dependency_app_label, dependency_migration.name) | ||
|
||
# Calculate list of replaceable dependencies. | ||
rebase_node = loader.graph.node_map[new_dependency] | ||
ancestors = set(rebase_node.parents) | ||
replaceable_dependencies = set() | ||
while ancestors: | ||
rebase_node = ancestors.pop() | ||
ancestors.update(rebase_node.parents) | ||
if rebase_node.key[0] == dependency_app_label: | ||
replaceable_dependencies.add(rebase_node.key) | ||
|
||
# Identify all migrations that need to be adjusted. | ||
affected_nodes = [ | ||
node | ||
for node in loader.graph.node_map.values() | ||
if node.key[0] == app_label | ||
and any( | ||
( | ||
dependency | ||
for dependency in loader.disk_migrations[node.key].dependencies | ||
if dependency in replaceable_dependencies | ||
) | ||
) | ||
] | ||
|
||
for affected_node in affected_nodes: | ||
migration = loader.disk_migrations[affected_node.key] | ||
# Remove all replaceable dependencies. | ||
migration.dependencies = [ | ||
dependency | ||
for dependency in migration.dependencies | ||
if dependency not in replaceable_dependencies | ||
] | ||
# Identify if we have added / will add the dependency in an ancestor. | ||
ancestors = set(affected_node.parents) | ||
while ancestors: | ||
ancestor_node = ancestors.pop() | ||
ancestors.update(ancestor_node.parents) | ||
if ancestor_node in affected_nodes: | ||
break | ||
else: | ||
migration.dependencies.append(new_dependency) | ||
|
||
print(_("Changing migration {}").format(affected_node.key)) | ||
if not dry_run: | ||
writer = MigrationWriter(migration) | ||
with open(writer.path, "w") as output_file: | ||
output_file.write(writer.as_string()) |