commit 4533a2607969f2633ee05e2e9dee201df85431c1
parent 4fd1490bd1be8ecdf2afda6a53717f0716d6bc3c
Author: Bastien Orivel <borivel@mozilla.com>
Date: Tue, 4 Nov 2025 09:24:50 +0000
Bug 1997143 - Properly name XPI files in the langpack RPM packages. r=releng-reviewers,jcristau
Firefox expects XPI files in `distribution/extensions` to be named with
their ID or it throws exceptions on startup like
`/usr/lib/firefox-nightly/distribution/extensions/langpack-it@firefox-nightly.mozilla.org.xpi
contains an add-on with an incorrect ID`.
The previous code would use the package name but the langpack XPI ID is
actually using the remoting name (firefox/devedition) and does not
contain the track for which it was built. So we ended up with
`langpack-fr@firefox-nightly.mozilla.org.xpi` instead of
`langpack-fr@firefox.mozilla.org.xpi` for nightly builds (same issue
with beta), which prevents the langpack from loading.
Differential Revision: https://phabricator.services.mozilla.com/D270551
Diffstat:
3 files changed, 51 insertions(+), 19 deletions(-)
diff --git a/browser/installer/linux/app/rpm/firefox.spec.j2 b/browser/installer/linux/app/rpm/firefox.spec.j2
@@ -9,8 +9,8 @@ Vendor: Mozilla
Source0: %{name}.tar.xz
Source1: %{name}.desktop
Source2: %{name}.1
-{%- for codename in LANGUAGES %}
-Source{{ loop.index + 2 }}: {{ codename }}.langpack.xpi
+{%- for codename, metadata in LANGUAGES.items() %}
+Source{{ loop.index + 2 }}: {{ metadata.extension_id }}.xpi
{%- endfor %}
%global mozappdir /{{ PKG_INSTALL_PATH }}
@@ -27,8 +27,8 @@ Source{{ loop.index + 2 }}: {{ codename }}.langpack.xpi
%{__install} -m 0644 %{SOURCE2} %{buildroot}%{_mandir}/man1/%{name}.1
%{__ln_s} %{mozappdir}/firefox %{buildroot}%{_bindir}/%{name}
%{__mkdir_p} %{buildroot}/%{mozappdir}/distribution/extensions
-{%- for codename in LANGUAGES %}
-%{__install} -m 0644 %{SOURCE{{ loop.index + 2 }}} %{buildroot}%{mozappdir}/distribution/extensions/langpack-{{ codename }}@{{ PKG_NAME }}.mozilla.org.xpi
+{%- for codename, metadata in LANGUAGES.items() %}
+%{__install} -m 0644 %{SOURCE{{ loop.index + 2 }}} %{buildroot}%{mozappdir}/distribution/extensions/{{ metadata.extension_id }}.xpi
{%- endfor %}
%{__install} -D -m 0644 %{buildroot}%{mozappdir}/browser/chrome/icons/default/default16.png %{buildroot}%{_datadir}/icons/hicolor/16x16/apps/%{name}.png
%{__install} -D -m 0644 %{buildroot}%{mozappdir}/browser/chrome/icons/default/default32.png %{buildroot}%{_datadir}/icons/hicolor/32x32/apps/%{name}.png
@@ -36,18 +36,18 @@ Source{{ loop.index + 2 }}: {{ codename }}.langpack.xpi
%{__install} -D -m 0644 %{buildroot}%{mozappdir}/browser/chrome/icons/default/default64.png %{buildroot}%{_datadir}/icons/hicolor/64x64/apps/%{name}.png
%{__install} -D -m 0644 %{buildroot}%{mozappdir}/browser/chrome/icons/default/default128.png %{buildroot}%{_datadir}/icons/hicolor/128x128/apps/%{name}.png
-{% for codename, description in LANGUAGES.items() %}
+{% for codename, metadata in LANGUAGES.items() %}
%package l10n-{{ codename }}
-Summary: {{ description }}
+Summary: {{ metadata.description }}
BuildArch: noarch
Requires: %{name} = %{version}-%{release}
Supplements: (%{name} = %{version}-%{release} and langpacks-{{ codename }})
%description l10n-{{ codename }}
-{{ description }}
+{{ metadata.description }}
%files l10n-{{ codename }}
-%{mozappdir}/distribution/extensions/langpack-{{ codename }}@{{ PKG_NAME }}.mozilla.org.xpi
+%{mozappdir}/distribution/extensions/{{ metadata.extension_id }}.xpi
{% endfor %}
%files
diff --git a/python/mozbuild/mozbuild/repackaging/utils.py b/python/mozbuild/mozbuild/repackaging/utils.py
@@ -218,8 +218,12 @@ def prepare_langpack_files(output_dir, xpi_directory):
manifest = get_manifest_from_langpack(xpi_file, output_dir)
if manifest is not None:
language = manifest["langpack_id"]
- metadata[language] = manifest["description"]
- output_file = mozpath.join(output_dir, f"{language}.langpack.xpi")
+ extension_id = manifest["browser_specific_settings"]["gecko"]["id"]
+ metadata[language] = {
+ "description": manifest["description"],
+ "extension_id": extension_id,
+ }
+ output_file = mozpath.join(output_dir, f"{extension_id}.xpi")
shutil.copy(xpi_file, output_file)
return metadata
diff --git a/python/mozbuild/mozbuild/test/repackaging/test_utils.py b/python/mozbuild/mozbuild/test/repackaging/test_utils.py
@@ -541,7 +541,11 @@ def test_inject_desktop_entry_file(monkeypatch):
_MINIMAL_MANIFEST_JSON = """{
"langpack_id": "%(lang)s",
"manifest_version": 2,
- "browser_specific_settings": {},
+ "browser_specific_settings": {
+ "gecko": {
+ "id": "langpack-%(lang)s@%(remoting_name)s.mozilla.org"
+ }
+ },
"name": "Language: %(lang)s",
"description": "Firefox Language Pack for %(lang)s",
"version": "136.0.20250326.231000",
@@ -561,27 +565,49 @@ def test_get_manifest_from_langpack():
with zipfile.ZipFile(path, "w") as zip_file:
zip_file.writestr(
- "manifest.json", _MINIMAL_MANIFEST_JSON % {"lang": "dummy"}
+ "manifest.json",
+ _MINIMAL_MANIFEST_JSON % {"lang": "dummy", "remoting_name": "firefox"},
)
assert utils.get_manifest_from_langpack(path, d) == json.loads(
- _MINIMAL_MANIFEST_JSON % {"lang": "dummy"}
+ _MINIMAL_MANIFEST_JSON % {"lang": "dummy", "remoting_name": "firefox"}
)
@pytest.mark.parametrize(
- "languages, expected",
+ "languages, remoting_name, expected",
(
- ([], {}),
+ ([], "firefox", {}),
(
["ach", "fr"],
+ "firefox",
+ {
+ "ach": {
+ "description": "Firefox Language Pack for ach",
+ "extension_id": "langpack-ach@firefox.mozilla.org",
+ },
+ "fr": {
+ "description": "Firefox Language Pack for fr",
+ "extension_id": "langpack-fr@firefox.mozilla.org",
+ },
+ },
+ ),
+ (
+ ["de", "es"],
+ "devedition",
{
- "ach": "Firefox Language Pack for ach",
- "fr": "Firefox Language Pack for fr",
+ "de": {
+ "description": "Firefox Language Pack for de",
+ "extension_id": "langpack-de@devedition.mozilla.org",
+ },
+ "es": {
+ "description": "Firefox Language Pack for es",
+ "extension_id": "langpack-es@devedition.mozilla.org",
+ },
},
),
),
)
-def test_prepare_langpack_files(monkeypatch, languages, expected):
+def test_prepare_langpack_files(monkeypatch, languages, remoting_name, expected):
def _mock_copy(source, destination):
pass
@@ -592,7 +618,9 @@ def test_prepare_langpack_files(monkeypatch, languages, expected):
path = os.path.join(xpi_dir, f"{language}.langpack.xpi")
with zipfile.ZipFile(path, "w") as zip_file:
zip_file.writestr(
- "manifest.json", _MINIMAL_MANIFEST_JSON % {"lang": language}
+ "manifest.json",
+ _MINIMAL_MANIFEST_JSON
+ % {"lang": language, "remoting_name": remoting_name},
)
assert utils.prepare_langpack_files(output_dir, xpi_dir) == expected