ci: build identifiable NanoKVM release packages

This commit is contained in:
Guoguo
2026-07-31 03:47:54 -07:00
parent ef432da2fe
commit b23eb33611
9 changed files with 1084 additions and 2 deletions

92
scripts/README.md Normal file
View File

@@ -0,0 +1,92 @@
# Release packaging
These scripts assemble the application update package that NanoKVM devices pull
over the air: `nanokvm_<version>.tar.gz` plus its `latest.json` manifest.
| Script | Purpose |
|---|---|
| `build-in-container.sh` | Builds every riscv64 artifact (`kvm_system`, `libkvm.so`, `NanoKVM-Server`). Runs inside the `nanokvm-builder` image only. |
| `package.sh` | Stages the package tree, creates the tarball, and writes `latest.json`. |
| `compare-release.sh` | Diffs a freshly built package against the currently published one. Informational. |
## What the updater expects
The format is not arbitrary — it is fixed by the on-device updater in
`server/service/application/`:
- **One root directory.** `install.go` untars the package and moves the single
top-level directory over `/kvmapp`, so the tarball must contain exactly
`nanokvm_<version>/`.
- **`name` is the file name.** `version.go` builds the download URL as
`<base>/<name>`, where `<base>` is `https://cdn.sipeed.com/nanokvm` (or
`.../preview` when `/etc/kvm/preview_updates` exists).
- **`sha512` is base64, not hex.** `update.go` compares against
`base64(raw sha512 digest)`. A hex digest will fail verification on device.
- **`/kvmapp/version`** is what the device reports as its installed version, so
it must match the `version` field.
`size` is parsed into `Latest.Size` but never read anywhere, so nothing on the
device depends on its units. Published manifests have carried a kilobyte-ish
value; `package.sh` writes the exact byte count instead.
## Building a release
The frontend needs Node and pnpm on the host; everything riscv64 needs the
builder image (see `docker/Dockerfile`).
```bash
make release VERSION=2.4.4
```
That is equivalent to:
```bash
make release-build # kvm_system + libkvm.so + NanoKVM-Server, in Docker
make web # web/dist
make package VERSION=2.4.4 # build/release/{nanokvm_2.4.4.tar.gz,latest.json}
```
In CI this runs as the **NanoKVM Package** workflow. Pull requests get a
short-lived Actions artifact for device testing; pushing a `MAJOR.MINOR.PATCH`
tag creates or updates a GitHub release with three assets:
- `nanokvm_<version>.tar.gz`
- `latest.json`
- `sha256.txt`
The Actions artifact also includes `BUILD_INFO.txt` with the source commit,
workflow run, immutable builder image digest, and both checksum encodings; that
provenance file is not attached to the GitHub release. Uploading the tarball and
`latest.json` to the CDN is what actually offers the update to devices in the
field, so that step stays manual. `sha256.txt` is only for users verifying a
manual download and does not change the on-device `latest.json` contract.
## Where each file in the package comes from
| Package path | Source |
|---|---|
| `version` | the requested version |
| `server/NanoKVM-Server` | `server/build.sh` (BoringCrypto, RPATH `$ORIGIN/dl_lib`) |
| `server/dl_lib/` | whatever the `kvm_vision` build emits, with any library it does not emit backfilled from the tracked `server/dl_lib/` |
| `server/web/` | `web/dist/` |
| `kvm_system/kvm_system` | `support/sg2002` `build kvm_system` |
| `system/tool/` | prebuilt binaries in `tools/nanokvm_update_edid/` |
| `kvm/` | default runtime state (resolution, fps, quality) |
| everything else | tracked `kvmapp/` |
Note that `kvm/` ships default runtime state, and `install.go` replaces
`/kvmapp` wholesale, so an update resets those values on the device. That is
long-standing behaviour, not something these scripts introduce.
## The two copies of libkvm.so
`NanoKVM-Server` is cgo-linked against the **tracked** `server/dl_lib/libkvm.so`
(`server/common/kvm_vision.go`: `-L../dl_lib -lkvm`), but the package ships the
library the `kvm_vision` build just produced. Those are two different files, and
the tracked one can lag well behind — published releases have shipped this
mismatch for a long time.
Because a symbol the binary imports could in principle be absent from the
shipped library, and that would only surface as a crash on a real device,
`build-in-container.sh` checks the shipped `libkvm.so` still exports every
symbol `NanoKVM-Server` actually imports from it, and fails the build otherwise.