diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 457913c9..539a0b59 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -70,7 +70,7 @@ body: label: Steps to reproduce description: Numbered steps that reliably trigger the issue. placeholder: | - 1. Open MeshChatX 4.8.0 on Windows 11 + 1. Open MeshChatX on Windows 11 2. Add an RNode interface with port ble://... 3. Restart the app validations: diff --git a/meshchatx.rsm b/meshchatx.rsm index 8b8c85e8..33d62f60 100644 Binary files a/meshchatx.rsm and b/meshchatx.rsm differ diff --git a/scripts/sync_version.js b/scripts/sync_version.js index 87ba2796..381846be 100644 --- a/scripts/sync_version.js +++ b/scripts/sync_version.js @@ -9,6 +9,7 @@ * meshchatx/src/backend/data/licenses_backend.json (reticulum-meshchatx entry), * android/app/build.gradle, * electron/app-version.json, + * .github/ISSUE_TEMPLATE bug and feature version placeholders, * pipx example, packaging/arch/PKGBUILD pkgver / printf fallback, * then runs scripts/bake_build_meta.js (git commit / channel overlay). * @@ -141,6 +142,15 @@ console.log(`Synced version ${version} from package.json`); const appVersionPath = path.join(root, "electron", "app-version.json"); writeIfChanged(appVersionPath, `${JSON.stringify({ version }, null, 4)}\n`); +patchFile(".github/ISSUE_TEMPLATE/bug_report.yml", (c) => { + let x = c.replace(/placeholder: "\d+\.\d+\.\d+"/, `placeholder: "${version}"`); + x = x.replace(/\(for example \d+\.\d+\.\d+\)/, `(for example ${version})`); + return x; +}); +patchFile(".github/ISSUE_TEMPLATE/feature_request.yml", (c) => + c.replace(/placeholder: "\d+\.\d+\.\d+"/, `placeholder: "${version}"`) +); + try { require("./bake_build_meta.js"); } catch (err) { diff --git a/tests/backend/test_version_sync.py b/tests/backend/test_version_sync.py new file mode 100644 index 00000000..a83019b7 --- /dev/null +++ b/tests/backend/test_version_sync.py @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: 0BSD + +"""Synced version files must match package.json after pnpm run version:sync.""" + +from __future__ import annotations + +import json +import re +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[2] + + +def _package_version() -> str: + version = json.loads((ROOT / "package.json").read_text(encoding="utf-8"))["version"] + assert isinstance(version, str) and re.match(r"^\d+\.\d+\.\d+", version) + return version + + +def _text(rel: str) -> str: + return (ROOT / rel).read_text(encoding="utf-8") + + +def test_electron_app_version_json_matches_package(): + data = json.loads(_text("electron/app-version.json")) + assert data["version"] == _package_version() + + +def test_python_version_strings_match_package(): + version = _package_version() + expected = f'__version__ = "{version}"' + assert expected in _text("meshchatx/__init__.py") + assert expected in _text("meshchatx/src/version.py") + + +def test_pyproject_version_matches_package(): + assert re.search( + rf'^version = "{re.escape(_package_version())}"', + _text("pyproject.toml"), + re.M, + ) + + +def test_android_version_matches_package(): + version = _package_version() + major, minor, patch = (int(part) for part in version.split(".")[:3]) + version_code = major * 10000 + minor * 1000 + patch + gradle = _text("android/app/build.gradle") + assert f'versionName "{version}"' in gradle + assert f"versionCode {version_code}" in gradle + + +def test_readme_current_version_lines_match_package(): + version = _package_version() + checks = ( + ("README.md", f"Current version in this repo is `{version}`."), + ("lang/README.de.md", f"Aktuelle Version in diesem Repository: `{version}`."), + ("lang/README.it.md", f"Versione attuale nel repository: `{version}`."), + ("lang/README.ja.md", f"このリポジトリの現在のバージョンは `{version}` です。"), + ("lang/README.ru.md", f"Текущая версия в репозитории: `{version}`."), + ("lang/README.zh.md", f"本仓库当前版本: `{version}`。"), + ) + for rel, needle in checks: + assert needle in _text(rel), rel + + +def test_notices_and_backend_license_match_package(): + version = _package_version() + notices = _text("meshchatx/src/backend/data/THIRD_PARTY_NOTICES.txt") + assert re.search(rf"^reticulum-meshchatx {re.escape(version)}$", notices, re.M) + licenses = json.loads(_text("meshchatx/src/backend/data/licenses_backend.json")) + entry = next(item for item in licenses if item.get("name") == "reticulum-meshchatx") + assert entry["version"] == version + + +def test_arch_packaging_version_matches_package(): + version = _package_version() + pkgbuild = _text("packaging/arch/PKGBUILD") + assert re.search(rf"^pkgver={re.escape(version)}", pkgbuild, re.M) + assert f'printf "{version}.r%s.%s"' in pkgbuild + srcinfo = _text("packaging/arch/.SRCINFO") + assert re.search(rf"^\tpkgver = {re.escape(version)}", srcinfo, re.M) + + +def test_issue_template_placeholders_match_package(): + version = _package_version() + bug = _text(".github/ISSUE_TEMPLATE/bug_report.yml") + feature = _text(".github/ISSUE_TEMPLATE/feature_request.yml") + assert f'placeholder: "{version}"' in bug + assert f"for example {version}" in bug + assert f'placeholder: "{version}"' in feature + + +def test_raspberry_pi_docs_match_package(): + version = _package_version() + wheel = f"reticulum_meshchatx-{version}-py3-none-any.whl" + for rel in ( + "docs/en/platform-guides/raspberry-pi.md", + "meshchatx/src/frontend/public/meshchatx-docs/en/platform-guides/raspberry-pi.md", + ): + text = _text(rel) + assert f"({version} or newer)" in text, rel + assert f"Direct example (v{version}):" in text, rel + assert wheel in text, rel diff --git a/tests/electron/appVersion.test.js b/tests/electron/appVersion.test.js index c62303a1..a0f38359 100644 --- a/tests/electron/appVersion.test.js +++ b/tests/electron/appVersion.test.js @@ -1,8 +1,21 @@ +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; import { describe, expect, it } from "vitest"; import { readPackagedAppVersion } from "../../electron/appVersion.js"; +const repoRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", ".."); + +function readJson(relPath) { + return JSON.parse(fs.readFileSync(path.join(repoRoot, relPath), "utf8")); +} + describe("electron/appVersion", () => { - it("reads version from electron/app-version.json", () => { - expect(readPackagedAppVersion("0.0.0")).toBe("4.8.3"); + it("reads the synced package.json version from electron/app-version.json", () => { + const pkgVersion = readJson("package.json").version; + const appVersion = readJson("electron/app-version.json").version; + expect(pkgVersion).toMatch(/^\d+\.\d+\.\d+/); + expect(appVersion).toBe(pkgVersion); + expect(readPackagedAppVersion("0.0.0")).toBe(pkgVersion); }); });