diff --git a/landoscript/src/landoscript/actions/version_bump.py b/landoscript/src/landoscript/actions/version_bump.py index 3f10fbeee..a1310167d 100644 --- a/landoscript/src/landoscript/actions/version_bump.py +++ b/landoscript/src/landoscript/actions/version_bump.py @@ -1,6 +1,7 @@ import json import logging import os.path +import re import typing from dataclasses import dataclass @@ -30,6 +31,12 @@ "mail/config/version_display.txt", ) +# Standalone browser extensions live at browser/extensions//manifest.json and are +# version bumped independently of the Gecko/Firefox version. Their commit messages call out +# the extension name so they're distinguishable from a Gecko version bump, which has broader +# implications. See bug 2068060. +_EXTENSION_MANIFEST_RE = re.compile(r"^browser/extensions/(?P[^/]+)/manifest\.json$") + @dataclass(frozen=True) class VersionBumpInfo: @@ -59,6 +66,7 @@ async def run( are discouraged, and should be avoided if at all possible.""" diffs = [] + bumped_files = set() for version_bump_info in version_bump_infos: next_version = version_bump_info.next_version @@ -102,6 +110,7 @@ async def run( log.info(f"{file}: successfully bumped! new contents are:") log_file_contents(modified) + bumped_files.add(file) diffs.append(diff_contents(orig, modified, file)) if not diffs: @@ -121,13 +130,35 @@ def extract_path(diff_text): log_file_contents(diff) # version bumps always ignore a closed tree - commitmsg = "Automatic version bump NO BUG a=release CLOSED TREE" + component = extension_component(bumped_files) + commitmsg = f"Automatic {component}version bump NO BUG a=release CLOSED TREE" if dontbuild: commitmsg += " DONTBUILD" return [create_commit_action(commitmsg, diff)] +def extension_component(bumped_files: set[str]) -> str: + """Return a commit message component naming the standalone extension(s) being bumped. + + If every bumped file is a standalone browser extension manifest, return the extension + name(s) so the commit reads e.g. ``Automatic newtab version bump ...`` or, for a mix of + extensions, ``Automatic newtab, webcompat version bump ...``. Otherwise (a Gecko version + bump, or a mix that includes non-extension files) return an empty string for the generic + message. + """ + names = set() + for file in bumped_files: + match = _EXTENSION_MANIFEST_RE.match(file) + if not match: + return "" + names.add(match.group("name")) + + if names: + return f"{', '.join(sorted(names))} " + return "" + + def get_cur_and_next_version(filename, orig_contents, next_version, munge_next_version): if filename.endswith("/manifest.json"): cur = parse_manifest_version(orig_contents) diff --git a/landoscript/tests/test_version_bump.py b/landoscript/tests/test_version_bump.py index 481e81fb5..1178a36ef 100644 --- a/landoscript/tests/test_version_bump.py +++ b/landoscript/tests/test_version_bump.py @@ -436,7 +436,7 @@ async def test_json_manifest_bump(aioresponses, github_installation_responses, c ["version_bump"], assert_func=lambda req: assert_success( req, - ["Automatic version bump", "NO BUG", "a=release", "CLOSED TREE"], + ["Automatic newtab version bump", "NO BUG", "a=release", "CLOSED TREE"], {MANIFEST_FILE: f' "version": "{initial_version}"'}, {MANIFEST_FILE: f' "version": "{expected_version}"'}, ), @@ -484,8 +484,71 @@ async def test_webcompat_manifest_bump(aioresponses, github_installation_respons ["version_bump"], assert_func=lambda req: assert_success( req, - ["Automatic version bump", "NO BUG", "a=release", "CLOSED TREE"], + ["Automatic webcompat version bump", "NO BUG", "a=release", "CLOSED TREE"], {WEBCOMPAT_MANIFEST: ' "version": "151.0.0"'}, {WEBCOMPAT_MANIFEST: ' "version": "151.1.0"'}, ), ) + + +@pytest.mark.asyncio +async def test_multiple_extension_manifest_bump(aioresponses, github_installation_responses, context): + payload = { + "actions": ["version_bump"], + "lando_repo": "repo_name", + "version_bump_info": { + "files": [NEWTAB_MANIFEST, WEBCOMPAT_MANIFEST], + "next_version": "151.1.0", + }, + } + setup_github_graphql_responses( + aioresponses, + get_files_payload({NEWTAB_MANIFEST: _manifest("151.0.0"), WEBCOMPAT_MANIFEST: _manifest("151.0.0")}), + ) + await run_test( + aioresponses, + github_installation_responses, + context, + payload, + ["version_bump"], + assert_func=lambda req: assert_success( + req, + # both extensions are named, sorted, in a single message + ["Automatic newtab, webcompat version bump", "NO BUG", "a=release", "CLOSED TREE"], + {NEWTAB_MANIFEST: ' "version": "151.0.0"', WEBCOMPAT_MANIFEST: ' "version": "151.0.0"'}, + {NEWTAB_MANIFEST: ' "version": "151.1.0"', WEBCOMPAT_MANIFEST: ' "version": "151.1.0"'}, + ), + ) + + +@pytest.mark.asyncio +async def test_extension_and_browser_version_bump(aioresponses, github_installation_responses, context): + # A bump that mixes an extension manifest with a browser version file is not a + # standalone extension bump, so it falls back to the generic commit message. + VERSION_FILE = "browser/config/version.txt" + payload = { + "actions": ["version_bump"], + "lando_repo": "repo_name", + "version_bump_info": { + "files": [VERSION_FILE, NEWTAB_MANIFEST], + "next_version": "135.0", + }, + } + setup_github_graphql_responses( + aioresponses, + get_files_payload({VERSION_FILE: "134.0", NEWTAB_MANIFEST: _manifest("134.0")}), + ) + await run_test( + aioresponses, + github_installation_responses, + context, + payload, + ["version_bump"], + assert_func=lambda req: assert_success( + req, + # generic message: the extension name is not called out + ["Automatic version bump", "NO BUG", "a=release", "CLOSED TREE"], + {VERSION_FILE: "134.0", NEWTAB_MANIFEST: ' "version": "134.0"'}, + {VERSION_FILE: "135.0", NEWTAB_MANIFEST: ' "version": "135.0"'}, + ), + )