From ead2dca00175ad10c922763fec7d1ccfbe113628 Mon Sep 17 00:00:00 2001 From: David Ficociello Date: Wed, 15 Apr 2026 20:47:24 -0400 Subject: [PATCH] fix: rewrite Dockerfile for CI build context (not monorepo) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous Dockerfile assumed the docker build context was the beacon monorepo root directory (/data/home/davidfic/dev/beacon/), with haul/, web-shared/, and pulse/ as sibling directories. That works for local `docker build -f haul/docker/Dockerfile .` runs from the monorepo root but falls over immediately in CI because the GitHub workflow's docker build context is the haul repo root, and none of haul/web/ui/package.json, web-shared/, or pulse/ exist at those paths — they live at web/ui/package.json, web/ui/src/shared/ (vendored via git subtree now), and a go.mod require github.com/beacon-stack/pulse respectively. CI was failing with: failed to calculate checksum of ref ...: "/web-shared": not found failed to calculate checksum of ref ...: "/haul/web/ui/package.json": not found Rewrites the Dockerfile to match the CI build context (haul repo root) and to stop relying on the sibling-dir layout: - Frontend stage copies web/ui/ directly (instead of haul/web/ui/) - Drops the separate COPY web-shared/ step — the shared components are vendored into web/ui/src/shared/ via git subtree, so they come along with the rest of web/ui/. - Drops the pulse sibling copy and the `sed` rewrite of the go.mod replace directive. That replace directive was removed entirely when we pinned pulse v0.1.0 via a normal require, so the Go build stage just runs `go mod download` and `go build` with no cross-module surgery. - Output binary lands at /haul and gets copied into the final stage at /usr/local/bin/haul, matching the pilot/prism conventions. - Still uses CGO_ENABLED=0 and alpine:3.21 as before. Co-Authored-By: Claude Opus 4.6 (1M context) --- docker/Dockerfile | 64 ++++++++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index f05a55a..d35ad78 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,55 +1,61 @@ # ── Frontend build stage ────────────────────────────────────────────────────── FROM node:22-alpine AS frontend -WORKDIR /app/web/ui +WORKDIR /app -COPY haul/web/ui/package.json haul/web/ui/package-lock.json ./ -RUN npm ci +# Copy the frontend package files first so the dependency install layer +# caches until they change. +COPY web/ui/package.json web/ui/package-lock.json ./web/ui/ +RUN cd web/ui && npm ci -COPY haul/web/ui/ ./ - -# Shared UI components (Modal, ConfirmDialog). The tsconfig paths + vite -# alias resolve `@beacon-shared/*` to `../../../web-shared/*`, and from -# WORKDIR `/app/web/ui` that's `/web-shared`. Install web-shared's own -# minimal devDependencies (react + @types/react) so TypeScript can find -# react types when type-checking files from that directory. -COPY web-shared/ /web-shared/ -RUN cd /web-shared && npm install --no-audit --no-fund --loglevel=error - -RUN npm run build +# Copy the rest of the frontend source, including the vendored shared +# components under src/shared/ (git subtree from beacon-stack/web-shared). +COPY web/ui/ ./web/ui/ +RUN cd web/ui && npm run build # Output lands in /app/web/static/ # ── Go build stage ─────────────────────────────────────────────────────────── FROM golang:1.25-alpine AS builder -RUN apk add --no-cache git +ARG VERSION=dev +ARG BUILD_TIME=unknown -WORKDIR /src +WORKDIR /app -# Copy Pulse SDK (needed for local replace directive) -COPY pulse/ /pulse/ +# Download dependencies first so the layer caches until go.mod/go.sum change. +COPY go.mod go.sum ./ +RUN go mod download -# Copy Haul source -COPY haul/ . +# Full source after dependency fetch. +COPY . . -# Overwrite web/static/ with the freshly built frontend +# Overwrite web/static/ with the freshly-built frontend from the earlier stage. COPY --from=frontend /app/web/static/ ./web/static/ -# Rewrite the replace directive to point to the copied Pulse module -RUN sed -i 's|replace github.com/beacon-stack/pulse => .*|replace github.com/beacon-stack/pulse => /pulse|' go.mod - -RUN go mod download -RUN CGO_ENABLED=0 go build -o /haul ./cmd/haul +# anacrolix/torrent compiles cleanly with CGO_ENABLED=0 for a fully static +# binary. No cgo dependencies in haul's tree. +RUN CGO_ENABLED=0 GOOS=linux go build \ + -ldflags "-X github.com/beacon-stack/haul/internal/version.Version=${VERSION} \ + -X github.com/beacon-stack/haul/internal/version.BuildTime=${BUILD_TIME} \ + -s -w" \ + -o /haul ./cmd/haul # ── Final stage ─────────────────────────────────────────────────────────────── FROM alpine:3.21 -RUN apk add --no-cache ca-certificates tzdata +# ca-certificates is needed for outbound HTTPS to trackers and indexers. +# tzdata provides timezone data for correct time display in the UI. +RUN apk add --no-cache ca-certificates tzdata && \ + adduser -D -u 1000 haul COPY --from=builder /haul /usr/local/bin/haul +# /config holds the local database and settings; /downloads holds torrent data. +VOLUME ["/config", "/downloads"] + +# Web UI + API / peer-wire / peer-wire UDP. EXPOSE 8484 6881 6881/udp -VOLUME ["/config", "/downloads"] +USER haul -ENTRYPOINT ["haul"] +ENTRYPOINT ["/usr/local/bin/haul"]