lib/l10n_utils/management/commands/open_ftl_pr.py (103 lines of code) (raw):

# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. from datetime import datetime from os import getenv from subprocess import CalledProcessError from django.conf import settings from django.core.management.base import CommandError from django.template.defaultfilters import slugify from django.utils.functional import cached_property from dirsync import sync from bedrock.utils import github from ._ftl_repo_base import FTLRepoCommand GIT_HASH = getenv("GIT_SHA", None) class Command(FTLRepoCommand): help = "Open a pull-request on the L10n Team's repo for FTL file changes" branch_prefix = "update-from-bedrock" open_branch = None open_pr_url = None github = None def __init__(self, stdout=None, stderr=None, no_color=False, force_color=False): client = github.get_client() if client: self.github = client.get_repo(settings.FLUENT_L10N_TEAM_REPO) super().__init__(stdout, stderr, no_color, force_color) def handle(self, *args, **options): super().handle(*args, **options) self.get_open_pr() self.update_l10n_team_files() self.create_branch() self.sync_dirs() changed = self.commit_changes() if changed: self.push_changes() self.open_pr() @cached_property def branch_name(self): if self.open_branch: return self.open_branch if GIT_HASH: branch_suffix = GIT_HASH[:8] else: branch_suffix = slugify(datetime.now().isoformat()) return f"{self.branch_prefix}-{branch_suffix}" @property def commit_message(self): message = "Updates from bedrock\n\n" if GIT_HASH: message += f"From file changes in https://github.com/mozilla/bedrock/commit/{GIT_HASH}" else: message += "From file changes in https://github.com/mozilla/bedrock/commits/main" return message def sync_dirs(self): sync(settings.FLUENT_LOCAL_PATH, self.l10n_repo.path, "sync", content=True) def create_branch(self): if self.open_branch: self.stdout.write(f"Using existing branch: {self.branch_name}") else: self.l10n_repo.git("checkout", "-b", self.branch_name) self.stdout.write(f"Created branch: {self.branch_name}") def commit_changes(self): self.config_git() self.l10n_repo.git("add", ".") try: self.l10n_repo.git("commit", "-m", self.commit_message) except CalledProcessError: self.stdout.write("No changes to commit") return False self.stdout.write("Committed changes to local repo") return True def push_changes(self): try: result = self.l10n_repo.git("push", self.git_push_url, f"HEAD:{self.branch_name}") self.stdout.write(result) except CalledProcessError as cpe: raise CommandError(f"There was a problem pushing to {self.l10n_repo.remote_url}: {cpe}\n{cpe.output}") commit = self.l10n_repo.git("rev-parse", "--short", "HEAD") self.stdout.write(f"Pushed {commit} to {self.l10n_repo.remote_url} as {self.branch_name}") @property def git_push_url(self): if not settings.FLUENT_REPO_AUTH: raise CommandError("Git push authentication not configured") return self.l10n_repo.remote_url_auth(settings.FLUENT_REPO_AUTH) def get_open_pr(self): if self.github is None: return for pr in self.github.get_pulls(state="open"): if pr.head.ref.startswith(self.branch_prefix): self.open_branch = pr.head.ref self.open_pr_url = pr.html_url self.l10n_repo.branch_name = self.open_branch return def open_pr(self): if self.github is None: return if self.open_branch: self.stdout.write(f"Updated existing pull-request: {self.open_pr_url}") return title, body = self.commit_message.split("\n\n") pr = self.github.create_pull( title=title, body=body, base=self.l10n_repo.branch_name, head=self.branch_name, ) self.stdout.write(f"Opened a pull-request: {pr.html_url}")