Files
tubestation/browser/extensions/newtab/mach_commands.py

239 lines
8.1 KiB
Python

# 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 http://mozilla.org/MPL/2.0/.
import json
import os
import shutil
import subprocess
import tempfile
import time
from datetime import datetime
from pathlib import Path
from colorama import Fore, Style
from mach.decorators import (
Command,
SubCommand,
)
FIREFOX_L10N_REPO = "https://github.com/mozilla-l10n/firefox-l10n.git"
FLUENT_FILE = "newtab.ftl"
WEBEXT_LOCALES_PATH = Path("browser", "extensions", "newtab", "webext-glue", "locales")
LOCAL_EN_US_PATH = Path("browser", "locales", "en-US", "browser", "newtab", FLUENT_FILE)
COMPARE_TOOL_PATH = Path(
"third_party", "python", "moz.l10n", "moz", "l10n", "bin", "compare.py"
)
REPORT_PATH = Path(WEBEXT_LOCALES_PATH, "locales-report.json")
REPORT_LEFT_JUSTIFY_CHARS = 15
FLUENT_FILE_ANCESTRY = Path("browser", "newtab")
SUPPORTED_LOCALES_PATH = Path(WEBEXT_LOCALES_PATH, "supported-locales.json")
@Command(
"newtab",
category="misc",
description="Run a command for the newtab built-in addon",
virtualenv_name="newtab",
)
def newtab(command_context):
"""
Desktop New Tab build and update utilities.
"""
command_context._sub_mach(["help", "newtab"])
return 1
def run_mach(command_context, cmd, **kwargs):
return command_context._mach_context.commands.dispatch(
cmd, command_context._mach_context, **kwargs
)
@SubCommand(
"newtab",
"watch",
description="Parses the current locales-report.json and produces something human readable.",
)
def watch(command_context):
processes = []
try:
p1 = subprocess.Popen(
["./mach", "npm", "run", "watchmc", "--prefix=browser/extensions/newtab"]
)
p2 = subprocess.Popen(["./mach", "watch"])
processes.extend([p1, p2])
print("Watching subprocesses started. Press Ctrl-C to terminate them.")
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\nSIGINT received. Terminating subprocesses...")
for p in processes:
p.terminate()
for p in processes:
p.wait()
print("All watching subprocesses terminated.")
# Rebundle to avoid having all of the sourcemaps stick around.
run_mach(
command_context,
"npm",
args=["run", "bundle", "--prefix=browser/extensions/newtab"],
)
@SubCommand(
"newtab",
"update-locales",
description="Update the locales snapshot.",
virtualenv_name="newtab",
)
def update_locales(command_context):
try:
os.mkdir(WEBEXT_LOCALES_PATH)
except FileExistsError:
pass
# Step 1: We download the latest reckoning of strings from firefox-l10n
print("Cloning the latest HEAD of firefox-l10n repository")
with tempfile.TemporaryDirectory() as clone_dir:
subprocess.check_call(
["git", "clone", "--depth=1", FIREFOX_L10N_REPO, clone_dir]
)
# Step 2: Get some metadata about what we just pulled down -
# specifically, the revision.
revision = subprocess.check_output(
["git", "rev-parse", "HEAD"],
cwd=str(clone_dir),
universal_newlines=True,
).strip()
# Step 3: Recursively find all files matching the filename for our
# FLUENT_FILE, and copy them into WEBEXT_LOCALES_PATH/AB_CD/FLUENT_FILE
root_dir = Path(clone_dir)
fluent_file_matches = list(root_dir.rglob(FLUENT_FILE))
for fluent_file_abs_path in fluent_file_matches:
relative_path = fluent_file_abs_path.relative_to(root_dir)
# The first element of the path is the locale code, which we want
# to recreate under WEBEXT_LOCALES_PATH
locale = relative_path.parts[0]
destination_file = WEBEXT_LOCALES_PATH.joinpath(
locale, FLUENT_FILE_ANCESTRY, FLUENT_FILE
)
destination_file.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(fluent_file_abs_path, destination_file)
# Now clean up the temporary directory.
shutil.rmtree(clone_dir)
# Step 4: Now copy the local version of FLUENT_FILE in LOCAL_EN_US_PATH
# into WEBEXT_LOCALES_PATH/en-US/FLUENT_FILE
print(f"Cloning local en-US copy of {FLUENT_FILE}")
dest_en_ftl_path = WEBEXT_LOCALES_PATH.joinpath(
"en-US", FLUENT_FILE_ANCESTRY, FLUENT_FILE
)
dest_en_ftl_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(LOCAL_EN_US_PATH, dest_en_ftl_path)
# Step 5: Now compare that en-US Fluent file with all of the ones we just
# cloned and create a report with how many strings are still missing.
print("Generating localization report…")
source_ftl_path = WEBEXT_LOCALES_PATH.joinpath("en-US")
paths = list(WEBEXT_LOCALES_PATH.rglob(FLUENT_FILE))
# There are 2 parent folders of each FLUENT_FILE (see FLUENT_FILE_ANCESTRY),
# and we want to get at the locale folder root for our comparison.
ANCESTRY_LENGTH = 2
# Get the full list of supported locales that we just pulled down
supported_locales = [path.parents[ANCESTRY_LENGTH].name for path in paths]
path_strs = [path.parents[ANCESTRY_LENGTH].as_posix() for path in paths]
# Verbosity on the compare.py tool appears to be a count value, which is
# incremented for each -v flag. We want an elevated verbosity so that we
# get back
verbosity = ["-v", "-v"]
# A bug in compare.py means that the source folder must be passed in as
# an absolute path.
source = ["--source=%s" % source_ftl_path.absolute().as_posix()]
other_flags = ["--json"]
# The moz.l10n compare tool is currently designed to be invoked from the
# command line interface. We'll use subprocess to invoke it and capture
# its output.
python = command_context.virtualenv_manager.python_path
def on_line(line):
locales = json.loads(line)
report = {
"locales": locales,
"meta": {
"repository": FIREFOX_L10N_REPO,
"revision": revision,
"updated": datetime.utcnow().isoformat(),
},
}
with open(REPORT_PATH, "w") as file:
json.dump(report, file)
display_report(report)
print("Wrote report to %s" % REPORT_PATH)
command_context.run_process(
[python, str(COMPARE_TOOL_PATH)] + other_flags + source + verbosity + path_strs,
pass_thru=False,
line_handler=on_line,
)
print("Writing supported locales to %s" % SUPPORTED_LOCALES_PATH)
with open(SUPPORTED_LOCALES_PATH, "w") as file:
json.dump(supported_locales, file)
print("Done")
@SubCommand(
"newtab",
"locales-report",
description="Parses the current locales-report.json and produces something human readable.",
virtualenv_name="newtab",
)
def locales_report(command_context):
with open(REPORT_PATH) as file:
report = json.load(file)
display_report(report)
def display_report(report):
meta = report["meta"]
print("New Tab locales report")
print("Locales last updated: %s" % meta["updated"])
print("From %s - revision: %s" % (meta["repository"], meta["revision"]))
print("------")
sorted_locales = sorted(report["locales"].keys(), key=lambda x: x.lower())
for locale in sorted_locales:
print(Style.RESET_ALL, end="")
if report["locales"][locale]["missing"]:
missing_translations = report["locales"][locale]["missing"][
str(FLUENT_FILE_ANCESTRY.joinpath(FLUENT_FILE))
]
total_missing_translations = len(missing_translations)
if total_missing_translations > 10:
color = Fore.RED
else:
color = Fore.YELLOW
print(
color
+ "%s%s missing translations"
% (locale.ljust(REPORT_LEFT_JUSTIFY_CHARS), total_missing_translations)
)
else:
print(
Fore.GREEN
+ "%s0 missing translations" % locale.ljust(REPORT_LEFT_JUSTIFY_CHARS)
)
print(Style.RESET_ALL, end="")