From 1d7a87ec315fc1fafbf9f10d077f348184a2115a Mon Sep 17 00:00:00 2001 From: Labib-Bin-Salam Date: Thu, 4 Jun 2026 06:50:23 +0100 Subject: [PATCH] Warn when an app description exceeds the recommended length A `description` longer than 256 characters silently corrupts the shortcut icon path in Windows MSI installers (the path prefix is truncated by the overflow amount). Descriptions are meant to be short, single-line summaries; longer text belongs in `long_description`. Briefcase now emits a warning during config parsing when an app's `description` is longer than 80 characters, pointing the user at `long_description`. The value is still preserved as-is. Refs #2864. --- changes/2864.bugfix.md | 1 + src/briefcase/config.py | 20 ++++++++++ tests/config/test_parse_config.py | 62 +++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) create mode 100644 changes/2864.bugfix.md diff --git a/changes/2864.bugfix.md b/changes/2864.bugfix.md new file mode 100644 index 000000000..39c008cd6 --- /dev/null +++ b/changes/2864.bugfix.md @@ -0,0 +1 @@ +Briefcase now warns when an app's `description` is longer than 80 characters, as overly long descriptions can be truncated by some packaging formats (such as Windows MSI shortcut icons). diff --git a/src/briefcase/config.py b/src/briefcase/config.py index 11cad0623..792001a99 100644 --- a/src/briefcase/config.py +++ b/src/briefcase/config.py @@ -41,6 +41,12 @@ "and cannot end with '-' or '_')." ) +# The `description` is a short, single-line summary of the app. Longer values +# belong in `long_description`. Some packaging formats embed the description in +# length-limited fields (e.g. Windows MSI shortcuts truncate at 256 characters, +# corrupting the icon path), so Briefcase warns when it exceeds this length. +MAX_DESCRIPTION_LENGTH = 80 + def is_valid_pep508_name(app_name): """Determine if the name is valid by PEP508 rules.""" @@ -1463,6 +1469,20 @@ def parse_config(config_file: Path, platform, output_format, console): # Normalize license fields to PEP 639 representation. normalize_license_config(config, app_name, base_path, console) + # The description should be a short, single-line summary. Warn (but don't + # fail) if it's too long, as some packaging formats embed it in + # length-limited fields; for example, an over-long description corrupts + # the shortcut icon path in Windows MSI installers. + description = config.get("description") + if isinstance(description, str) and len(description) > MAX_DESCRIPTION_LENGTH: + console.warning( + f"The description for {app_name!r} is {len(description)} characters " + f"long. Briefcase recommends a description of no more than " + f"{MAX_DESCRIPTION_LENGTH} characters; longer descriptions may be " + f"truncated when packaging for some platforms. Move any detailed " + f"text into the `long_description` field." + ) + # Construct a configuration object, and add it to the list # of configurations that are being handled. app_configs[app_name] = config diff --git a/tests/config/test_parse_config.py b/tests/config/test_parse_config.py index 91f197534..e8254dd42 100644 --- a/tests/config/test_parse_config.py +++ b/tests/config/test_parse_config.py @@ -754,6 +754,68 @@ def test_pep_621_merge(tmp_path): } +def test_long_description_warning(tmp_path): + """A description longer than the recommended length raises a warning.""" + long_description = "This is a needlessly long app description " + ("x" * 50) + assert len(long_description) > 80 + + config_file = create_file( + tmp_path / "pyproject.toml", + f""" + [tool.briefcase] + license = "MIT" + + [tool.briefcase.app.my_app] + description = "{long_description}" + """, + ) + + console = Mock() + _, apps = parse_config( + config_file, + platform="macOS", + output_format="Xcode", + console=console, + ) + + # The description is preserved as-is... + assert apps["my_app"]["description"] == long_description + # ...but the user is warned that it's too long. + console.warning.assert_called_once() + warning_text = console.warning.call_args[0][0] + assert "my_app" in warning_text + assert str(len(long_description)) in warning_text + assert "long_description" in warning_text + + +def test_short_description_no_warning(tmp_path): + """A description at the recommended length doesn't raise a warning.""" + # A description that is exactly at the limit is acceptable. + description = "x" * 80 + assert len(description) == 80 + + config_file = create_file( + tmp_path / "pyproject.toml", + f""" + [tool.briefcase] + license = "MIT" + + [tool.briefcase.app.my_app] + description = "{description}" + """, + ) + + console = Mock() + parse_config( + config_file, + platform="macOS", + output_format="Xcode", + console=console, + ) + + console.warning.assert_not_called() + + def test_license_pep621_table_with_files_is_error(tmp_path): """PEP 621 table format mixed with license-files raises an error.""" config_file = create_file(