-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathDockerfile.worker
More file actions
145 lines (124 loc) · 5.47 KB
/
Dockerfile.worker
File metadata and controls
145 lines (124 loc) · 5.47 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
FROM node:22-slim AS builder
WORKDIR /app
# Install dependencies (including dev for build)
# Using --ignore-scripts to skip lefthook install (needs git repo)
COPY package*.json .npmrc ./
RUN npm ci --ignore-scripts
# Build
COPY tsconfig.json ./
COPY src ./src
RUN npm run build
# Production image
FROM node:22-bookworm AS production
WORKDIR /app
# `cascade.managed=true` is the contract the router's dangling-image cleanup
# loop filters on (src/router/dangling-image-cleanup.ts). Without this LABEL,
# the loop matches zero images and reclaims nothing — see PR #1256.
LABEL cascade.managed=true
# Install pnpm globally (some repos use pnpm)
RUN npm install -g pnpm --force
# Install system packages needed by agent runtime
#
# `python3` plus `python-is-python3` provide a Debian-owned Python shim so
# both `python` and `python3` work predictably for `python -c 'import json'`
# and other lightweight scripting agents reach for. The package ownership is
# load-bearing — manual `ln -s python3 python` symlinks drift on base-image
# changes; the apt package survives those cleanly. See MNG-1055 (and the
# friction cluster MNG-887/897/926/934/947/957/973/1010/1024/1033/1039/1044).
RUN apt-get update && apt-get install -y \
ca-certificates \
curl \
ed \
fd-find \
git \
gnupg \
jq \
lsof \
postgresql-client \
procps \
psutils \
python3 \
python-is-python3 \
redis-tools \
ripgrep \
ruby \
sudo \
tmux \
unzip \
&& rm -rf /var/lib/apt/lists/* \
&& ln -s $(which fdfind) /usr/local/bin/fd \
&& python3 --version \
&& python --version \
&& python -c 'import json; print(json.dumps({"ok": True}))'
# Configure tmux to keep panes alive after command exits
# This allows capturing output and exit code from fast-exiting commands
RUN echo "set-option -g remain-on-exit on" > /root/.tmux.conf
# Install ast-grep
RUN ARCH=$(dpkg --print-architecture) && \
if [ "$ARCH" = "amd64" ]; then AST_ARCH="x86_64"; else AST_ARCH="aarch64"; fi && \
curl -L "https://github.com/ast-grep/ast-grep/releases/download/0.40.3/app-${AST_ARCH}-unknown-linux-gnu.zip" -o /tmp/ast-grep.zip && \
unzip /tmp/ast-grep.zip -d /usr/local/bin && \
rm /tmp/ast-grep.zip && \
chmod +x /usr/local/bin/sg
# Install agent CLIs used by headless engines in worker jobs.
# All three are explicitly pinned so worker image rebuilds are reproducible —
# upstream CLI changes can introduce subtle behavioural drift in headless mode.
RUN npm install -g \
@anthropic-ai/claude-code@2.1.119 \
@openai/codex@0.125.0 \
opencode-ai@1.14.25
# Playwright browser cache.
#
# Workers regularly review PRs that include UI/Playwright tests, and review
# agents call `playwright test` / `playwright launch chromium` in shell
# sessions. Without a baseline browser cache they hit
# `browserType.launch: Executable doesn't exist` and waste budget trying to
# install Chromium themselves (often into `~/.cache/ms-playwright` for the
# wrong user, or while offline). See friction cluster MNG-998 / MNG-1048.
#
# The cache lives at a stable non-home path so:
# - Chromium binaries are owned by the runtime `node` user and readable by all
# users.
# - Project setup scripts that need a different Playwright revision can write
# the missing browser revision into the same cache instead of failing on a
# root-owned directory.
# - The `USER node` switch below does not invalidate or duplicate the cache.
# - `PLAYWRIGHT_BROWSERS_PATH` can be allowlisted by the native-tool env
# filter (`src/backends/shared/envFilter.ts`) and forwarded to agent
# subprocesses without leaking any other Playwright config.
ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
RUN npm install -g @playwright/test@1.49.1 \
&& PLAYWRIGHT_BROWSERS_PATH=/ms-playwright \
npx --yes playwright@1.49.1 install --with-deps chromium \
&& chown -R node:node /ms-playwright \
&& chmod -R u+rwX,go+rX /ms-playwright
# Switch to non-root user for running workers.
# Claude Code CLI refuses --dangerously-skip-permissions when running as root.
# The node user (uid 1000) is pre-created by the Node.js base image and matches
# the default host user for volume mount compatibility.
# Passwordless sudo is needed for setup scripts that require root (e.g. apt-get).
RUN echo "node ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/node
# Create workspace directory owned by node user
RUN mkdir -p /workspace && chown node:node /workspace
# Configure tmux for node user
RUN cp /root/.tmux.conf /home/node/.tmux.conf && chown node:node /home/node/.tmux.conf
# Give node user ownership of the (empty) app directory before switching
RUN chown node:node /app
# Switch to node user before installing app files (avoids slow chown -R)
USER node
# Install production dependencies as node user
COPY --chown=node:node package*.json .npmrc ./
RUN npm ci --omit=dev --ignore-scripts
# Copy built code from builder
COPY --chown=node:node --from=builder /app/dist ./dist
# Copy cascade-tools CLI entry point and make it executable
COPY --chown=node:node bin ./bin
RUN chmod +x bin/cascade-tools.js
# Symlink needs root — use sudo
RUN sudo ln -sf /app/bin/cascade-tools.js /usr/local/bin/cascade-tools
# Copy Eta template files (not handled by TypeScript compiler)
COPY --chown=node:node src/agents/prompts/templates ./dist/agents/prompts/templates
# Copy config
COPY --chown=node:node config ./config
# Worker entry point - processes a single job and exits
CMD ["node", "--import", "./dist/instrument.js", "dist/worker-entry.js"]