Files
MeshTerm/scripts/cardputer-zero/build-deb.sh
T
Jean-Pierre MartineauandClaude Opus 5.5 0b764e4a04 A script builds the Debian package for the Cardputer Zero's app store
scripts/cardputer-zero/build-deb.sh runs on arm64 Debian 13 (the
Cardputer itself) and makes meshterm_<version>-<revision>_arm64.deb.
MeshTerm and all its Python packages, the spi extra included, go in
/usr/lib/meshterm, compiled to bytecode. The package depends only on
python3: the store app installs a missing dependency with apt-get but
never runs apt-get update, so a Debian dependency can fail. For the
same reason the package carries Terminus beside its OFL, because an app
from the launcher has no terminal for --fetch-fonts.

packaging/notices.py gets a site-directory mode, because the walk of
the closure asks for no extras and so misses the spi packages.

scripts/cardputer-zero/store holds the listing that czdev publish reads
and a 256-pixel icon. The screenshots are still to make.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-07 02:39:27 -04:00

172 lines
7.5 KiB
Bash
Executable File

#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# build-deb -- build the Debian package of MeshTerm for the Cardputer Zero's app store.
#
# Usage: scripts/cardputer-zero/build-deb.sh [REVISION]
#
# Run it on arm64 Debian 13 ("Trixie") with Python 3.13: on the Cardputer Zero itself, or
# in a debian:trixie container on an arm64 machine. The package holds compiled code
# (spidev, pycryptodome, and others), so it must be built on the system that it is for.
# Build it from a clean checkout of a release tag. pip builds MeshTerm from this tree, and
# it also takes the files in meshterm/ that git does not track. On the Cardputer Zero, /tmp
# is in memory and too small for the build: set TMPDIR to a directory on the card.
#
# The package depends only on python3. All else is inside it:
#
# /usr/lib/meshterm/site MeshTerm and its Python packages, compiled to bytecode
# /usr/lib/meshterm/fonts Terminus 6x12, regular and bold, and its licence (the OFL)
# /usr/share/APPLaunch/ the launcher entry, its icon, and the wrapper that it runs
# /usr/bin/meshterm the command line, for a user on SSH
# /usr/share/doc/meshterm/ LICENSE, NOTICE, THIRD-PARTY-NOTICES.txt, and copyright
#
# We add no other Debian dependency on purpose. When a dependency is not installed, the
# store app runs `apt-get install`, but it never runs `apt-get update`. On a device with
# old package lists, that install can fail. For the same reason the package carries the
# font: Terminus is not in the repository or in the wheel, but a launcher app has no
# terminal in which the user can run `meshterm emulate --fetch-fonts`.
#
# REVISION is the Debian revision, 1 by default. Increase it to publish a new package of
# the same MeshTerm version, because the store refuses a version that is not newer.
# Set TERMINUS_ARCHIVE to the path of terminus-font-4.49.1.tar.gz if the download fails.
# The build checks that archive against the same digest.
set -eu
umask 022
HERE=$(cd "$(dirname "$0")" && pwd)
SOURCE=$(cd "$HERE/../.." && pwd)
REVISION=${1:-1}
PYTHON=/usr/bin/python3
die() {
echo "build-deb: $*" >&2
exit 1
}
command -v dpkg-deb >/dev/null || die "no dpkg-deb here -- run this on Debian"
[ "$(dpkg --print-architecture)" = arm64 ] || die "this is not arm64 -- the package holds arm64 code"
[ "$("$PYTHON" -c 'import sys; print("%d.%d" % sys.version_info[:2])')" = 3.13 ] ||
die "$PYTHON is not Python 3.13 -- the package is for Debian 13"
case "$REVISION" in
*[!0-9]* | "") die "the revision must be a number, not '$REVISION'" ;;
esac
VERSION=$(sed -n 's/^__version__ = "\(.*\)"$/\1/p' "$SOURCE/meshterm/__init__.py")
[ -n "$VERSION" ] || die "no __version__ in $SOURCE/meshterm/__init__.py"
DEB_VERSION="$VERSION-$REVISION"
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
STAGE="$WORK/root"
LIB="$STAGE/usr/lib/meshterm"
DOC="$STAGE/usr/share/doc/meshterm"
APPLAUNCH="$STAGE/usr/share/APPLaunch"
echo "building meshterm $DEB_VERSION from $SOURCE"
# A venv for the tools of the build only. It has a current pip and the `packaging` library
# that packaging/notices.py imports. Its base is the system Python, so the compiled
# packages that its pip builds are correct for the Python that runs them.
"$PYTHON" -m venv "$WORK/venv"
"$WORK/venv/bin/pip" install --quiet --upgrade pip packaging
# MeshTerm and its packages, with the radio of the Cap LoRa-1262 (the `spi` extra).
# --no-compile, because the bytecode is compiled below in a different mode.
"$WORK/venv/bin/pip" install --quiet --no-compile --target "$LIB/site" "$SOURCE[spi]"
# pip writes console scripts whose first line names the build venv. The wrappers below
# replace them.
rm -rf "$LIB/site/bin"
# The licences: MeshTerm's own, and the licence of each package in the site directory.
mkdir -p "$DOC"
cp "$SOURCE/LICENSE" "$SOURCE/NOTICE" "$DOC/"
"$WORK/venv/bin/python" "$SOURCE/packaging/notices.py" "$DOC/THIRD-PARTY-NOTICES.txt" "$LIB/site"
cat >"$DOC/copyright" <<EOF
MeshTerm $VERSION
Source: https://github.com/jpmartineau/MeshTerm
MeshTerm is licensed under the Apache License, Version 2.0. Refer to LICENSE and NOTICE in
this directory. The name "MeshTerm" and its logo are trademarks. NOTICE has the terms.
The Python packages in /usr/lib/meshterm/site have their own licences. Refer to
THIRD-PARTY-NOTICES.txt in this directory.
The Terminus font in /usr/lib/meshterm/fonts is licensed under the SIL Open Font
License, Version 1.1. Refer to /usr/lib/meshterm/fonts/OFL.TXT.
EOF
# Terminus, downloaded and checked against its digest by meshterm.emulator.fetch.
MESHTERM_EMULATOR_FONTS="$LIB/fonts" PYTHONPATH="$LIB/site" "$PYTHON" -s -c '
import os, sys
from pathlib import Path
from meshterm.emulator.fetch import install_terminus
archive = os.environ.get("TERMINUS_ARCHIVE")
install_terminus(Path(archive) if archive else None)
'
# Bytecode. The user cannot write in /usr/lib, so Python cannot cache bytecode there at
# run time. Without this step, each start compiles all of MeshTerm again, and the start is
# several seconds slower. "unchecked-hash": the files never change after the install, so
# Python does not compare them with the source at each import.
"$PYTHON" -m compileall -q -j 0 --invalidation-mode unchecked-hash "$LIB/site" >/dev/null
# The wrappers. PYTHONPATH is exported, so that a child process of MeshTerm finds the same
# packages. -s keeps the packages of the user's own site directory out.
wrapper() {
cat >"$1" <<EOF
#!/bin/sh
# $2
export PYTHONPATH=/usr/lib/meshterm/site
export MESHTERM_EMULATOR_FONTS="\${MESHTERM_EMULATOR_FONTS:-/usr/lib/meshterm/fonts}"
exec /usr/bin/python3 -s -m $3 "\$@"
EOF
chmod 755 "$1"
}
mkdir -p "$STAGE/usr/bin" "$APPLAUNCH/bin" "$APPLAUNCH/applications" "$APPLAUNCH/share/images"
wrapper "$STAGE/usr/bin/meshterm" "MeshTerm's command line." meshterm
wrapper "$APPLAUNCH/bin/meshterm" "MeshTerm from the launcher: it draws on the display itself." meshterm.emulator
# The launcher entry. Terminal=false gives the app the display and the keyboard.
cat >"$APPLAUNCH/applications/meshterm.desktop" <<EOF
[Desktop Entry]
Name=MeshTerm
Exec=/usr/share/APPLaunch/bin/meshterm
Icon=share/images/meshterm.png
Terminal=false
Type=Application
EOF
install -m 644 "$HERE/meshterm.png" "$APPLAUNCH/share/images/meshterm.png"
# Make sure that the bundle runs before it is packaged: the help, a tool's help (the tools
# are found at run time), the written pages (the specimen), and the font of the display.
# MESHTERM_HOME keeps the files that a run writes out of the home directory of the builder.
check() {
MESHTERM_HOME="$WORK/home" MESHTERM_EMULATOR_FONTS="$LIB/fonts" PYTHONPATH="$LIB/site" \
"$PYTHON" -s "$@" >/dev/null
}
check -m meshterm --help
check -m meshterm contacts --help
check -m meshterm specimen
check -c 'from meshterm.emulator.font import find_font; find_font()'
mkdir -p "$STAGE/DEBIAN"
cat >"$STAGE/DEBIAN/control" <<EOF
Package: meshterm
Version: $DEB_VERSION
Architecture: arm64
Maintainer: Jean-Pierre Martineau <johnputer@meshterm.net>
Installed-Size: $(du -sk "$STAGE" | cut -f1)
Depends: python3 (>= 3.13), python3 (<< 3.14)
Section: hamradio
Priority: optional
Homepage: https://meshterm.net
Description: full-featured MeshCore client for the Cardputer Zero
MeshTerm is a full-featured TUI MeshCore client. On the Cardputer Zero it
draws on the display itself, and it runs the Cap LoRa-1262 as its own radio.
It can also connect to a companion over USB, Bluetooth, or TCP.
EOF
mkdir -p "$SOURCE/dist"
OUT="$SOURCE/dist/meshterm_${DEB_VERSION}_arm64.deb"
dpkg-deb --root-owner-group -Zxz --build "$STAGE" "$OUT" >/dev/null
echo "built $OUT ($(du -h "$OUT" | cut -f1))"