From f3a4cddfd6af5265587f3549df40c2932aea1bf0 Mon Sep 17 00:00:00 2001 From: Rene Kievits Date: Wed, 26 Aug 2026 12:51:13 +0200 Subject: [PATCH] Package through a pinned Node, not whatever the host has MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found the real cause of "-5: ipk verify failed": ares-package's own packaging code (ar-async/fstream/tar, last touched 2017-2019) silently zeroes every file's mtime in the ipk when run under a sufficiently new Node (reproduced on v25.8.1; a Node-18 container was unaffected). Confirmed by unpacking data.tar.gz from our built ipk (every entry 1970-01-01) versus webosbrew/hyperhdr-webos-loader's real published ipk, structurally identical to ours (web app + native service, same control file, same ares-cli) but with genuine October 2025 timestamps. The archive still parses fine everywhere generic tooling looks — Python's tarfile, our own ar/tar inspection — so nothing here ever errors. The TV's own installer is what eventually rejects it, and it gives no hint why. This took three rounds of elimination to isolate: root elevation wasn't it (Homebrew Channel's own root status was "ok"), and system-wide native-code verification wasn't it either (a real native-service app installed fine on the same TV). Comparing our ipk against that known-good one byte-for-byte was what surfaced the timestamp anomaly, and rebuilding under Node 18 reproduced correct timestamps immediately. build.sh now runs ares-package inside a pinned node:18 container by default, falling back to the host's Node with a warning if Docker isn't available. Rebuilt the actual release ipk this way and regenerated manifest.json/repo.json against its corrected hash. Co-Authored-By: Claude Opus 5 --- docs/development.md | 16 +++++++++++++-- tools/build.sh | 50 +++++++++++++++++++++++++++++++++++++++------ 2 files changed, 58 insertions(+), 8 deletions(-) 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)"