Skip to content

Commit f6d7ec3

Browse files
committed
feat(shell, strategy-curator, chat): Dockerfile production builds, fix empty-string env fallback (Build-Order Step 9)
Railway's Railpack builder auto-detected this repo as a turborepo (turbo.json exists at root) and unconditionally ran the root package.json's "build" script (turbo run build) regardless of each service's configured buildCommand override — and turbo was never actually an installed binary here (dev.sh's own comment already avoids `pnpm dev` for this exact reason), so every Railpack-built frontend failed with "turbo: not found". Added a Dockerfile per frontend (shell, strategy-curator, chat), each a direct `pnpm --filter <pkg> build` against the full monorepo context, matching the Dockerfile pattern already proven reliable for the four backend services. Verified locally with `docker build` (with and without build-args) before pushing. That local verification caught a second, real bug: `?? default` doesn't protect against a Docker ARG that's declared but never passed — those resolve to an EMPTY STRING once assigned to ENV, not undefined, so `??` alone silently shipped `strategyCurator@` (no host) instead of falling back, and rspack's Module Federation remote-info resolution crashed on the empty entry ("TypeError: object null is not iterable"). Switched the four PUBLIC_-var fallbacks (shell/rsbuild.config.ts's two remote URLs; shell, chat, and strategy-curator's WS_URL) from `??` to `||`. Files changed: - shell/Dockerfile (new) - apps/strategy-curator/Dockerfile (new) - apps/chat/Dockerfile (new) - shell/rsbuild.config.ts - shell/src/App.svelte - apps/chat/src/App.svelte - apps/strategy-curator/src/curation.svelte.ts
1 parent 6ed2b3d commit f6d7ec3

7 files changed

Lines changed: 107 additions & 5 deletions

File tree

apps/chat/Dockerfile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Production build for the chat federated remote (didi). Same rationale
2+
# as shell/Dockerfile — direct pnpm --filter build instead of relying on
3+
# Railpack's turborepo auto-detection, which overrides any custom
4+
# buildCommand unconditionally and runs the (nonfunctional, turbo was
5+
# never actually installed) root "build" script instead.
6+
#
7+
# Build context is the repo root (Railway: no rootDirectory set for this
8+
# service, dockerfilePath: apps/chat/Dockerfile).
9+
10+
FROM node:22-alpine AS build
11+
WORKDIR /monorepo
12+
RUN corepack enable
13+
14+
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
15+
COPY packages ./packages
16+
COPY apps ./apps
17+
COPY shell ./shell
18+
RUN pnpm install --frozen-lockfile
19+
20+
ARG PUBLIC_WS_URL
21+
ENV PUBLIC_WS_URL=$PUBLIC_WS_URL
22+
RUN pnpm --filter @augment-it/chat build
23+
24+
FROM node:22-alpine
25+
WORKDIR /app
26+
RUN npm install -g serve
27+
COPY --from=build /monorepo/apps/chat/dist ./dist
28+
EXPOSE 3006
29+
CMD ["serve", "-s", "dist", "-l", "3006"]

apps/chat/src/App.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
// WS_URL the shell and strategy-curator each read (rsbuild inlines
1717
// PUBLIC_-prefixed vars into import.meta.env at build time).
1818
const WS_URL =
19-
((import.meta as { env?: Record<string, string> }).env?.PUBLIC_WS_URL as string | undefined) ??
19+
((import.meta as { env?: Record<string, string> }).env?.PUBLIC_WS_URL as string | undefined) ||
2020
'ws://localhost:3001/ws';
2121
2222
let connectionStatus = $state<'connecting' | 'open' | 'closed' | 'error'>('connecting');

apps/strategy-curator/Dockerfile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Production build for the strategy-curator federated remote. Same
2+
# rationale as shell/Dockerfile — direct pnpm --filter build instead of
3+
# relying on Railpack's turborepo auto-detection, which overrides any
4+
# custom buildCommand unconditionally and runs the (nonfunctional, turbo
5+
# was never actually installed) root "build" script instead.
6+
#
7+
# Build context is the repo root (Railway: no rootDirectory set for this
8+
# service, dockerfilePath: apps/strategy-curator/Dockerfile).
9+
10+
FROM node:22-alpine AS build
11+
WORKDIR /monorepo
12+
RUN corepack enable
13+
14+
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
15+
COPY packages ./packages
16+
COPY apps ./apps
17+
COPY shell ./shell
18+
RUN pnpm install --frozen-lockfile
19+
20+
ARG PUBLIC_WS_URL
21+
ENV PUBLIC_WS_URL=$PUBLIC_WS_URL
22+
RUN pnpm --filter @augment-it/strategy-curator build
23+
24+
FROM node:22-alpine
25+
WORKDIR /app
26+
RUN npm install -g serve
27+
COPY --from=build /monorepo/apps/strategy-curator/dist ./dist
28+
EXPOSE 3017
29+
CMD ["serve", "-s", "dist", "-l", "3017"]

apps/strategy-curator/src/curation.svelte.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const TOKEN_KEY = 'augment-it:session-token';
1919
// import.meta.env at build time — applies to plain .ts modules too, not
2020
// just .svelte files).
2121
const WS_URL =
22-
((import.meta as { env?: Record<string, string> }).env?.PUBLIC_WS_URL as string | undefined) ??
22+
((import.meta as { env?: Record<string, string> }).env?.PUBLIC_WS_URL as string | undefined) ||
2323
'ws://localhost:3001/ws';
2424
const ACTIVE_STRATEGY_KEY = 'augment-it:active-strategy';
2525
// The operator-chosen domain type this surface is currently browsing/

shell/Dockerfile

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Production build for the shell — the only one of the three frontend
2+
# Dockerfiles that needs multi-stage (build with the full pnpm workspace
3+
# context, serve only the static output). Root package.json's own "build"
4+
# script (turbo run build) is dead weight — turbo was never actually an
5+
# installed binary in this repo — so this Dockerfile builds via a direct
6+
# pnpm --filter invocation instead of relying on Railpack's turborepo
7+
# auto-detection, which overrides any custom buildCommand unconditionally.
8+
#
9+
# Build context is the repo root (Railway: no rootDirectory set for this
10+
# service, dockerfilePath: shell/Dockerfile).
11+
12+
FROM node:22-alpine AS build
13+
WORKDIR /monorepo
14+
RUN corepack enable
15+
16+
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
17+
COPY packages ./packages
18+
COPY apps ./apps
19+
COPY shell ./shell
20+
RUN pnpm install --frozen-lockfile
21+
22+
ARG PUBLIC_WS_URL
23+
ARG PUBLIC_ID_BASE
24+
ARG PUBLIC_STRATEGY_CURATOR_REMOTE
25+
ARG PUBLIC_CHAT_REMOTE
26+
ENV PUBLIC_WS_URL=$PUBLIC_WS_URL
27+
ENV PUBLIC_ID_BASE=$PUBLIC_ID_BASE
28+
ENV PUBLIC_STRATEGY_CURATOR_REMOTE=$PUBLIC_STRATEGY_CURATOR_REMOTE
29+
ENV PUBLIC_CHAT_REMOTE=$PUBLIC_CHAT_REMOTE
30+
RUN pnpm --filter @augment-it/shell build
31+
32+
FROM node:22-alpine
33+
WORKDIR /app
34+
RUN npm install -g serve
35+
COPY --from=build /monorepo/shell/dist ./dist
36+
EXPOSE 3100
37+
CMD ["serve", "-s", "dist", "-l", "3100"]

shell/rsbuild.config.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,15 @@ import { pluginModuleFederation } from '@module-federation/rsbuild-plugin';
1616
// leaving them pointed at localhost is the same "no isolation, they error
1717
// if poked" rule the build order already established, just now true for
1818
// federation URLs too — nobody on this instance ever navigates to them.
19-
const STRATEGY_CURATOR_REMOTE = process.env.PUBLIC_STRATEGY_CURATOR_REMOTE ?? 'http://localhost:3017/remoteEntry.js';
20-
const CHAT_REMOTE = process.env.PUBLIC_CHAT_REMOTE ?? 'http://localhost:3006/remoteEntry.js';
19+
// `|| default` (not `?? default`) deliberately — an unset Docker ARG
20+
// resolves to an EMPTY STRING once assigned to ENV, not undefined, so `??`
21+
// alone would silently ship `strategyCurator@` / `chat@` (no host) instead
22+
// of falling back. Caught locally: a docker build with these vars unset
23+
// produced "TypeError: object null is not iterable" deep in rspack's
24+
// Module Federation remote-info resolution — an empty remote URL, not a
25+
// missing one.
26+
const STRATEGY_CURATOR_REMOTE = process.env.PUBLIC_STRATEGY_CURATOR_REMOTE || 'http://localhost:3017/remoteEntry.js';
27+
const CHAT_REMOTE = process.env.PUBLIC_CHAT_REMOTE || 'http://localhost:3006/remoteEntry.js';
2128

2229
export default defineConfig({
2330
plugins: [

shell/src/App.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
// second env var — Step 7's /config check needs the same origin, not the
3737
// WS scheme.
3838
const WS_URL =
39-
((import.meta as { env?: Record<string, string> }).env?.PUBLIC_WS_URL as string | undefined) ??
39+
((import.meta as { env?: Record<string, string> }).env?.PUBLIC_WS_URL as string | undefined) ||
4040
'ws://localhost:3001/ws';
4141
const WS_HTTP_BASE = WS_URL.replace(/^ws/, 'http').replace(/\/ws$/, '');
4242

0 commit comments

Comments
 (0)