#!/bin/bash # build-deb.sh — stage and build the jray-server Debian package. # # Built with dpkg-deb from an explicit staging tree rather than with cargo-deb. # The reason is debconf: its `config` script and `templates` live in the control # archive alongside the maintainer scripts, and controlling that archive directly # is simpler than discovering what a wrapper will and will not copy into it. # dpkg-dev is present on any Debian builder, so this adds no build dependency. # # Usage: # scripts/build-deb.sh # build the binary, then package # scripts/build-deb.sh --binary path/to/bin # package an existing binary # scripts/build-deb.sh --version 1.2.3 # override the computed version # scripts/build-deb.sh --out dist # output directory set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$REPO_ROOT" TARGET="x86_64-unknown-linux-musl" BINARY="" VERSION="" OUT="$REPO_ROOT/dist" while [ $# -gt 0 ]; do case "$1" in --binary) BINARY="${2:?--binary needs a path}"; shift ;; --version) VERSION="${2:?--version needs a value}"; shift ;; --out) OUT="${2:?--out needs a path}"; shift ;; -h|--help) sed -n '2,16p' "${BASH_SOURCE[0]}"; exit 0 ;; *) echo "error: unknown argument '$1'" >&2; exit 2 ;; esac shift done # ── Version ───────────────────────────────────────────────────────────────── # # A tagged commit packages as that tag. Anything else packages as a PRE-release # of the version in Cargo.toml: `0.1.0~git20260905.9bcc765` sorts BELOW `0.1.0` # in dpkg's ordering, because `~` sorts before everything including the empty # string. That is what makes a master build upgradeable to the eventual release # rather than blocking it — the mistake would be `0.1.0+git...`, which sorts # above and would leave apt refusing the real 0.1.0. if [ -z "$VERSION" ]; then if tag=$(git describe --exact-match --tags HEAD 2>/dev/null); then VERSION="${tag#v}" else cargo_version=$(sed -n 's/^version *= *"\(.*\)"/\1/p' Cargo.toml | head -1) VERSION="${cargo_version}~git$(date -u +%Y%m%d).$(git rev-parse --short HEAD)" fi fi # ── Binary ────────────────────────────────────────────────────────────────── if [ -z "$BINARY" ]; then echo "=== building $TARGET" cargo build --release --target "$TARGET" BINARY="target/$TARGET/release/jray-server" fi [ -f "$BINARY" ] || { echo "error: no binary at $BINARY" >&2; exit 1; } # The package claims no libc dependency, which is only honest if the binary # genuinely has none. Asserted with `file` rather than `ldd`: a musl static-PIE # makes ldd print the musl loader path, so an ldd check calls a static binary # dynamic. Same reasoning as the Dockerfile and the CI musl job. if command -v file >/dev/null 2>&1; then linkage=$(file -b "$BINARY") case "$linkage" in *"static-pie linked"*|*"statically linked"*) ;; *) echo "error: $BINARY is not static ($linkage)." >&2 echo " The package declares no libc dependency, so a dynamic" >&2 echo " binary here would install cleanly and then fail to run." >&2 exit 1 ;; esac fi # ── Stage ─────────────────────────────────────────────────────────────────── STAGE="$(mktemp -d)" trap 'rm -rf "$STAGE"' EXIT install -d -m 0755 "$STAGE/DEBIAN" install -d -m 0755 "$STAGE/usr/bin" install -d -m 0755 "$STAGE/lib/systemd/system" install -d -m 0755 "$STAGE/usr/share/doc/jray-server/examples" install -m 0755 "$BINARY" "$STAGE/usr/bin/jray-server" install -m 0644 packaging/jray-server.service "$STAGE/lib/systemd/system/jray-server.service" install -m 0644 packaging/examples/nginx-jray-server.conf \ "$STAGE/usr/share/doc/jray-server/examples/nginx-jray-server.conf" install -m 0644 SPEC.md "$STAGE/usr/share/doc/jray-server/SPEC.md" install -m 0644 README.md "$STAGE/usr/share/doc/jray-server/README.md" # Licences. LICENSE-DATA is not the code licence: contributed manifests are CC0 # while the server itself is GPLv3, and shipping only one of them would misstate # what the operator is redistributing. install -m 0644 LICENSE "$STAGE/usr/share/doc/jray-server/LICENSE" install -m 0644 LICENSE-DATA "$STAGE/usr/share/doc/jray-server/LICENSE-DATA" install -m 0755 packaging/debian/config "$STAGE/DEBIAN/config" install -m 0755 packaging/debian/postinst "$STAGE/DEBIAN/postinst" install -m 0755 packaging/debian/prerm "$STAGE/DEBIAN/prerm" install -m 0755 packaging/debian/postrm "$STAGE/DEBIAN/postrm" install -m 0644 packaging/debian/templates "$STAGE/DEBIAN/templates" SIZE=$(du -ks "$STAGE" | cut -f1) # No libc, no libsqlite3, no CA bundle: SQLite is compiled in (`rusqlite` # bundled), the TLS roots are compiled in (`reqwest` rustls-tls uses # webpki-roots), and the target is musl static. The only dependencies are the # two the maintainer scripts themselves call. cat > "$STAGE/DEBIAN/control" <= 0.5) | debconf-2.0, init-system-helpers (>= 1.54) Installed-Size: $SIZE Maintainer: Duncan Tourolle Homepage: https://gitea.tourolle.paris/dtourolle/JRay-public-server Description: JRay public server - community manifest exchange Serves and accepts JRay manifests: per-actor scene windows contributed by media-centre users and matched to a cut by runtime and audio signature. . One static binary and one SQLite file, behind a reverse proxy the operator provides. The server stores no binary content by design - no images, no embeddings, no opaque blobs - which is what makes it safe for a volunteer to run. . An example nginx site is installed under /usr/share/doc/jray-server/examples/, to be copied to whichever host runs your proxy. EOF mkdir -p "$OUT" DEB="$OUT/jray-server_${VERSION}_amd64.deb" dpkg-deb --root-owner-group --build "$STAGE" "$DEB" >/dev/null echo "=== built $DEB" dpkg-deb --info "$DEB" | sed 's/^/ /' echo "=== contents" dpkg-deb --contents "$DEB" | sed 's/^/ /'