diff --git a/Dockerfile b/Dockerfile index 4fd5b15..273f53f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,13 @@ +# Web build stage — compile SPA from source +# node:22-alpine, 2026-03-09 +FROM node@sha256:8094c002d08262dba12645a3b4a15cd6cd627d30bc782f53229a2ec13ee22a00 AS web-builder +WORKDIR /web +COPY web/package.json web/package-lock.json ./ +RUN npm ci +COPY web/src/ src/ +COPY web/build.sh build.sh +RUN sh build.sh + # Lint stage — fast feedback on formatting and lint issues # golangci/golangci-lint:v2.1.6, 2026-03-02 FROM golangci/golangci-lint@sha256:568ee1c1c53493575fa9494e280e579ac9ca865787bafe4df3023ae59ecf299b AS lint @@ -5,6 +15,9 @@ WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . +# Create placeholder files so //go:embed dist/* in web/embed.go resolves +# without depending on the web-builder stage (lint should fail fast) +RUN mkdir -p web/dist && touch web/dist/index.html web/dist/style.css web/dist/app.js RUN make fmt-check RUN make lint @@ -21,6 +34,7 @@ COPY go.mod go.sum ./ RUN go mod download COPY . . +COPY --from=web-builder /web/dist/ web/dist/ RUN make test diff --git a/README.md b/README.md index 4fe5c58..268603c 100644 --- a/README.md +++ b/README.md @@ -1858,26 +1858,16 @@ docker run -p 8080:8080 \ neoirc ``` -The Dockerfile is a multi-stage build: -1. **Build stage**: Compiles `neoircd` and `neoirc-cli` (CLI built to verify - compilation, not included in final image) -2. **Final stage**: Alpine Linux + `neoircd` binary only +The Dockerfile is a 4-stage build: +1. **Web builder stage** (`web-builder`): Compiles the Preact JSX SPA into + static assets (`web/dist/`) using esbuild +2. **Lint stage** (`lint`): Runs formatting checks and linting via golangci-lint +3. **Build stage** (`builder`): Compiles `neoircd` and `neoirc-cli`, runs tests + (CLI built to verify compilation, not included in final image) +4. **Runtime stage**: Alpine Linux + `neoircd` binary only -```dockerfile -FROM golang:1.24-alpine AS builder -WORKDIR /src -RUN apk add --no-cache make -COPY go.mod go.sum ./ -RUN go mod download -COPY . . -RUN go build -o /neoircd ./cmd/neoircd/ -RUN go build -o /neoirc-cli ./cmd/neoirc-cli/ - -FROM alpine:latest -COPY --from=builder /neoircd /usr/local/bin/neoircd -EXPOSE 8080 -CMD ["neoircd"] -``` +`web/dist/` is not committed to git — it is built from `web/src/` by the +web-builder stage during `docker build`. ### Binary @@ -2339,7 +2329,13 @@ neoirc/ │ └── http.go # HTTP timeouts ├── web/ │ ├── embed.go # go:embed directive for SPA -│ └── dist/ # Built SPA (vanilla JS, no build step) +│ ├── build.sh # esbuild script: JSX → dist/ +│ ├── package.json # Node dependencies (esbuild, preact) +│ ├── src/ # SPA source (Preact JSX) +│ │ ├── app.jsx +│ │ ├── index.html +│ │ └── style.css +│ └── dist/ # Built SPA (generated by web-builder Docker stage) │ ├── index.html │ ├── style.css │ └── app.js