# 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/. from mach.decorators import Command, CommandArgument LICENSE_HEADER = """# 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/. """ GENERATED_HEADER = """ ### This file was AUTOMATICALLY GENERATED by `./mach update-glean-tags` ### DO NOT edit it by hand. """ DATA_REVIEW_HELP = """ Beginning 2024-05-07[1], data reviews for projects in mozilla-central are now conducted on Phabricator. Simply duplicate your bug URL from the `bugs` list to the `data_reviews` list in your metrics and pings definitions, and push for code review in the normal way[2]. More details about this process can be found in the in-tree docs[3] and wiki[4]. If you'd like to generate a Data Review Request template anyway (if, for instance, you can't use Phabricator for your data review or you need a Data Review Request to aid in a Sensitive Data Review process. Or you're just curious), you can invoke glean_parser directly: ./mach python -m glean_parser data-review [1]: https://groups.google.com/a/mozilla.org/g/firefox-dev/c/7z-i6UhPoKY [2]: https://firefox-source-docs.mozilla.org/contributing/index.html [3]: https://firefox-source-docs.mozilla.org/contributing/data-review.html [4]: https://wiki.mozilla.org/Data_Collection """ @Command( "data-review", category="misc", description="Describe how Data Review works in mozilla-central", ) def data_review(command_context): # Data Review happens in Phabricator now # (https://groups.google.com/a/mozilla.org/g/firefox-dev/c/7z-i6UhPoKY) # so explain how to do it. print(DATA_REVIEW_HELP) @Command( "update-glean-tags", category="misc", description=( "Creates a list of valid glean tags based on in-tree bugzilla component definitions" ), ) def update_glean_tags(command_context): from pathlib import Path import yaml from mozbuild.backend.configenvironment import ConfigEnvironment from mozbuild.frontend.reader import BuildReader config = ConfigEnvironment( command_context.topsrcdir, command_context.topobjdir, defines=command_context.defines, substs=command_context.substs, ) reader = BuildReader(config) bug_components = set() for p in reader.read_topsrcdir(): if p.get("BUG_COMPONENT"): bug_components.add(p["BUG_COMPONENT"]) tags_filename = (Path(__file__).parent / "../tags.yaml").resolve() tags = {"$schema": "moz://mozilla.org/schemas/glean/tags/1-0-0"} for bug_component in bug_components: product = bug_component.product.strip() component = bug_component.component.strip() tags["{} :: {}".format(product, component)] = { "description": "The Bugzilla component which applies to this object." } open(tags_filename, "w").write( "{}\n{}\n\n".format(LICENSE_HEADER, GENERATED_HEADER) + yaml.dump(tags, width=78, explicit_start=True) ) def replace_in_file(path, pattern, replace): """ Replace `pattern` with `replace` in the file `path`. The file is modified on disk. Returns `True` if exactly one replacement happened. `False` otherwise. """ import re with open(path, "r+") as file: data = file.read() data, subs_made = re.subn(pattern, replace, data, flags=re.MULTILINE) file.seek(0) file.write(data) file.truncate() if subs_made != 1: return False return True def replace_in_file_or_die(path, pattern, replace): """ Replace `pattern` with `replace` in the file `path`. The file is modified on disk. If not exactly one occurrence of `pattern` was replaced it will exit with exit code 1. """ import sys success = replace_in_file(path, pattern, replace) if not success: print(f"ERROR: Failed to replace one occurrence in {path}") print(f" Pattern: {pattern}") print(f" Replace: {replace}") print("File was modified. Check the diff.") sys.exit(1) @Command( "update-glean", category="misc", description="Update Glean to the given version", ) @CommandArgument("version", help="Glean version to upgrade to") def update_glean(command_context, version): import textwrap from pathlib import Path topsrcdir = Path(command_context.topsrcdir) replace_in_file_or_die( topsrcdir / "mobile" / "android" / "android-components" / "plugins" / "dependencies" / "src" / "main" / "java" / "DependenciesPlugin.kt", r'mozilla_glean = "[0-9.]+"', f'mozilla_glean = "{version}"', ) replace_in_file_or_die( topsrcdir / "toolkit" / "components" / "glean" / "Cargo.toml", r'^glean = "[0-9.]+"', f'glean = "{version}"', ) replace_in_file_or_die( topsrcdir / "toolkit" / "components" / "glean" / "api" / "Cargo.toml", r'^glean = "[0-9.]+"', f'glean = "{version}"', ) replace_in_file_or_die( topsrcdir / "gfx" / "wr" / "webrender" / "Cargo.toml", r'^glean = { version = "[0-9.]+"(.+)}', f'glean = {{ version = "{version}"\\1}}', ) replace_in_file_or_die( topsrcdir / "gfx" / "wr" / "wr_glyph_rasterizer" / "Cargo.toml", r'^glean = { version = "[0-9.]+"(.+)}', f'glean = {{ version = "{version}"\\1}}', ) replace_in_file_or_die( topsrcdir / "python" / "sites" / "mach.txt", r"glean-sdk==[0-9.]+", f"glean-sdk=={version}", ) instructions = f""" We've edited the necessary files to require Glean SDK {version}. To ensure Glean and Firefox's other Rust dependencies are appropriately vendored, please run the following commands: cargo update -p glean ./mach vendor rust --ignore-modified `./mach vendor rust` may identify version mismatches. Please consult the Updating the Glean SDK docs for assistance: https://firefox-source-docs.mozilla.org/toolkit/components/glean/dev/updating_sdk.html The Glean SDK is already vetted and no additional vetting for it is necessary. To prune the configuration file after vendoring run: ./mach cargo vet prune Then, to update webrender which independently relies on the Glean SDK, run: cd gfx/wr cargo update -p glean Then, to ensure all is well, build Firefox and run the FOG tests. Instructions can be found here: https://firefox-source-docs.mozilla.org/toolkit/components/glean/dev/testing.html """ print(textwrap.dedent(instructions)) @Command( "event-into-legacy", category="misc", description="Create a Legacy Telemetry compatible event definition from an existing Glean Event metric.", ) @CommandArgument( "--append", "-a", action="store_true", help="Append to toolkit/components/telemetry/Events.yaml (note: verify and make any necessary modifications before landing).", ) @CommandArgument("event", default=None, nargs="?", type=str, help="Event name.") def event_into_legacy(command_context, event=None, append=False): # Get the metrics_index's list of metrics indices # by loading the index as a module. import sys from os import path sys.path.append(path.join(path.dirname(__file__), path.pardir)) from metrics_index import metrics_yamls sys.path.append(path.dirname(__file__)) from pathlib import Path from translate_events import translate_event legacy_yaml_path = path.join( Path(command_context.topsrcdir), "toolkit", "components", "telemetry", "Events.yaml", ) return translate_event( event, append, [Path(command_context.topsrcdir) / x for x in metrics_yamls], legacy_yaml_path, )