bandsaunter/packaging/build-deb.sh
The Dust Council 8d94a52942 Package the speech recogniser and its model for apt
No speech recogniser is in Debian, so installing bandsaunter from a .deb
left transcription to a manual pip step on every machine. A repository of
one's own is not bound by archive policy, so build-repo.sh now packages
faster-whisper and the base.en model alongside the application:

  bandsaunter                the application (Architecture: all)
  bandsaunter-transcribe     faster-whisper, vendored (amd64)
  bandsaunter-model-base-en  the model, so nothing reaches the network

The wheels land in /usr/lib/bandsaunter/vendor rather than dist-packages,
and transcribe.py appends that directory to sys.path -- appends, so an
apt-managed numpy or PyYAML still wins and the vendor copy only fills the
gap. Duplicates of what Debian already ships are stripped from the tree.
resolve_model() turns a bare "base.en" into the packaged copy when one is
installed, and leaves it alone to be downloaded when none is.

The app package recommends the other two, so "apt install bandsaunter"
brings the lot and --no-install-recommends still gets just the scanner.
Its postinst explains how to add a recogniser only when there genuinely
is not one -- including the case where apt has already unpacked the
recogniser package but not yet configured it.

Verified with the source tree hidden and no home directory: the packaged
CLI runs, and a real recording transcribes offline from the vendored
engine and packaged model while numpy still resolves to the system one.
apt itself resolves the repository over HTTP and plans all three.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 22:12:24 -07:00

109 lines
4.1 KiB
Bash
Executable file

#!/bin/sh
# Build a .deb that pulls in every dependency through apt.
#
# Deliberately plain dpkg-deb rather than debhelper: everything this package
# needs is already in Debian, the payload is pure Python with no compiled
# parts, and this way the build needs nothing installed beyond dpkg itself.
set -eu
here=$(cd "$(dirname "$0")/.." && pwd)
version=$(cd "$here" && python3 -c 'import bandsaunter; print(bandsaunter.debian_version())')
revision=${DEB_REVISION:-1}
arch=all
stage=$(mktemp -d)
trap 'rm -rf "$stage"' EXIT
pkgdir="$stage/bandsaunter_${version}-${revision}_${arch}"
site="$pkgdir/usr/lib/python3/dist-packages"
mkdir -p "$site/bandsaunter" "$pkgdir/usr/bin" "$pkgdir/DEBIAN" \
"$pkgdir/usr/share/doc/bandsaunter"
cp "$here"/bandsaunter/*.py "$site/bandsaunter/"
cp "$here/README.md" "$pkgdir/usr/share/doc/bandsaunter/"
gzip -9n "$pkgdir/usr/share/doc/bandsaunter/README.md"
cat > "$pkgdir/usr/bin/bandsaunter" <<'EOF'
#!/usr/bin/python3
import sys
from bandsaunter.cli import main
sys.exit(main())
EOF
chmod 755 "$pkgdir/usr/bin/bandsaunter"
# Every one of these is in Debian, so apt resolves the lot. No speech
# recogniser is packaged for Debian, so that part ships as its own package
# (built by build-repo.sh) and is recommended rather than depended on: apt
# pulls it in from the repository, and a bare "dpkg -i" of this file still
# installs, without transcription.
cat > "$pkgdir/DEBIAN/control" <<EOF
Package: bandsaunter
Version: ${version}-${revision}
Section: hamradio
Priority: optional
Architecture: ${arch}
Depends: python3 (>= 3.10), python3-numpy, python3-scipy, python3-rich,
python3-yaml, librtlsdr0
Recommends: bandsaunter-transcribe, espeak-ng
Suggests: rtl-sdr
Maintainer: bandsaunter
Installed-Size: $(du -ks "$pkgdir" | cut -f1)
Description: signal scanner and recorder for RTL-SDR receivers
Sweeps any set of frequency ranges, or presets from a built-in US band plan,
records what it finds and works out what kind of signal it was.
.
Captures are kept only if they carry voice, decodable CW or an identified
digital keying scheme, so static and interference are discarded rather than
filling the disk. CW is decoded to text, and speech can be transcribed when
a recogniser is installed.
EOF
cat > "$pkgdir/DEBIAN/postinst" <<'EOF'
#!/bin/sh
set -e
if [ "$1" = configure ]; then
# The DVB-T driver claims the dongle on sight and has to be kept off it.
if [ ! -e /etc/modprobe.d/blacklist-rtlsdr.conf ]; then
echo 'blacklist dvb_usb_rtl28xxu' > /etc/modprobe.d/blacklist-rtlsdr.conf
echo 'bandsaunter: blacklisted dvb_usb_rtl28xxu; unplug and replug the'
echo ' receiver, or run: sudo rmmod dvb_usb_rtl28xxu'
fi
# Said once, at install time, only when there is genuinely nothing to
# transcribe with -- otherwise the recogniser package has already
# arrived and there is nothing to explain.
if ! python3 -c 'import importlib.util as u, sys
sys.path.append("/usr/lib/bandsaunter/vendor")
try:
found = any(u.find_spec(m) for m in
("faster_whisper", "whisper", "vosk", "pocketsphinx"))
except Exception:
found = True # something is there, however unhappy; say nothing
sys.exit(0 if found else 1)' \
2>/dev/null; then
echo 'bandsaunter: speech transcription is unavailable -- no recogniser'
echo ' is installed. Scanning, recording and CW decoding all'
echo ' work without one. To add transcription, either:'
echo ' apt install bandsaunter-transcribe'
echo ' (from the repository this package came from), or'
echo ' pip install --break-system-packages faster-whisper'
echo ' Then: bandsaunter transcribe --engines'
fi
fi
exit 0
EOF
chmod 755 "$pkgdir/DEBIAN/postinst"
cat > "$pkgdir/DEBIAN/prerm" <<'EOF'
#!/bin/sh
set -e
if [ "$1" = remove ] || [ "$1" = purge ]; then
rm -f /etc/modprobe.d/blacklist-rtlsdr.conf
fi
exit 0
EOF
chmod 755 "$pkgdir/DEBIAN/prerm"
out=${1:-$here/dist}
mkdir -p "$out"
fakeroot dpkg-deb --build "$pkgdir" "$out" >/dev/null
echo "$out/bandsaunter_${version}-${revision}_${arch}.deb"