#!/usr/bin/env bash # Build the punktfunk-scripting .deb — the plugin/script runner (the SDK's `punktfunk-scripting`, # built on Effect, run on bun). # # Runtime is BUN: the runner `import()`s the operator's `.ts` plugin/script files directly, which # only bun can do. Like the web console, we VENDOR a bun binary into the package (bun isn't in apt), # which makes the package per-arch (amd64/arm64), NOT `all`. Unlike the console it is NOT a Nitro # bundle: we `bun build` the runner CLI into ONE self-contained JS (effect + the SDK inlined; the # dynamic plugin import stays a runtime import), so there is no node_modules to ship. The host's # punktfunk-host .deb Recommends this so a default `apt install punktfunk-host` pulls the runner too; # its systemd --user unit is installed but NOT auto-enabled (the runner is inert until you add # scripts/plugins — enable it with `systemctl --user enable --now punktfunk-scripting`). # # Usage: VERSION=0.0.1~ci42.gdeadbee [DEB_ARCH=amd64] [BUN_BIN=/path/to/bun] bash packaging/debian/build-scripting-deb.sh # Output: dist/punktfunk-scripting__.deb set -euo pipefail VERSION="${VERSION:?set VERSION (e.g. 0.0.1 or 0.0.1~ci42.gdeadbee)}" PKG="punktfunk-scripting" ROOTDIR="$(cd "$(dirname "$0")/../.." && pwd)" cd "$ROOTDIR" # Per-arch: vendor bun for the target Debian arch. Map deb arch → bun's release arch tag. DEB_ARCH="${DEB_ARCH:-$(dpkg --print-architecture)}" BUN_VERSION="${BUN_VERSION:-1.3.14}" # pinned bun build vendored into the package (matches build-web-deb.sh) case "$DEB_ARCH" in amd64) BUN_ARCH=x64 ;; arm64) BUN_ARCH=aarch64 ;; *) echo "ERROR: unsupported DEB_ARCH=$DEB_ARCH (want amd64 or arm64)" >&2; exit 1 ;; esac STAGE="$(mktemp -d)" trap 'rm -rf "$STAGE"' EXIT SHAREDIR="$STAGE/usr/share/$PKG" DOCDIR="$STAGE/usr/share/doc/$PKG" LIBDIR="$STAGE/usr/lib/$PKG" # --- build the runner bundle ------------------------------------------------- # One self-contained JS: `bun build --target=bun` inlines effect + the @punktfunk/host SDK; the # runner's dynamic `import()` of the operator's plugin files is left as a runtime import (bun keeps # unresolvable dynamic specifiers external). `--ignore-scripts` on install: nothing needs a # postinstall, and we skip the `prepare` codegen (it wants ../api/openapi.json — not needed here). mkdir -p "$SHAREDIR" ( cd sdk bun install --frozen-lockfile --ignore-scripts bun build src/runner-cli.ts --target=bun --outfile="$SHAREDIR/runner-cli.js" ) grep -q 'attempt=' "$SHAREDIR/runner-cli.js" \ || { echo "ERROR: runner bundle missing the dynamic plugin import — wrong build" >&2; exit 1; } # --- vendor the bun runtime -------------------------------------------------- # Honor a pre-fetched bun (CI may cache it) via BUN_BIN; else download the pinned release. mkdir -p "$LIBDIR" if [ -n "${BUN_BIN:-}" ]; then echo "==> vendoring bun from BUN_BIN=$BUN_BIN" install -m0755 "$BUN_BIN" "$LIBDIR/bun" else url="https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/bun-linux-${BUN_ARCH}.zip" echo "==> downloading bun $BUN_VERSION ($BUN_ARCH) from $url" tmp="$(mktemp -d)" curl -fsSL "$url" -o "$tmp/bun.zip" unzip -q "$tmp/bun.zip" -d "$tmp" install -m0755 "$tmp/bun-linux-${BUN_ARCH}/bun" "$LIBDIR/bun" rm -rf "$tmp" fi "$LIBDIR/bun" --version # --- file layout ------------------------------------------------------------- # Stable PATH-independent launcher (the systemd unit's ExecStart) — runs the bundle on vendored bun. install -d "$STAGE/usr/bin" cat > "$STAGE/usr/bin/punktfunk-scripting" <<'WRAP' #!/bin/sh # The runner runs on the vendored bun (it import()s the operator's .ts plugins); bun lives privately # under /usr/lib/punktfunk-scripting so it never collides with a system-wide bun on PATH. exec /usr/lib/punktfunk-scripting/bun /usr/share/punktfunk-scripting/runner-cli.js "$@" WRAP chmod 0755 "$STAGE/usr/bin/punktfunk-scripting" install -Dm0644 scripts/punktfunk-scripting.service "$STAGE/usr/lib/systemd/user/punktfunk-scripting.service" install -Dm0644 LICENSE-MIT "$DOCDIR/LICENSE-MIT" install -Dm0644 LICENSE-APACHE "$DOCDIR/LICENSE-APACHE" install -Dm0644 sdk/README.md "$DOCDIR/README.md" cat > "$DOCDIR/copyright" < %s\n' \ "$PKG" "$VERSION" "$VERSION" "$(date -uR 2>/dev/null || echo 'Thu, 01 Jan 1970 00:00:00 +0000')" \ | gzip -9n > "$DOCDIR/changelog.Debian.gz" INSTALLED_KB="$(du -k -s "$STAGE" | cut -f1)" install -d "$STAGE/DEBIAN" cat > "$STAGE/DEBIAN/control" < Installed-Size: $INSTALLED_KB Section: net Priority: optional Homepage: https://git.unom.io/unom/punktfunk Description: punktfunk plugin/script runner (Effect SDK on bun) Runs a punktfunk host's automation: loose scripts in ~/.config/punktfunk/scripts and installed punktfunk-plugin-* packages under ~/.config/punktfunk/plugins, each supervised (Effect fibers with capped-jittered restart; SIGTERM shuts the whole tree down structurally so plugin finalizers run). Bundles its own bun runtime (no system nodejs/bun dependency). . ON BY DEFAULT: the systemd --user unit is enabled for every user (systemctl --global). The runner is inert until you add scripts or plugins, and the game-library scanners now ship AS plugins — so a host without the runner has an empty library and no obvious reason why. A plugin auto-wires to the host's mgmt token + identity cert on the same box — no env editing. Opt out per user with: systemctl --user mask punktfunk-scripting EOF cat > "$STAGE/DEBIAN/postinst" <<'EOF' #!/bin/sh set -e if [ "$1" = "configure" ]; then # `--global`, not `--user`: a maintainer script has no user session to act on, and this is the # only mechanism that makes a `--user` unit on-by-default for everyone (it symlinks into # /etc/systemd/user/…wants/). The library's scanners are plugins now, so the runner is a default # component rather than an add-on (design D9) — but installing it stays opt-OUT, and the opt-out # is `systemctl --user mask punktfunk-scripting`, since a plain `--user disable` cannot remove a # global symlink. # # Only on FIRST configure ($2 empty): re-running it on every upgrade would silently undo the # mask of anyone who turned it off. if [ -z "$2" ] && command -v systemctl >/dev/null 2>&1; then systemctl --global enable punktfunk-scripting.service >/dev/null 2>&1 || true fi echo "punktfunk-scripting installed and enabled for all users." echo "It runs your automation — game-library sources, scripts in" echo " ~/.config/punktfunk/scripts/ (loose .ts/.js files)" echo "and plugins under ~/.config/punktfunk/plugins/." echo "It starts with your next login; start it now with:" echo " systemctl --user start punktfunk-scripting" echo "Don't want it? systemctl --user mask punktfunk-scripting" fi exit 0 EOF chmod 0755 "$STAGE/DEBIAN/postinst" mkdir -p dist OUT="dist/${PKG}_${VERSION}_${DEB_ARCH}.deb" dpkg-deb --root-owner-group --build "$STAGE" "$OUT" >/dev/null echo "built $OUT" dpkg-deb -I "$OUT" | sed -n 's/^/ /p' | grep -E 'Version|Installed-Size|Depends' || true