From bca1c83dcd657e46d3a2d0ec4ef1d7deb7ad1b11 Mon Sep 17 00:00:00 2001 From: Jean-Pierre Martineau Date: Sun, 13 Sep 2026 15:50:53 -0400 Subject: [PATCH] MeshTerm 0.3.4 The macOS installers of 0.3.3 never built: the notices step refused pyobjc-core and pyobjc-framework-libdispatch, whose wheels ship no licence text. Their MIT text is vendored now, and a test checks every vendored path on every platform. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 16 ++++++++++- README.md | 12 ++++---- docs/cli.md | 2 +- meshterm/__init__.py | 2 +- packaging/notices.py | 9 ++++++ .../vendored-licenses/pyobjc-LICENSE.txt | 28 +++++++++++++++++++ tests/test_notices.py | 16 +++++++++++ 7 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 packaging/vendored-licenses/pyobjc-LICENSE.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index 3628086..410759c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,19 @@ allowed to change behaviour, not just add to it. ## [Unreleased] +## [0.3.4] — 2026-09-13 + +### Fixed + +- **The macOS installers build again.** 0.3.3's release never published: both macOS legs + stopped at the new third-party notices step, which refuses to bundle a dependency whose + wheel carries no licence text — and on macOS bleak's Bluetooth backend pulls in two such + wheels, `pyobjc-core` and `pyobjc-framework-libdispatch`. Their MIT text is vendored + beside pyserial's and pywinrt's now, verified against the wheels themselves, and a test + checks every vendored path on every platform so a gap that only one runner can hit is + caught here first. The 0.3.3 tag stands with no release behind it; this is the same + code with its installers. + ## [0.3.3] — 2026-09-13 ### Added @@ -697,7 +710,8 @@ deliberately not reconstructed here. - Two `TYPE_CHECKING` imports the test suite referenced but never imported, on paths that happened never to run. -[Unreleased]: https://github.com/jpmartineau/MeshTerm/compare/v0.3.3...HEAD +[Unreleased]: https://github.com/jpmartineau/MeshTerm/compare/v0.3.4...HEAD +[0.3.4]: https://github.com/jpmartineau/MeshTerm/releases/tag/v0.3.4 [0.3.3]: https://github.com/jpmartineau/MeshTerm/releases/tag/v0.3.3 [0.3.2]: https://github.com/jpmartineau/MeshTerm/releases/tag/v0.3.2 [0.3.1]: https://github.com/jpmartineau/MeshTerm/releases/tag/v0.3.1 diff --git a/README.md b/README.md index e840040..3e4961a 100644 --- a/README.md +++ b/README.md @@ -79,14 +79,14 @@ if anything goes wrong at startup the window closes before you can read why. **Windows** — open Windows Terminal or PowerShell, `cd` to your downloads, then: ```powershell -.\meshterm-0.3.3-windows-x64.exe +.\meshterm-0.3.4-windows-x64.exe ``` **macOS and Linux** — open Terminal, `cd` to your downloads, then: ```bash -chmod +x meshterm-0.3.3-* -./meshterm-0.3.3-* +chmod +x meshterm-0.3.4-* +./meshterm-0.3.4-* ``` ### A word about terminals, on Windows @@ -124,7 +124,7 @@ becomes just `meshterm` from anywhere. > project. > > - **macOS**: *"cannot be opened because the developer cannot be verified"*. Clear it with -> `xattr -d com.apple.quarantine meshterm-0.3.3-*` and run it again. +> `xattr -d com.apple.quarantine meshterm-0.3.4-*` and run it again. > - **Windows**: *"Windows protected your PC"*. Click **More info → Run anyway**. > > Every release ships a `SHA256SUMS` file if you'd rather check the download first. @@ -134,10 +134,10 @@ becomes just `meshterm` from anywhere. > MeshTerm uses that same folder. Set `MESHTERM_HOME` to try one in isolation: > > ```bash -> MESHTERM_HOME=~/meshterm-test ./meshterm-0.3.3-* # macOS, Linux +> MESHTERM_HOME=~/meshterm-test ./meshterm-0.3.4-* # macOS, Linux > ``` > ```powershell -> $env:MESHTERM_HOME = "$HOME\meshterm-test"; .\meshterm-0.3.3-windows-x64.exe +> $env:MESHTERM_HOME = "$HOME\meshterm-test"; .\meshterm-0.3.4-windows-x64.exe > ``` > > Same variable if you run two radios and want them kept apart. diff --git a/docs/cli.md b/docs/cli.md index e553654..e5be218 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -1460,7 +1460,7 @@ each link; the scripted face prints the link alone. **`--json`:** ```json -{"page":"discord","title":"Join Discord","version":"0.3.3","text":"Join the Discord\n Questions, ideas, bug reports, and mesh talk.\n\n • https://discord.gg/AZwe5Uvb3S","links":["https://discord.gg/AZwe5Uvb3S"]} +{"page":"discord","title":"Join Discord","version":"0.3.4","text":"Join the Discord\n Questions, ideas, bug reports, and mesh talk.\n\n • https://discord.gg/AZwe5Uvb3S","links":["https://discord.gg/AZwe5Uvb3S"]} ``` | Field | Type | Meaning | diff --git a/meshterm/__init__.py b/meshterm/__init__.py index dc54ae5..8715bb1 100644 --- a/meshterm/__init__.py +++ b/meshterm/__init__.py @@ -6,7 +6,7 @@ Supports USB, Bluetooth and TCP companion connection. For Windows, macOS and Lin from datetime import datetime -__version__ = "0.3.3" +__version__ = "0.3.4" __author__ = "Jean-Pierre Martineau" #: The year MeshTerm was first published; the copyright span starts here. diff --git a/packaging/notices.py b/packaging/notices.py index 9b636ad..d54cef7 100644 --- a/packaging/notices.py +++ b/packaging/notices.py @@ -63,8 +63,17 @@ ROOT_DISTRIBUTION = "mesh-term" #: in a Windows build's closure. All nine are generated and published together by the #: pywinrt project and share the one upstream license file, which none of their #: wheels include. +#: * ``pyobjc-core`` and ``pyobjc-framework-libdispatch`` — two of the four pyobjc +#: packages bleak's macOS BLE backend (``bleak.backends.corebluetooth``) pulls in on +#: ``sys_platform == "darwin"``, so they only appear in a macOS build's closure, which +#: is where the 0.3.3 release build caught them. Their 12.2.2 wheels ship no license +#: file; the other two (Cocoa, CoreBluetooth) do, and are read from their dist-info +#: like everything else. All four come from the one pyobjc repository and its single +#: MIT text. _VENDORED_LICENSE_GAPS: dict[str, tuple[str, ...]] = { "pyserial": ("vendored-licenses/pyserial-LICENSE.txt",), + "pyobjc-core": ("vendored-licenses/pyobjc-LICENSE.txt",), + "pyobjc-framework-libdispatch": ("vendored-licenses/pyobjc-LICENSE.txt",), "winrt-runtime": ("vendored-licenses/pywinrt-LICENSE.txt",), "winrt-windows-devices-bluetooth": ("vendored-licenses/pywinrt-LICENSE.txt",), "winrt-windows-devices-bluetooth-advertisement": ("vendored-licenses/pywinrt-LICENSE.txt",), diff --git a/packaging/vendored-licenses/pyobjc-LICENSE.txt b/packaging/vendored-licenses/pyobjc-LICENSE.txt new file mode 100644 index 0000000..8da32c1 --- /dev/null +++ b/packaging/vendored-licenses/pyobjc-LICENSE.txt @@ -0,0 +1,28 @@ +Vendored by packaging/notices.py -- not part of any pyobjc distribution. + +Two pyobjc wheels on PyPI ship no license file of any kind: pyobjc-core 12.2.2 and +pyobjc-framework-libdispatch 12.2.2 carry an MIT license classifier and nothing matching +LICENSE*, LICENCE*, COPYING*, or NOTICE* in their dist-info -- verified by inspecting the +macOS universal2 wheels directly (the two sibling wheels bleak also pulls in, +pyobjc-framework-Cocoa and pyobjc-framework-CoreBluetooth, do carry licenses/LICENSE.txt +and are read from there). First caught by the macOS legs of the 0.3.3 release build, which +refused to bundle them unnoticed; they only appear in a macOS build's closure, since bleak +marks them sys_platform == "darwin". + +All pyobjc packages are built and published together from the one pyobjc repository and +share its single license text, reproduced below unmodified. Pulled 2026-09-13 from: +https://raw.githubusercontent.com/ronaldoussoren/pyobjc/master/pyobjc-core/License.txt +Its copyright range (2003-2025) covers the pinned 12.2.2 release. + +---------------------------------------------------------------------------------------- + +(This is the MIT license, note that libffi-src is a separate product with its own license) + +Copyright 2002, 2003 - Bill Bumgarner, Ronald Oussoren, Steve Majewski, Lele Gaifax, et.al. +Copyright 2003-2025 - Ronald Oussoren + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/test_notices.py b/tests/test_notices.py index 60b35e1..d226071 100644 --- a/tests/test_notices.py +++ b/tests/test_notices.py @@ -146,3 +146,19 @@ def test_write_third_party_notices_writes_utf8_lf(tmp_path: Path) -> None: raw = out.read_bytes() assert b"\r\n" not in raw assert raw.decode("utf-8") == notices.render(notices.collect_notices()) + + +def test_every_vendored_license_gap_has_its_file() -> None: + """A gap-table entry whose file is missing would fail only on the platform that hits it. + + The pyobjc entries are exercised by a macOS build alone and the winrt ones by a + Windows build alone, so a typo in a path would surface on a release runner rather + than here. Check every entry on every platform: the file exists, is non-empty, and + the key is in the canonical (lowercase, hyphenated) form ``collect_notices`` looks up. + """ + for name, paths in notices._VENDORED_LICENSE_GAPS.items(): + assert name == name.lower().replace("_", "-"), name + for rel in paths: + path = Path(notices.__file__).parent / rel + assert path.is_file(), f"{name}: {rel} is missing" + assert path.read_text(encoding="utf-8").strip(), f"{name}: {rel} is empty"