ci: pick the webserver bind address from the installed schema
Sync upstream and build images / sync-and-build (push) Successful in 38m42s

The 0.2.1-alpha.2 fix rendered `host` onto the container's own address
unconditionally. Every earlier release still validates it against the union of
the two literals 127.0.0.1 and 0.0.0.0, so those images now die in the boot
smoke test with

  @deepseek-ai/dsh-host-webserver: ValidationError: invalid config:
    - $.host expected "127.0.0.1" | "0.0.0.0" but got "172.17.0.22" (at host)

and their refresh rebuilds -- which the recipe-hash change re-queued anyway --
can no longer succeed. Probe the installed webserver schema instead: where it
rejects the unspecified address render the container's own IPv4, otherwise keep
0.0.0.0 so older versions build exactly as they did before. The probe is
overridable through DSH_INSTALL_ROOT so both branches are testable.
This commit is contained in:
dsh-relay committed 2026-10-13 21:09:56 -04:00
1 parent 8363be933d
commit 0af110766d
3 files changed
+45 -26

No files matched your search

+5 -4
View File
@@ -8,10 +8,11 @@
#
# No upstream code is patched. The bind address comes from the `--patch`
# launcher layer (/opt/deepseek-harness/web.cordis.patch.yml), which the
# entrypoint renders onto the container's own IPv4 address at startup: dsh
# 0.2.1-alpha.2+ rejects the unspecified address in the webserver schema, so a
# static 0.0.0.0 would no longer boot. The UI is reachable only through ports
# you explicitly publish on the host.
# entrypoint renders at startup: dsh 0.2.1-alpha.2+ rejects the unspecified
# address in the webserver schema and gets the container's own IPv4 instead,
# while releases whose schema still only accepts 127.0.0.1 or 0.0.0.0 keep
# 0.0.0.0. The UI is reachable only through ports you explicitly publish on the
# host.
FROM node:22-bookworm-slim
+30 -13
View File
@@ -38,22 +38,39 @@ else
-*) set -- web --patch "$PATCH" --no-open "$@" ;;
esac
fi
# dsh 0.2.1-alpha.2 validates the webserver bind address in a config transform
# and rejects the unspecified address outright — "host … is an unspecified
# (wildcard) address, which is not supported … bind one concrete IPv4 or IPv6
# address of a local interface instead" — and no launcher flag can opt out of a
# schema-level check. The shipped patch is therefore a template; render it onto
# this container's own address and hand dsh the rendered copy, so a port
# published on the host still reaches the UI while the socket binds one
# concrete interface instead of every interface. DSH_WEB_HOST overrides the
# discovered address.
# 0.2.1-alpha.2 replaced the webserver's old `host: "127.0.0.1" | "0.0.0.0"`
# union with a config transform that rejects the unspecified address outright —
# "host … is an unspecified (wildcard) address, which is not supported … bind
# one concrete IPv4 or IPv6 address of a local interface instead" — and a
# schema-level check cannot be opted out of from the launcher. The shipped patch
# is therefore a template: render it onto this container's own address and hand
# dsh the rendered copy, so a port published on the host still reaches the UI
# while the socket binds one concrete interface instead of every interface.
# Releases whose schema still accepts the wildcard keep 0.0.0.0, so old images
# rebuild exactly as before. DSH_WEB_HOST overrides the address.
if [ -r "$PATCH" ]; then
BIND_HOST="${DSH_WEB_HOST:-}"
if [ -z "$BIND_HOST" ]; then
# Same discovery dsh's own boot smoke test uses: first non-internal IPv4.
BIND_HOST="$(node -e 'const os=require("os");const n=Object.values(os.networkInterfaces()).flat().find(i=>i&&i.family==="IPv4"&&!i.internal);process.stdout.write(n?n.address:"")' 2>/dev/null || true)"
# Detect the installed webserver schema rather than guessing from a
# version string.
dsh_root="${DSH_INSTALL_ROOT:-/usr/local/lib/node_modules/@deepseek-ai/dsh}"
ws="$dsh_root/node_modules/@deepseek-ai/dsh-host-webserver/lib/index.js"
if [ -f "$ws" ] && grep -qs 'unspecified (wildcard) address' "$ws"; then
wildcard_rejected=1
elif [ ! -f "$ws" ] && grep -rqs 'unspecified (wildcard) address' "$dsh_root" 2>/dev/null; then
wildcard_rejected=1
else
wildcard_rejected=0
fi
if [ "$wildcard_rejected" = 1 ]; then
# Same discovery dsh's own boot smoke test uses: first non-internal IPv4.
BIND_HOST="$(node -e 'const os=require("os");const n=Object.values(os.networkInterfaces()).flat().find(i=>i&&i.family==="IPv4"&&!i.internal);process.stdout.write(n?n.address:"")' 2>/dev/null || true)"
[ -n "$BIND_HOST" ] || BIND_HOST=127.0.0.1
else
BIND_HOST=0.0.0.0
fi
fi
[ -n "$BIND_HOST" ] || BIND_HOST=127.0.0.1
render_patch() {
sed -e "s|__DSH_BIND_HOST__|${BIND_HOST}|g" \
@@ -67,7 +84,7 @@ if [ -r "$PATCH" ]; then
render_patch > "$RUNTIME_PATCH"
fi
chmod 0644 "$RUNTIME_PATCH" 2>/dev/null || true
log "webserver binds ${BIND_HOST} (dsh 0.2.1+ rejects the unspecified address); patch: ${RUNTIME_PATCH}"
log "webserver binds ${BIND_HOST}; runtime patch: ${RUNTIME_PATCH}"
# Point every --patch argument that names the shipped template at the
# rendered copy. Rebuilt with a rotating `set --`, so quoting survives.
+10 -9
View File
@@ -1,16 +1,17 @@
# Launcher patch layer for containerized `dsh web` (passed via --patch).
#
# The host is a placeholder, not a value: dsh 0.2.1-alpha.2 rejects the
# unspecified address in the webserver schema ("host … is an unspecified
# The host is a placeholder, not a value. From 0.2.1-alpha.2 the webserver
# schema rejects the unspecified address outright ("host … is an unspecified
# (wildcard) address, which is not supported … bind one concrete IPv4 or IPv6
# address of a local interface instead"), and that check is a config transform,
# so no launcher flag can opt out of it. A static 0.0.0.0 — which the earlier
# 0.1.x schema accepted — now fails the boot smoke test.
# address of a local interface instead") and that check is a config transform,
# so no launcher flag can opt out of it; a static 0.0.0.0 — which the earlier
# `"127.0.0.1" | "0.0.0.0"` schema accepted — now fails the boot smoke test.
#
# The image entrypoint renders this file onto the container's own IPv4 address
# at startup and passes the rendered copy to dsh. A port published on the
# Docker host still reaches the UI, because the published port is forwarded to
# exactly that address. Set DSH_WEB_HOST to pin a different address.
# The image entrypoint renders this file at startup: onto the container's own
# IPv4 address when the installed schema rejects the wildcard, otherwise back
# to 0.0.0.0 so older images build unchanged. A port published on the Docker
# host still reaches the UI either way, because the published port is forwarded
# to exactly that address. Set DSH_WEB_HOST to pin a different address.
- id: webserver
config:
host: __DSH_BIND_HOST__