From cb3265e40f7ee8c1d09bb797306176efdcd238c7 Mon Sep 17 00:00:00 2001 From: James Rich <2199651+jamesarich@users.noreply.github.com> Date: Fri, 18 Sep 2026 18:26:56 -0700 Subject: [PATCH] feat(release): say what changed in the Play listing (#7224) --- .github/workflows/pull-request.yml | 4 + .../android/en-US/changelogs/default.txt | 4 +- scripts/check-metadata-length.py | 3 + scripts/sync-play-changelog.py | 90 +++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100755 scripts/sync-play-changelog.py diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 84b4c0fd17..67afad37b1 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -310,6 +310,10 @@ jobs: # through an upload. Catch it here instead. - name: Validate store listing locale codes run: python3 scripts/check-play-locales.py + # default.txt is rendered from metainfo.xml; a PR that edits one without the + # other ships a listing that no longer says what the release did. + - name: Check Play what's-new matches AppStream + run: python3 scripts/sync-play-changelog.py --check # Compose Multiplatform does not strip Android-style \" / \' escapes, so a # backslash written before a quote renders literally in the UI (PR #6357). # Guards the English source strings; locale mirrors are cleaned upstream by diff --git a/fastlane/metadata/android/en-US/changelogs/default.txt b/fastlane/metadata/android/en-US/changelogs/default.txt index 0553de284f..76874cc530 100644 --- a/fastlane/metadata/android/en-US/changelogs/default.txt +++ b/fastlane/metadata/android/en-US/changelogs/default.txt @@ -1 +1,3 @@ -For detailed release notes, please visit: https://github.com/meshtastic/Meshtastic-Android/releases/ \ No newline at end of file +Stability and reliability fixes. + +Full release notes: https://github.com/meshtastic/Meshtastic-Android/releases diff --git a/scripts/check-metadata-length.py b/scripts/check-metadata-length.py index 7b5dbb236f..d6577c8e4f 100755 --- a/scripts/check-metadata-length.py +++ b/scripts/check-metadata-length.py @@ -30,6 +30,9 @@ METADATA_DIR = REPO_ROOT / "fastlane" / "metadata" / "android" LIMITS = { "short_description.txt": 80, "title.txt": 30, + # Play's "what's new" cap. A path key works because the glob below is + # rooted at the locale directory, not at a bare file name. + "changelogs/default.txt": 500, } # Running inside GitHub Actions enables ::error:: annotations on the PR. diff --git a/scripts/sync-play-changelog.py b/scripts/sync-play-changelog.py new file mode 100755 index 0000000000..650a7b3076 --- /dev/null +++ b/scripts/sync-play-changelog.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""Write the Play "what's new" text from the AppStream release description. + +metainfo.xml already carries one hand-written paragraph per version, required by +pull-request.yml on the VERSION_NAME_BASE bump. This renders that same paragraph +into fastlane/metadata/android/en-US/changelogs/default.txt, followed by a link to +the full release notes, so the store listing says what the release did. + +default.txt rather than a .txt: it is the file crowdin.yml already +maps, so translations continue in place instead of starting from zero on a new +source file every internal build. Play falls back to it for any build without a +version-specific file, which is all of them here. + +Run it in the PR that bumps VERSION_NAME_BASE, so Crowdin has the whole internal +cycle to translate before a production promotion uploads anything. + + python3 scripts/sync-play-changelog.py [--check] +""" +import re +import sys +import xml.etree.ElementTree as ET +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +METAINFO = REPO_ROOT / "desktopApp/packaging/linux/org.meshtastic.MeshtasticDesktop.metainfo.xml" +CONFIG = REPO_ROOT / "config.properties" +TARGET = REPO_ROOT / "fastlane/metadata/android/en-US/changelogs/default.txt" +RELEASES_URL = "https://github.com/meshtastic/Meshtastic-Android/releases" + +# Google Play caps "what's new" at 500 characters per locale. Translations run +# longer than English, so leave room rather than filling it. +PLAY_LIMIT = 500 +BUDGET = 380 + + +def version() -> str: + m = re.search(r"^VERSION_NAME_BASE=(.+)$", CONFIG.read_text(), re.M) + if not m: + sys.exit("VERSION_NAME_BASE not found in config.properties") + return m.group(1).strip() + + +def description(v: str) -> str: + root = ET.parse(METAINFO).getroot() + for rel in root.findall(".//release"): + if rel.get("version") != v: + continue + desc = rel.find("description") + if desc is None: + break + paras = [" ".join((p.text or "").split()) for p in desc.findall("p")] + paras = [p for p in paras if p] + if paras: + return " ".join(paras) + break + sys.exit( + f"no for {v} in {METAINFO.relative_to(REPO_ROOT)} - " + "add one alongside the VERSION_NAME_BASE bump" + ) + + +def render(text: str) -> str: + if len(text) > BUDGET: + cut = text[:BUDGET].rsplit(" ", 1)[0].rstrip(" .,;:") + text = f"{cut}…" + return f"{text}\n\nFull release notes: {RELEASES_URL}\n" + + +def main() -> int: + body = render(description(version())) + if len(body.rstrip()) > PLAY_LIMIT: + sys.exit(f"rendered text is {len(body.rstrip())} chars, over Play's {PLAY_LIMIT}") + if "--check" in sys.argv: + current = TARGET.read_text() if TARGET.exists() else "" + if current != body: + print( + f"::error file={TARGET.relative_to(REPO_ROOT)}::Play what's-new is stale. " + "Run python3 scripts/sync-play-changelog.py and commit the result.", + file=sys.stderr, + ) + return 1 + print("Play what's-new matches metainfo.") + return 0 + TARGET.write_text(body) + print(f"wrote {TARGET.relative_to(REPO_ROOT)} ({len(body.rstrip())} chars)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())