|
| 1 | +import argparse |
| 2 | + |
| 3 | +from django.core.management import BaseCommand, CommandError, CommandParser |
| 4 | + |
| 5 | +from lando.main.models import SCM_ALLOW_DIRECT_PUSH |
| 6 | +from lando.main.models.repo import Repo |
| 7 | + |
| 8 | + |
| 9 | +class Command(BaseCommand): |
| 10 | + help = """This stub commands allows to run data migrations. |
| 11 | +
|
| 12 | + It should be fleshed out as part of bug 2013991. |
| 13 | + """ |
| 14 | + # TODO: record already-applied migrations. |
| 15 | + |
| 16 | + def add_arguments(self, parser: CommandParser): |
| 17 | + parser.add_argument( |
| 18 | + "-l", "--list", default=False, action=argparse.BooleanOptionalAction |
| 19 | + ) |
| 20 | + parser.add_argument( |
| 21 | + "-y", |
| 22 | + "--yes", |
| 23 | + default=False, |
| 24 | + help="Don't ask for confirmation", |
| 25 | + action=argparse.BooleanOptionalAction, |
| 26 | + ) |
| 27 | + parser.add_argument("migration", help="Migration to apply", nargs="?") |
| 28 | + |
| 29 | + def handle(self, *args, **options): |
| 30 | + ask_confirm = not options.get("yes", False) |
| 31 | + |
| 32 | + if options.get("list"): |
| 33 | + self.stdout.write("Available data migrations:\n") |
| 34 | + for migration in [ |
| 35 | + method.removeprefix("migrate_") |
| 36 | + for method in dir(self) |
| 37 | + if method.startswith("migrate_") |
| 38 | + ]: |
| 39 | + self.stdout.write(f"* {migration}") |
| 40 | + raise SystemExit() |
| 41 | + |
| 42 | + if not (migration := options.get("migration")): |
| 43 | + raise CommandError("No migration specified") |
| 44 | + |
| 45 | + method_name = f"migrate_{migration}" |
| 46 | + |
| 47 | + if not (migration_method := getattr(self, method_name)): |
| 48 | + raise CommandError("Migration method not found: {method_name}") |
| 49 | + |
| 50 | + migration_method(ask_confirm) |
| 51 | + |
| 52 | + def _get_confirmation(self, ask_confirm: bool, prompt: str, details: str): |
| 53 | + """Show a confirmation prompt with details. |
| 54 | +
|
| 55 | + If ask_confirm is set, details are printed prior to requesting confirmation. |
| 56 | +
|
| 57 | + Otherwise, let the caller show the details as it proceeds. |
| 58 | + """ |
| 59 | + self.stdout.write(prompt, ending="") |
| 60 | + if ask_confirm: |
| 61 | + self.stdout.write(f"{details}. Confirm [yN]? ", ending="") |
| 62 | + if input().lower() != "y": |
| 63 | + raise CommandError("User interruption") |
| 64 | + |
| 65 | + self.stdout.write("Progress: ", ending="") |
| 66 | + |
| 67 | + # |
| 68 | + # MIGRATION METHODS |
| 69 | + # |
| 70 | + |
| 71 | + def migrate_1_bug1971103_add_firefox_required_automation_permissions( |
| 72 | + self, ask_confirm: bool = True |
| 73 | + ): |
| 74 | + """ |
| 75 | + Explicitly set the `required_automation_permission` for all Firefox repos. |
| 76 | +
|
| 77 | + This is to match the currectly implicit authorisation for Firefox sheriffs. |
| 78 | + """ |
| 79 | + fx_repos = Repo.objects.filter( |
| 80 | + name__startswith="firefox-", required_automation_permission="" |
| 81 | + ) |
| 82 | + if not fx_repos: |
| 83 | + self.stdout.write( |
| 84 | + "No firefox-% repo found with NULL required_automation_permission." |
| 85 | + ) |
| 86 | + raise SystemExit() |
| 87 | + |
| 88 | + fx_repo_names = ", ".join(repo.name for repo in fx_repos) |
| 89 | + self._get_confirmation( |
| 90 | + ask_confirm, "Updating required_automation_permission for: ", fx_repo_names |
| 91 | + ) |
| 92 | + |
| 93 | + for repo in fx_repos: |
| 94 | + if not repo.required_automation_permission: |
| 95 | + repo.required_automation_permission = SCM_ALLOW_DIRECT_PUSH |
| 96 | + repo.save() |
| 97 | + self.stdout.write(f"{repo.name} ", ending="") |
| 98 | + |
| 99 | + self.stdout.write("done.") |
0 commit comments