diff --git a/docs/development.md b/docs/development.md index f64b461..fedb4a0 100644 --- a/docs/development.md +++ b/docs/development.md @@ -27,8 +27,20 @@ Everywhere else, build the native part in a container: That bakes the SDK into an image, so it downloads once and later builds start immediately. There are aarch64 and x86_64 SDK builds and the image picks whichever matches the container, so on Apple Silicon it runs natively rather -than under emulation. Only the compile happens in the container; packaging and -deployment run on the host, where the TV is reachable. +than under emulation. Only the compile happens in the container; deployment +runs on the host, where the TV is reachable. + +Packaging (`ares-package`) also runs in a container by default — pinned to +Node 18, not whatever Node the host has. This isn't optional hygiene: on a +newer Node (v22+, confirmed on v25) `ares-package`'s own dependencies +(`fstream`/`tar`, last touched around 2017-2019) silently zero out every +timestamp in the ipk instead of erroring. The archive still parses fine +everywhere generic tools look, so nothing here fails — the TV's installer is +what eventually rejects it, as an opaque `-5: ipk verify failed` with no +indication why. If Docker isn't available, `build.sh` falls back to the host's +own Node with a warning; if installs fail mysteriously in that mode, this is +the first thing to suspect — checked by unpacking `data.tar.gz` from the ipk +and confirming the timestamps aren't 1970-01-01. Register the TV with ares once, using the Homebrew Channel's ssh (port 9922, root): diff --git a/tools/build.sh b/tools/build.sh index f52192b..7186c0b 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -68,17 +68,55 @@ EOF echo "$toolchain" } -check_ares() { - command -v ares-package >/dev/null 2>&1 || cat >&2 <<'EOF' -error: ares-package is not on PATH +PACKAGER_NODE_IMAGE="${PACKAGER_NODE_IMAGE:-node:18-bookworm-slim}" - npm install -g @webosose/ares-cli +check_ares() { + [ -f "$ROOT/node_modules/@webosose/ares-cli/bin/ares-package.js" ] && return 0 + command -v ares-package >/dev/null 2>&1 && return 0 + cat >&2 <<'EOF' +error: ares-cli is not installed + + npm install Then register the TV once (developer mode or the Homebrew Channel's ssh): ares-setup-device --add tv --info "{'host':'192.168.1.20','port':9922,'username':'root'}" EOF - command -v ares-package >/dev/null 2>&1 + return 1 +} + +# ares-package's own packaging code (ar-async/fstream/tar, all last touched +# around 2017-2019) silently mishandles file metadata on very new Node +# releases: every mtime in the ipk comes out as 1970-01-01 instead of the real +# date. The archive still parses, so nothing here errors — the TV's installer +# is what eventually rejects it, as "ipk verify failed" with no clue why. +# Running the same ares-cli under a pinned, known-good Node avoids the whole +# class of bug regardless of what's on the host. +run_ares_package() { + local ares_js="$ROOT/node_modules/@webosose/ares-cli/bin/ares-package.js" + if [ -f "$ares_js" ] && command -v docker >/dev/null 2>&1; then + # Arguments are host paths under $ROOT; rewrite them to the container's + # mount point since nothing outside $ROOT is visible in there. + local args=() a + for a in "$@"; do + args+=("${a/#$ROOT//src}") + done + docker run --rm \ + --volume "$ROOT:/src" \ + --workdir /src \ + --user "$(id -u):$(id -g)" \ + --env HOME=/tmp \ + "$PACKAGER_NODE_IMAGE" \ + node /src/node_modules/@webosose/ares-cli/bin/ares-package.js "${args[@]}" + return + fi + if [ -f "$ares_js" ]; then + say "warning: no docker, running ares-package on the host's own Node ($(node --version 2>/dev/null))" + say " if the ipk fails to install with a vague error, re-run with docker installed" + node "$ares_js" "$@" + return + fi + ares-package "$@" } build_native() { @@ -132,7 +170,7 @@ package() { step "Packaging" mkdir -p "$OUT_DIR" rm -f "$OUT_DIR"/${APP_ID}_*.ipk - ares-package "$STAGE_APP" "$STAGE_SERVICE" -o "$OUT_DIR" + run_ares_package "$STAGE_APP" "$STAGE_SERVICE" -o "$OUT_DIR" local ipk ipk="$(ls -t "$OUT_DIR"/${APP_ID}_*.ipk | head -1)"