-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathDockerfile
More file actions
98 lines (85 loc) · 4.69 KB
/
Copy pathDockerfile
File metadata and controls
98 lines (85 loc) · 4.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# Stage 1: Compile the Claude Agent SDK sidecar to a standalone binary.
# Bun cross-compiles to $TARGETARCH from $BUILDPLATFORM, so this runs
# natively (no QEMU). We pick the musl variant because the runtime stage
# below is Alpine (musl libc) — the glibc-targeted Bun binary would not
# load there without gcompat shims.
FROM --platform=$BUILDPLATFORM oven/bun:1 AS sidecar-builder
ARG TARGETARCH
WORKDIR /sidecar
COPY agent/sidecar/package.json agent/sidecar/bun.lock ./
RUN bun install --frozen-lockfile
COPY agent/sidecar/ ./
RUN case "$TARGETARCH" in \
amd64) TARGET=bun-linux-x64-musl ;; \
arm64) TARGET=bun-linux-arm64-musl ;; \
*) echo "unsupported TARGETARCH=$TARGETARCH" >&2; exit 1 ;; \
esac \
&& bun build --compile --minify --target=$TARGET \
--outfile /breadbox-agent index.ts
# Stage 2: Build Go binary (runs natively on the build host, cross-compiles Go)
FROM --platform=$BUILDPLATFORM golang:1.25-alpine AS builder
ARG VERSION=dev
ARG TARGETARCH
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# Generate sqlc code (generated files are gitignored)
# Use pre-built binary — compiling from source is very slow under QEMU emulation.
RUN ARCH=$(uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/') \
&& wget -qO- "https://github.com/sqlc-dev/sqlc/releases/download/v1.30.0/sqlc_1.30.0_linux_${ARCH}.tar.gz" | tar xz -C /usr/local/bin sqlc \
&& sqlc generate
# Generate templ components (*.templ → *_templ.go, also gitignored).
# templ's own binary is small and the go install is fast on the build host.
RUN go install github.com/a-h/templ/cmd/templ@latest \
&& /go/bin/templ generate
# Build CSS: download tailwindcss-extra (musl variant for Alpine) and compile input.css
RUN apk add --no-cache libstdc++ libgcc \
&& ARCH=$(uname -m | sed 's/aarch64/arm64/' | sed 's/x86_64/x64/') \
&& wget -qO tailwindcss-extra "https://github.com/dobicinaitis/tailwind-cli-extra/releases/latest/download/tailwindcss-extra-linux-${ARCH}-musl" \
&& chmod +x tailwindcss-extra \
&& ./tailwindcss-extra -i input.css -o static/css/styles.css --minify
RUN CGO_ENABLED=0 GOOS=linux GOARCH=$TARGETARCH go build -trimpath \
-ldflags="-s -w -X main.version=${VERSION}" \
-o /breadbox ./cmd/breadbox
# Stage 3: Runtime
FROM alpine:3.21
# CA certificates: required for TLS connections to Plaid API and PostgreSQL
# tzdata: required for cron schedule timezone handling
# postgresql16-client: required by the /backups page for pg_dump and psql.
# Must match the server major version (postgres:16-alpine in compose) —
# pg_dump refuses to dump a newer server.
# libstdc++, libgcc: required by the Bun-compiled breadbox-agent sidecar.
# Even with --target=bun-linux-*-musl the resulting binary dynamically
# links libstdc++.so.6 and libgcc_s.so.1, which aren't in alpine base.
# Without these, the sidecar exits 127 and every agent run fails.
# nodejs: required to spawn the Claude Agent SDK's bundled cli.js subprocess.
# The sidecar passes executable:"node" to query() so the SDK's
# isRunningWithBun() heuristic (which would otherwise pick "bun" because
# the sidecar binary IS bun) doesn't try to spawn a `bun` we don't ship.
# Without nodejs the spawn fires ENOENT on an unhandled error handler
# inside the SDK; the async iterator ends silently with zero messages
# and the run is misclassified as a 0-cost, 0-turn "success".
# bash + ripgrep: the agent's own analysis tools (Bash, Grep) need a POSIX
# shell and ripgrep. Alpine ships neither (only busybox sh, no rg), so an
# agent that tried to grep a large saved tool-result hit
# "No suitable shell found" / "spawn .../vendor/ripgrep/.../rg ENOENT" and
# was left unable to do file-level analysis. We install both and point the
# SDK at the system rg via USE_BUILTIN_RIPGREP=0 (its bundled copy is not
# embedded in the Bun --compile output). See agent/sidecar/index.ts.
RUN apk --no-cache add ca-certificates tzdata postgresql16-client libstdc++ libgcc nodejs bash ripgrep
# SHELL: the Claude Agent SDK's Bash tool requires SHELL to point at a POSIX
# shell; the container env doesn't set it otherwise. USE_BUILTIN_RIPGREP=0:
# use the system ripgrep we installed instead of the SDK's vendored binary,
# which isn't present in the sidecar bundle. Both are also defaulted
# defensively in the sidecar itself for non-Docker installs.
ENV SHELL=/bin/bash \
USE_BUILTIN_RIPGREP=0
WORKDIR /app
COPY --from=builder /breadbox /app/breadbox
# Sidecar lands on PATH so internal/agent.LocateBinary picks it up via the
# `breadbox-agent` lookup without any extra config.
COPY --from=sidecar-builder /breadbox-agent /usr/local/bin/breadbox-agent
EXPOSE 8080
ENTRYPOINT []
CMD ["/app/breadbox", "serve"]