Repository navigation
Expand file tree
/
Copy pathDockerfile
More file actions
152 lines (141 loc) · 7.8 KB
/
Copy pathDockerfile
File metadata and controls
152 lines (141 loc) · 7.8 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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# StackAlchemist Multi-Component Dockerfile
# This file uses multi-stage builds and targets to build the Web, Engine, and Worker.
# ==========================================
# STAGE 1: NEXT.JS FRONTEND (WEB)
# ==========================================
FROM node:26-alpine AS web-builder
RUN apk add --no-cache git
WORKDIR /app
# Accept public env vars at build time so Next.js bakes them into the bundle.
# NEXT_PUBLIC_* vars are read at module scope (e.g. layout.tsx `isTestSite`)
# which means they must be present during `next build`, not just at runtime.
# Missing IS_TEST_SITE here is what historically broke the test-mirror noindex.
ARG NEXT_PUBLIC_APP_URL=https://stackalchemist.app
ARG NEXT_PUBLIC_IS_TEST_SITE=false
ENV NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL
ENV NEXT_PUBLIC_IS_TEST_SITE=$NEXT_PUBLIC_IS_TEST_SITE
ARG NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
ARG NEXT_PUBLIC_PLAUSIBLE_DOMAIN
ENV NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=$NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
ENV NEXT_PUBLIC_PLAUSIBLE_DOMAIN=$NEXT_PUBLIC_PLAUSIBLE_DOMAIN
# Copy manifests first so the install layer is cached independently of source.
COPY src/StackAlchemist.Web/package.json src/StackAlchemist.Web/package-lock.json ./
# Install all dependencies. --ignore-scripts skips postinstall (setup-env-safe.mjs)
# which requires the full scripts/ dir that isn't copied until the next step.
RUN npm install --include=dev --ignore-scripts
COPY src/StackAlchemist.Web/ .
# Docs, blog, compare, and solutions pages resolve their markdown via
# process.cwd()/../../<folder> at build time. CWD is /app, so these trees must
# live at /docs and /content for generateStaticParams + generateMetadata to read.
COPY docs/ /docs/
COPY content/ /content/
# Build the Next.js app and clean build cache.
# We invoke next build directly because the npm script wrapper
# (scripts/build-wrapper.mjs) is only needed on Windows+pnpm.
# --webpack opts out of Turbopack (Next 16 default); the custom webpack hook
# in next.config.ts (resolve.symlinks=false) only runs under webpack and is
# load-bearing for static prerendering on Windows local dev — keep webpack
# everywhere so Linux CI and Windows local stay in sync.
RUN npx next build --webpack \
&& rm -rf /root/.npm /tmp/*
FROM node:26-alpine AS web
# Install wget for production health checks
RUN apk add --no-cache wget
WORKDIR /app
ENV NODE_ENV=production
ENV HOSTNAME=0.0.0.0
ENV PORT=3000
COPY --from=web-builder /app/.next/standalone ./
COPY --from=web-builder /app/.next/static ./.next/static
COPY --from=web-builder /app/public ./public
EXPOSE 3000
CMD ["node", "server.js"]
# ==========================================
# STAGE 2: .NET ENGINE (API)
# ==========================================
FROM mcr.microsoft.com/dotnet/sdk:10.0-resolute AS engine-builder
WORKDIR /src
COPY src/StackAlchemist.Engine/*.csproj ./StackAlchemist.Engine/
RUN dotnet restore ./StackAlchemist.Engine/StackAlchemist.Engine.csproj
COPY src/StackAlchemist.Engine/ ./StackAlchemist.Engine/
RUN dotnet publish ./StackAlchemist.Engine/StackAlchemist.Engine.csproj -c Release -o /app/publish
# NOTE: this used to be the slim `aspnet` runtime image. CompileWorkerService runs
# in-process here (promoted from Worker in Phase 4) and shells out to `dotnet build`,
# `npm`/`npx`, and — for PythonReact generations — `python`/pip/flake8/pytest. None of
# that exists on the aspnet runtime image, so every paid Tier-2/3 generation failed at
# the build step in production. Base on the SDK image (mirrors the worker stage below)
# and install the same Node + Python toolchain the build strategies invoke. Tradeoff:
# meaningfully larger image (SDK vs aspnet runtime) in exchange for generations that
# actually compile.
FROM mcr.microsoft.com/dotnet/sdk:10.0-resolute AS engine
RUN apt-get update && apt-get install -y --no-install-recommends \
wget \
python3 \
python3-pip \
python3-venv \
iptables \
procps \
&& ln -sf /usr/bin/python3 /usr/bin/python \
&& rm -rf /var/lib/apt/lists/*
# Node.js — PythonReactBuildStrategy runs `npm install`, `npm run lint`, and `npx tsc`
# against generated React frontends.
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs
# PythonReactBuildStrategy builds in a per-generation venv (#451): Ubuntu's python3 is
# EXTERNALLY-MANAGED (PEP 668), so `pip install -r requirements.txt` into it is refused, and
# `python -m venv` needs python3-venv (ensurepip) on Ubuntu. The venv is created with
# --system-site-packages, so the flake8/pytest below stay reachable as a fallback for
# generated projects whose requirements.txt omits them.
RUN python3 -m pip install --no-cache-dir --break-system-packages flake8 pytest
# Build sandbox (#454). Compile Guarantee builds run LLM-generated code, so they run as
# sa-builder (uid 10001, no login, no home) through sa-sandbox-exec, in a per-job copy under
# /var/lib/stackalchemist/build (0711: enterable, not listable). The Engine's own temp tree,
# where it keeps every job's source and archive, is /var/lib/stackalchemist/tmp (0700 root).
# entrypoint.sh installs the build user's egress firewall, then starts the Engine.
RUN groupadd --system --gid 10001 sa-builder \
&& useradd --system --uid 10001 --gid 10001 --no-create-home --home-dir /nonexistent \
--shell /usr/sbin/nologin sa-builder \
&& install -d -m 0711 /var/lib/stackalchemist /var/lib/stackalchemist/build \
&& install -d -m 0700 /var/lib/stackalchemist/tmp
COPY --chmod=0755 docker/engine/ /usr/local/lib/stackalchemist/
ENV SA_BUILD_UID=10001 \
SA_BUILD_GID=10001 \
SA_BUILD_ROOT=/var/lib/stackalchemist/build \
TMPDIR=/var/lib/stackalchemist/tmp
ENV ASPNETCORE_URLS=http://+:80
WORKDIR /app
COPY --from=engine-builder /app/publish .
# Handlebars template sets are loaded from disk at runtime and are NOT part of the
# published DLL output. Copy them next to the entrypoint — TemplatesRootResolver probes
# <BaseDirectory>/StackAlchemist.Templates first, which is /app here. Without this the
# engine reports "Template set ... not found. Available: none" and all codegen fails.
COPY src/StackAlchemist.Templates/ ./StackAlchemist.Templates/
EXPOSE 80
EXPOSE 443
ENTRYPOINT ["/usr/local/lib/stackalchemist/entrypoint.sh"]
# ==========================================
# STAGE 3: .NET WORKER (COMPILE GUARANTEE)
# ==========================================
FROM mcr.microsoft.com/dotnet/sdk:10.0-resolute AS worker-builder
WORKDIR /src
# Worker references Engine — copy both .csproj files before restore so the
# dependency graph can be resolved, then copy full source for both projects.
COPY src/StackAlchemist.Engine/*.csproj ./StackAlchemist.Engine/
COPY src/StackAlchemist.Worker/*.csproj ./StackAlchemist.Worker/
RUN dotnet restore ./StackAlchemist.Worker/StackAlchemist.Worker.csproj
COPY src/StackAlchemist.Engine/ ./StackAlchemist.Engine/
COPY src/StackAlchemist.Worker/ ./StackAlchemist.Worker/
RUN dotnet publish ./StackAlchemist.Worker/StackAlchemist.Worker.csproj -c Release -o /app/publish
FROM mcr.microsoft.com/dotnet/sdk:10.0-resolute AS worker
# Note: Worker needs SDK to run 'dotnet build' on generated repos
WORKDIR /app
COPY --from=worker-builder /app/publish .
# Install Node.js in the worker container so it can run 'npm build' on generated repos
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt-get install -y nodejs
# Not currently deployed (see docker-compose.*.yml — compile pipeline runs in-process
# in the engine stage above), but kept buildable as a future scale-out host, so it
# needs the same Python toolchain PythonReactBuildStrategy invokes.
RUN apt-get update && apt-get install -y --no-install-recommends python3 python3-pip python3-venv \
&& ln -sf /usr/bin/python3 /usr/bin/python \
&& rm -rf /var/lib/apt/lists/*
RUN python3 -m pip install --no-cache-dir --break-system-packages flake8 pytest
ENTRYPOINT ["dotnet", "StackAlchemist.Worker.dll"]