Merge branch 'main' into cardputer-zero

This commit is contained in:
Jean-Pierre Martineau committed 2026-10-03 23:37:52 -04:00
commit a7ce313bf0
3 files changed
+110 -5

No files matched your search

+18 -3
View File
@@ -36,7 +36,9 @@ jobs:
build:
name: ${{ matrix.label }}
runs-on: ${{ matrix.os }}
timeout-minutes: 25
# Windows gets an extra hour: a signed build waits for the maintainer to approve it on
# SignPath (see "Sign it" below), and that wait is spent inside this job.
timeout-minutes: ${{ matrix.os == 'windows-latest' && 90 || 25 }}
strategy:
fail-fast: false
matrix:
@@ -137,9 +139,17 @@ jobs:
# Both legs are skipped wholesale until the account exists (the variable is unset
# until then), so this file builds unsigned Windows binaries in the meantime rather
# than failing, and a fork with no SignPath project of its own is unaffected.
#
# SignPath Foundation's terms make a person approve every signing request, so each
# one waits on the maintainer. Only a release (a tag, arriving through the release
# workflow's call) or a run started by hand asks; a push to `main` that touched the
# packaging builds unsigned, rather than sending an approval request nobody wanted
# and failing the run when it went unanswered.
- name: Hand the Windows binary to SignPath
id: unsigned
if: runner.os == 'Windows' && vars.SIGNPATH_ORGANIZATION_ID != ''
if: >-
runner.os == 'Windows' && vars.SIGNPATH_ORGANIZATION_ID != '' &&
(github.ref_type == 'tag' || github.event_name == 'workflow_dispatch')
uses: actions/upload-artifact@v4
with:
# Deliberately not `meshterm-*`: the release workflow attaches every artifact
@@ -149,7 +159,7 @@ jobs:
if-no-files-found: error
- name: Sign it
if: runner.os == 'Windows' && vars.SIGNPATH_ORGANIZATION_ID != ''
if: steps.unsigned.outcome == 'success'
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
@@ -161,6 +171,11 @@ jobs:
# The signed file comes back where the unsigned one was built, under the same
# name, so every step after this one is untouched by any of it.
wait-for-completion: true
# An hour to approve it, where the action's own default is ten minutes: long
# enough to see SignPath's email and answer it from a phone, so a release
# doesn't fail because the maintainer stepped away. The job's timeout above
# leaves room for it.
wait-for-completion-timeout-in-seconds: 3600
output-artifact-directory: packaging/dist
- name: Name it after the version and the platform
+35 -2
View File
@@ -122,8 +122,12 @@ Whichever you took, you now have a single file: `meshterm` on macOS and Linux,
`meshterm-windows-x64.exe` on Windows. Put it somewhere on your `PATH` (renamed to
`meshterm.exe` on Windows) and it's just `meshterm` from anywhere.
> **These builds aren't code-signed**, which is why macOS and Windows both push back the
> first time; signing costs real money on both platforms and this is a free side project.
> **These builds aren't code-signed yet**, which is why macOS and Windows both push back
> the first time. **Windows signing is on its way.** MeshTerm has applied to the
> [SignPath Foundation](https://signpath.org), which signs open-source projects for free.
> Once that's set up, every Windows download will be signed. The
> [code signing policy](#code-signing-policy) below explains how. Mac builds will stay
> unsigned, because Apple charges for signing and this is a free side project.
> Every release ships a `SHA256SUMS` file if you'd rather check what you got, and
> [installing with pip or pipx](#or-install-with-pip) sidesteps the whole business, since
> nothing arrives as a downloaded binary.
@@ -324,6 +328,35 @@ Also here: [CONTRIBUTING.md](CONTRIBUTING.md) ·
[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) ·
[CHANGELOG.md](CHANGELOG.md)
## Code signing policy
Free code signing provided by [SignPath.io](https://signpath.io), certificate by
[SignPath Foundation](https://signpath.org).
This is for the Windows download. It's being set up now, so releases up to and including
0.10.2 are still unsigned.
**How a build gets signed.** Every Windows build is made by GitHub Actions, straight from
the public source code in this repository. SignPath only signs a file it can trace back to
one of those builds, and only after the maintainer approves that release. A file built on
anyone's own computer can't be signed this way, not even the maintainer's.
**Who does what.** MeshTerm has one maintainer,
[Jean-Pierre Martineau](https://github.com/jpmartineau), who holds all three roles:
- **Author:** writes the code and commits it.
- **Reviewer:** reviews every change from anyone else before it goes in.
- **Approver:** approves each release before it gets signed.
**Privacy.** This program will not transfer any information to other networked systems
unless specifically requested by the user or the person installing or operating it.
In practice, MeshTerm goes online for one thing: map tiles. It downloads them from
[OpenFreeMap](https://openfreemap.org/) when you open a screen that shows a map, and
OpenFreeMap's [privacy policy](https://openfreemap.org/privacy) covers those downloads. It
collects nothing about you and doesn't check for updates. Over the radio, it sends only
what you ask it to send.
## Acknowledgements & attribution
MeshTerm stands on the [MeshCore](https://meshcore.io/) project — its firmware and the
+57
View File
@@ -9,6 +9,7 @@
# Built on the machine it targets. There is no cross-compiling here — the Windows build
# comes off a Windows runner, the macOS one off macOS. That is what the workflow is for.
import re
import sys
from PyInstaller.utils.hooks import collect_submodules
@@ -34,6 +35,61 @@ third_party_notices = notices.write_third_party_notices(
os.path.join(workpath, "THIRD-PARTY-NOTICES.txt")
)
# The Windows build's version resource: the Details tab of the file's Properties, and the
# name and version Windows shows for it. Without one the .exe is just "meshterm.exe" with
# no product behind it, and SignPath refuses to sign it — their signing configuration
# checks the product name and version written here, so a file that doesn't carry them
# can't be passed off as a MeshTerm release. Built from the package's own `__version__`
# rather than kept as a second copy that would drift. The other platforms have no such
# resource, and PyInstaller warns if handed one there.
version_info = None
if sys.platform == "win32":
from PyInstaller.utils.win32.versioninfo import (
FixedFileInfo,
StringFileInfo,
StringStruct,
StringTable,
VarFileInfo,
VarStruct,
VSVersionInfo,
)
# The checkout being frozen, not whichever MeshTerm the interpreter happens to have
# installed: `Analysis` below reads its sources from here too.
sys.path.insert(0, os.path.dirname(SPECPATH))
import meshterm # noqa: E402 (import must follow the sys.path edit above)
# The resource's fixed half holds exactly four numbers: 0.10.2 → (0, 10, 2, 0). A
# suffix such as `rc1` has nowhere to go there and is dropped; the text fields below
# keep the version exactly as written.
numbers = [int(n) for n in re.match(r"\d+(?:\.\d+)*", meshterm.__version__)[0].split(".")]
numbers = tuple((numbers + [0, 0, 0, 0])[:4])
version_info = VSVersionInfo(
ffi=FixedFileInfo(filevers=numbers, prodvers=numbers),
kids=[
StringFileInfo(
[
# 0409 is US English and 04B0 says the strings are Unicode — the
# pairing nearly every Windows program declares.
StringTable(
"040904B0",
[
StringStruct("CompanyName", meshterm.__author__),
StringStruct("FileDescription", "MeshTerm"),
StringStruct("FileVersion", meshterm.__version__),
StringStruct("InternalName", "meshterm"),
StringStruct("LegalCopyright", meshterm.copyright_notice()),
StringStruct("OriginalFilename", "meshterm.exe"),
StringStruct("ProductName", "MeshTerm"),
StringStruct("ProductVersion", meshterm.__version__),
],
)
]
),
VarFileInfo([VarStruct("Translation", [0x0409, 1200])]),
],
)
# The assets have to land at `meshterm/assets`, because `ui/about.py` finds them with
# `Path(__file__).parent.parent / "assets"` and that path has to keep resolving inside
# the bundle exactly as it does in a checkout.
@@ -105,4 +161,5 @@ exe = EXE(
target_arch=None,
codesign_identity=None,
entitlements_file=None,
version=version_info,
)