Package through a pinned Node, not whatever the host has

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 <noreply@anthropic.com>
This commit is contained in:
Rene Kievits
2026-08-26 12:51:13 +02:00
co-authored by Claude Opus 5
parent f622c3a0bf
commit f3a4cddfd6
2 changed files with 58 additions and 8 deletions
+14 -2
View File
@@ -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 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 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 whichever matches the container, so on Apple Silicon it runs natively rather
than under emulation. Only the compile happens in the container; packaging and than under emulation. Only the compile happens in the container; deployment
deployment run on the host, where the TV is reachable. 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, Register the TV with ares once, using the Homebrew Channel's ssh (port 9922,
root): root):
+44 -6
View File
@@ -68,17 +68,55 @@ EOF
echo "$toolchain" echo "$toolchain"
} }
check_ares() { PACKAGER_NODE_IMAGE="${PACKAGER_NODE_IMAGE:-node:18-bookworm-slim}"
command -v ares-package >/dev/null 2>&1 || cat >&2 <<'EOF'
error: ares-package is not on PATH
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): 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'}" ares-setup-device --add tv --info "{'host':'192.168.1.20','port':9922,'username':'root'}"
EOF 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() { build_native() {
@@ -132,7 +170,7 @@ package() {
step "Packaging" step "Packaging"
mkdir -p "$OUT_DIR" mkdir -p "$OUT_DIR"
rm -f "$OUT_DIR"/${APP_ID}_*.ipk 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 local ipk
ipk="$(ls -t "$OUT_DIR"/${APP_ID}_*.ipk | head -1)" ipk="$(ls -t "$OUT_DIR"/${APP_ID}_*.ipk | head -1)"