From 616dc5dc6461378b9cd3cf0a7ba0fe08a2445cec Mon Sep 17 00:00:00 2001 From: mpuig Date: Tue, 11 Aug 2026 16:51:48 +0200 Subject: [PATCH 1/3] installer: build the builder image on Linux and on release installs Source-to-image builds boot builder VMs from hypeman/builder:latest, and the API's fallback for installed (non-source) services is to find that image in the local Docker daemon -- lib/builds/manager.go's own comment says "the installer builds this image before loading the service". The installer only did so in its darwin branch, and only for source (BRANCH) installs. A Linux release install therefore had no builder image and no way to grow one: builder preparation retried forever, and every `hypeman build` failed. The builder-image step now runs on every platform where Docker is present, and release installs fetch the source tarball for the exact installed version to build from -- the builder Dockerfile is part of the same tree the binaries were released from. Skipped when the image already exists, warn-and-continue (matching the existing style) when it cannot be built. --- scripts/install.sh | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/scripts/install.sh b/scripts/install.sh index 67405d46..03558658 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -645,30 +645,53 @@ EOF fi # ============================================================================= -# Build builder image (macOS) +# Build builder image (all platforms) # ============================================================================= - -if [ "$OS" = "darwin" ]; then - info "Attempting to build builder image..." - if command -v docker >/dev/null 2>&1; then +# +# Source-to-image builds boot builder VMs from hypeman/builder:latest, and the +# API's fallback for installed (non-source) services is to find that image in +# the local Docker daemon (lib/builds/manager.go). Previously only the macOS +# source-install path built it, so a Linux release install could never run +# `hypeman build`: builder preparation retried forever and every build failed. + +if command -v docker >/dev/null 2>&1; then + if docker image inspect hypeman/builder:latest >/dev/null 2>&1; then + info "Builder image hypeman/builder:latest already present, skipping build" + else + info "Building builder image..." if [ -n "$BRANCH" ] && [ -d "${TMP_DIR}/hypeman" ]; then BUILD_CONTEXT="${TMP_DIR}/hypeman" + elif [ -n "$VERSION" ] && [ "${VERSION#v}" != "$VERSION" ]; then + # Release install: no source checkout on disk, so fetch the source + # for the exact installed version — the builder Dockerfile the API + # expects is part of the same tree. + info "Fetching source for ${VERSION} to build the builder image..." + if curl -fsSL "https://github.com/${REPO}/archive/refs/tags/${VERSION}.tar.gz" -o "${TMP_DIR}/hypeman-src.tar.gz" \ + && mkdir -p "${TMP_DIR}/hypeman-src" \ + && tar -xzf "${TMP_DIR}/hypeman-src.tar.gz" -C "${TMP_DIR}/hypeman-src" --strip-components=1; then + BUILD_CONTEXT="${TMP_DIR}/hypeman-src" + else + BUILD_CONTEXT="" + warn "Failed to fetch source for ${VERSION}; skipping builder image build" + fi else BUILD_CONTEXT="" fi if [ -n "$BUILD_CONTEXT" ] && [ -f "${BUILD_CONTEXT}/lib/builds/images/generic/Dockerfile" ]; then if ! docker build -t hypeman/builder:latest -f "${BUILD_CONTEXT}/lib/builds/images/generic/Dockerfile" "$BUILD_CONTEXT" 2>/dev/null; then - warn "Failed to build builder image. You can build it later manually." + warn "Failed to build builder image. Source builds will not work until it exists: docker build -t hypeman/builder:latest -f lib/builds/images/generic/Dockerfile " else info "Builder image built successfully" fi - else + elif [ -z "$BUILD_CONTEXT" ]; then warn "Builder image Dockerfile not available. Build it manually: docker build -t hypeman/builder:latest -f lib/builds/images/generic/Dockerfile ." + else + warn "Builder image Dockerfile not found in source; build it manually: docker build -t hypeman/builder:latest -f lib/builds/images/generic/Dockerfile ." fi - else - warn "Docker not available, skipping builder image build" fi +else + warn "Docker not available, skipping builder image build (hypeman build will not work without it)" fi if [ "$OS" = "darwin" ]; then From b6514816c3265b7eef39a5f6f54ee16d3247db8c Mon Sep 17 00:00:00 2001 From: mpuig Date: Tue, 11 Aug 2026 18:05:09 +0200 Subject: [PATCH 2/3] installer: escalate docker through $SUDO and keep the build log Bugbot's review finding, and it is right: the script's documented Linux invocation elevates privileged operations through $SUDO, but the new builder-image step ran docker as the invoking user -- who may not be in the docker group -- so the step would warn-and-skip and leave exactly the gap this PR fixes. Docker access is now probed and escalated through the same $SUDO the rest of the script uses. Also stop discarding the docker build output: it goes to a log file whose path the failure warning names, instead of 2>/dev/null-ing the only evidence of why a build failed. --- scripts/install.sh | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/scripts/install.sh b/scripts/install.sh index 03558658..f69cd89a 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -655,7 +655,17 @@ fi # `hypeman build`: builder preparation retried forever and every build failed. if command -v docker >/dev/null 2>&1; then - if docker image inspect hypeman/builder:latest >/dev/null 2>&1; then + # `docker` needs daemon access, which the invoking user may not have on + # Linux (not in the `docker` group); the rest of the script already + # escalates privileged operations through $SUDO, so do the same here + # rather than failing the one step this script exists to make work. + DOCKER="docker" + if ! docker info >/dev/null 2>&1; then + if [ -n "$SUDO" ] && $SUDO docker info >/dev/null 2>&1; then + DOCKER="$SUDO docker" + fi + fi + if $DOCKER image inspect hypeman/builder:latest >/dev/null 2>&1; then info "Builder image hypeman/builder:latest already present, skipping build" else info "Building builder image..." @@ -679,8 +689,9 @@ if command -v docker >/dev/null 2>&1; then fi if [ -n "$BUILD_CONTEXT" ] && [ -f "${BUILD_CONTEXT}/lib/builds/images/generic/Dockerfile" ]; then - if ! docker build -t hypeman/builder:latest -f "${BUILD_CONTEXT}/lib/builds/images/generic/Dockerfile" "$BUILD_CONTEXT" 2>/dev/null; then - warn "Failed to build builder image. Source builds will not work until it exists: docker build -t hypeman/builder:latest -f lib/builds/images/generic/Dockerfile " + BUILDER_BUILD_LOG="${TMP_DIR}/builder-image-build.log" + if ! $DOCKER build -t hypeman/builder:latest -f "${BUILD_CONTEXT}/lib/builds/images/generic/Dockerfile" "$BUILD_CONTEXT" > "$BUILDER_BUILD_LOG" 2>&1; then + warn "Failed to build builder image (log: ${BUILDER_BUILD_LOG}). Source builds will not work until it exists: docker build -t hypeman/builder:latest -f lib/builds/images/generic/Dockerfile " else info "Builder image built successfully" fi From 42cbab4910d738df61ad815e43e0815b96390c6d Mon Sep 17 00:00:00 2001 From: mpuig Date: Wed, 12 Aug 2026 08:32:36 +0200 Subject: [PATCH 3/3] installer: print builder build output on failure instead of a doomed log path The builder-image build log lives under TMP_DIR, which the EXIT trap removes when the script finishes, so the path named in the warning was gone by the time anyone read it. Print the captured docker build output to stderr on failure, before cleanup runs (per review feedback). --- scripts/install.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/install.sh b/scripts/install.sh index f69cd89a..bc9e576e 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -691,7 +691,11 @@ if command -v docker >/dev/null 2>&1; then if [ -n "$BUILD_CONTEXT" ] && [ -f "${BUILD_CONTEXT}/lib/builds/images/generic/Dockerfile" ]; then BUILDER_BUILD_LOG="${TMP_DIR}/builder-image-build.log" if ! $DOCKER build -t hypeman/builder:latest -f "${BUILD_CONTEXT}/lib/builds/images/generic/Dockerfile" "$BUILD_CONTEXT" > "$BUILDER_BUILD_LOG" 2>&1; then - warn "Failed to build builder image (log: ${BUILDER_BUILD_LOG}). Source builds will not work until it exists: docker build -t hypeman/builder:latest -f lib/builds/images/generic/Dockerfile " + # TMP_DIR is removed by the EXIT trap, so print the captured output + # now rather than naming a log path that will not survive the install. + warn "Failed to build builder image; docker build output follows:" + sed 's/^/ /' "$BUILDER_BUILD_LOG" >&2 + warn "Source builds will not work until it exists: docker build -t hypeman/builder:latest -f lib/builds/images/generic/Dockerfile " else info "Builder image built successfully" fi