Build every template in smoke-gate order, index the full repository and sign packages plus metadata with the RSA4096 key, in the pinned Void container via podman. Fail-closed without a key: no key file, no repo. Ticket #29 (needs #27, #28).
184 lines
8.2 KiB
Markdown
184 lines
8.2 KiB
Markdown
# Void packaging
|
|
|
|
Void Linux (glibc, x86_64) templates in void-packages layout, built with the
|
|
standard Void source tooling. Submission to upstream void-packages is out of
|
|
scope for spec #16; these templates serve the signed personal xbps
|
|
repository first.
|
|
|
|
## Layout
|
|
|
|
```
|
|
packaging/void/
|
|
├── README.md this file
|
|
├── build.sh podman harness: lint + xbps-src build + import smoke
|
|
├── smoke.sh ticket #28 gate: every template, one command
|
|
├── repo.sh ticket #29 pipeline: build all, index, sign
|
|
├── container-repo.sh ticket #29 container half (index + sign + verify)
|
|
├── tool_checksums.txt pinned base image and void-packages commit
|
|
└── srcpkgs/
|
|
├── python3-pywhispercpp/ ticket #26: whisper.cpp Python binding
|
|
├── ... ticket #25: small Python dependencies
|
|
└── voculinux ticket #27: the application
|
|
```
|
|
|
|
`srcpkgs/` mirrors void-packages so the tree overlays cleanly:
|
|
`cp -r packaging/void/srcpkgs/. /path/to/void-packages/srcpkgs/`
|
|
(`build.sh` does this inside the container.)
|
|
|
|
## Templates
|
|
|
|
| Template | Ticket | Notes |
|
|
|---|---|---|
|
|
| `python3-pywhispercpp` | #26 | Vulkan backend on by default; see below |
|
|
| `voculinux` | #27 | The app, from the canonical release sdist; see below |
|
|
|
|
## python3-pywhispercpp
|
|
|
|
Version `1.5.0`, pinned to the same version `install.sh`
|
|
(`PYWHISPERCPP_VERSION`) and `uv.lock` pin — never newest upstream.
|
|
The sdist checksum in the template must equal the `sha256` uv.lock records
|
|
for the sdist; `tests/test_void_pywhispercpp_template.py` fails otherwise.
|
|
|
|
- **Vulkan** is a build option, on by default. Rebuild CPU-only with
|
|
`build.sh python3-pywhispercpp -- -o ~vulkan pkg`. There is no CUDA
|
|
build: Void ships no CUDA toolkit package. Rebuilding locally with a
|
|
hand-installed toolkit (`GGML_CUDA=1`) is a documented, unsupported
|
|
escape hatch.
|
|
- **Portable CPU flags**: every ggml SIMD option is pinned off
|
|
(`-DGGML_NATIVE=OFF -DGGML_SSE42=OFF ...`), the plain x86-64 baseline
|
|
already used by the Flatpak (`#938`, `#957`). The offline test asserts
|
|
each pin in the template (the build configuration), and the harness
|
|
scans the shipped `libggml-cpu` (`.GCC.command.line`, `.comment`) for
|
|
recorded `-march=native`/`-mcpu=native`/`-mtune=native` flags and fails
|
|
on a match. The recorded-flags scan is a tripwire, not a proof: the
|
|
proof is the pinned configuration plus `setup.py`'s documented
|
|
`CMAKE_ARGS` passthrough.
|
|
- **Shared libraries**: `BUILD_SHARED_LIBS=ON` with an `$ORIGIN` rpath
|
|
(as `install.sh` does), so the extension resolves the whisper.cpp
|
|
libraries installed beside it with no `LD_LIBRARY_PATH`.
|
|
- **Offline build**: the sdist vendors whisper.cpp, pybind11, and the
|
|
ggml sources; the build fetches nothing beyond the declared distfiles.
|
|
`PYWHISPERCPP_VERSION` is injected because the sdist carries no
|
|
`version.txt` (its version otherwise comes from git metadata).
|
|
- **Runtime deps** (`python3-numpy`, `python3-requests`, `python3-tqdm`,
|
|
`python3-platformdirs`) are the sdist's own `install_requires` and were
|
|
verified to exist in current void-packages; the container build
|
|
re-verifies via dependency resolution.
|
|
|
|
## voculinux
|
|
|
|
Version `0.18.5`, matching `src/vocalinux/version.py` — never newest
|
|
upstream. The template builds the canonical release source distribution
|
|
(the `releases/download` sdist, the same bytes published to PyPI) with a
|
|
pinned `checksum`; `tests/test_void_app_template.py` fails otherwise.
|
|
The 0.18.5 asset still carries the pre-rename archive root; xbps-src
|
|
moves the single top-level directory to `pkgname-version` itself, so no
|
|
`wrksrc` override is needed (`wrksrc` is deprecated and `xlint` rejects
|
|
it). The first release cut after the rename points `distfiles` at the
|
|
renamed asset.
|
|
|
|
- **Runtime deps** are Void's own interpreter, GTK and desktop bindings
|
|
(`python3`, `python3-gobject`, `python3-cairo`, `gtk+3`,
|
|
`libayatana-appindicator`, `ibus`, `gobject-introspection`), the
|
|
packaged Python libraries, and the three templates from tickets #25
|
|
and #26 (`python3-pynput`, `python3-PyAudio`, `python3-pywhispercpp`).
|
|
Every name was verified to exist in current void-packages; the
|
|
container build re-verifies via dependency resolution.
|
|
- **No payload beyond the app**: no speech models (they download on
|
|
first run), no vendored wheels, no pip at build or install time. The
|
|
container smoke fails the build if the payload carries model weights
|
|
or wheels.
|
|
- **Renamed paths**: the `voculinux.desktop` entry, the hicolor icons,
|
|
and a `usr/share/voculinux/packaged` marker the app reads to present
|
|
package-manager updates. The 0.18.5 sdist predates the rename, so
|
|
`post_install` promotes the old file names at install time; the payload
|
|
smoke rejects any pre-rename leftovers in the OS-visible paths.
|
|
- **INSTALL.msg** covers what xbps cannot express: the `input` group for
|
|
the evdev hotkey backend, first-run model downloads, optional engines
|
|
as manual additions (vosk, OpenAI whisper, Parakeet, faster-whisper,
|
|
onnxruntime), the absent `ydotool` rung, and updating through xbps.
|
|
- **Update checker**: packaged installs keep background checks as a
|
|
signal, but every update affordance (tray entry, notification, About
|
|
page) presents the package-manager path
|
|
(`src/vocalinux/utils/packaging.py`).
|
|
|
|
## Building
|
|
|
|
```sh
|
|
packaging/void/build.sh python3-pywhispercpp
|
|
packaging/void/build.sh python3-pywhispercpp -- -o ~vulkan pkg
|
|
packaging/void/build.sh voculinux
|
|
```
|
|
|
|
Podman only, never Docker. The base image digest and the void-packages
|
|
commit come from `tool_checksums.txt`; bump them there, never in the
|
|
script.
|
|
|
|
## Smoke gate
|
|
|
|
One command runs the whole gate from a clean checkout — no host Void
|
|
install — linting, building, installing and smoke-testing every
|
|
template under `srcpkgs` in dependency order (the app last):
|
|
|
|
```sh
|
|
packaging/void/smoke.sh
|
|
# or: just void-smoke
|
|
```
|
|
|
|
Each package delegates to `build.sh`, so the pinned container and the
|
|
per-package stages (xlint, xbps-src, install, payload/version/import
|
|
smoke) stay implemented once. The gate fails fast: a broken package
|
|
prints `FAIL: <pkgname>` naming which package broke, above it the
|
|
harness echoes which stage broke. `tests/test_void_smoke_gate.py`
|
|
asserts offline that the gate stays wired to every `srcpkgs` template,
|
|
so a renamed or added template cannot silently drop out.
|
|
|
|
## Signed repository
|
|
|
|
`packaging/void/repo.sh` builds every template in smoke-gate order, indexes
|
|
the full repository (`x86_64-repodata`), and signs every binary package
|
|
(`*.sig2`) plus the repository metadata with the RSA4096 repository key —
|
|
one command, same pinned container via podman, no host Void install:
|
|
|
|
```sh
|
|
# Run the smoke gate first: the repo assembles tested packages.
|
|
packaging/void/smoke.sh
|
|
# or: just void-smoke
|
|
|
|
printf '%s' "$XBPS_SIGNING_KEY" > /tmp/xbps-sign-key.pem
|
|
XBPS_SIGNING_KEY_FILE=/tmp/xbps-sign-key.pem packaging/void/repo.sh
|
|
# or: XBPS_SIGNING_KEY_FILE=/tmp/xbps-sign-key.pem just void-repo
|
|
```
|
|
|
|
The pipeline stages `publish/xbps/repo/` (packages, signatures, repodata)
|
|
plus `publish/xbps/keys/` (the client `.plist` named by key fingerprint,
|
|
and a `FINGERPRINT` file carrying the signer identity and fingerprint for
|
|
release notes). `publish/` is local build output and stays uncommitted.
|
|
|
|
Fail-closed: a missing, unreadable, or non-RSA4096 key aborts before any
|
|
container runs, so an unsigned repository can never publish. CI holds the
|
|
PEM-encoded key in the `XBPS_SIGNING_KEY` secret and writes it to a file
|
|
first (a file keeps newlines intact and the key out of process listings).
|
|
|
|
**Maintainer key setup (one time):**
|
|
|
|
```sh
|
|
openssl genrsa -out xbps-sign-key.pem 4096
|
|
# back the key up somewhere safe, then store it as XBPS_SIGNING_KEY
|
|
```
|
|
|
|
**Client setup:**
|
|
|
|
```sh
|
|
# as root on the Void system (the release notes carry the repo URL):
|
|
cp <fingerprint>.plist /var/db/xbps/keys/
|
|
echo 'repository=<repo-url-from-release-notes>' > /etc/xbps.d/voculinux.conf
|
|
xbps-install -Sy voculinux
|
|
```
|
|
|
|
Answer `Y` when xbps shows the fingerprint prompt on first sync, and check
|
|
it matches the published `FINGERPRINT` file. `tests/test_void_signed_repo.py`
|
|
asserts offline that the pipeline stays wired to every `srcpkgs` template,
|
|
stays podman-only on the pinned inputs, and keeps the fail-closed key
|
|
contract.
|