diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 228c1625..a3fe93cd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -83,6 +83,23 @@ jobs: go-version-file: go.mod check-latest: true + # Frontend dist must be built BEFORE go build — Go's //go:embed + # all:dist directive in web/web.go requires web/dist/ to exist + # at compile time. web/dist/ is .gitignored, so on a fresh CI + # checkout it doesn't exist until vite emits it. + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + cache-dependency-path: frontend/package-lock.json + + - name: Build frontend bundle + run: | + npm ci + npm run build + working-directory: frontend + - name: Build 3X-UI run: | export CGO_ENABLED=1 @@ -209,6 +226,23 @@ jobs: go-version-file: go.mod check-latest: true + # Frontend dist must be built BEFORE go build — see comment on the + # Linux job above. This step is identical except npm runs on the + # Windows runner here. + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + cache-dependency-path: frontend/package-lock.json + + - name: Build frontend bundle + shell: pwsh + run: | + npm ci + npm run build + working-directory: frontend + - name: Install MSYS2 uses: msys2/setup-msys2@v2 with: diff --git a/Dockerfile b/Dockerfile index dabaf7f1..31dcee4f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,20 @@ +# ======================================================== +# Stage: Frontend (Vite) +# ======================================================== +# web/dist/ is .gitignored and embedded into the Go binary via +# //go:embed all:dist in web/web.go, so the SPA bundle MUST be built +# before the Go compile step. We build it in its own stage so the +# Go builder image doesn't need Node installed. +FROM node:22-alpine AS frontend +WORKDIR /src/frontend +COPY frontend/package.json frontend/package-lock.json ./ +RUN npm ci +COPY frontend/ ./ +RUN npm run build +# Vite outDir is set to ../web/dist (see frontend/vite.config.js), so +# the bundle lands at /src/web/dist — that's what we copy into the +# next stage. + # ======================================================== # Stage: Builder # ======================================================== @@ -12,6 +29,7 @@ RUN apk --no-cache --update add \ unzip COPY . . +COPY --from=frontend /src/web/dist ./web/dist ENV CGO_ENABLED=1 ENV CGO_CFLAGS="-D_LARGEFILE64_SOURCE"