Skip to content

Commit 98e44ad

Browse files
committed
fix(sandbox): keep app runtimes off persistent storage
1 parent a28e305 commit 98e44ad

5 files changed

Lines changed: 38 additions & 8 deletions

File tree

.github/workflows/build-snapshot.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ jobs:
133133
libreoffice --version
134134
test -f /home/node/cheatcode-next-template/pnpm-lock.yaml
135135
test -f /home/node/cheatcode-expo-template/pnpm-lock.yaml
136+
test -f /home/node/cheatcode-next-template/next.config.ts
137+
test ! -e /home/node/cheatcode-next-template/node_modules
138+
test ! -e /home/node/cheatcode-expo-template/node_modules
139+
test -x /home/node/.cheatcode/app-runtimes/next/node_modules/.bin/next
140+
test -x /home/node/.cheatcode/app-runtimes/expo/node_modules/.bin/expo
141+
NODE_PATH=/home/node/.cheatcode/app-runtimes/next/node_modules node -e "require.resolve(\"next\");require.resolve(\"react\");require.resolve(\"react-dom\")"
142+
NODE_PATH=/home/node/.cheatcode/app-runtimes/expo/node_modules node -e "require.resolve(\"expo\");require.resolve(\"react-native-web\");require.resolve(\"@expo/metro-runtime\")"
136143
test -f /home/node/.cheatcode/default-skills/browser-use/SKILL.md
137144
test -f /home/node/.cheatcode/default-skills/pptx/SKILL.md
138145
test -f /home/node/.cheatcode/default-skills/pptx/scripts/thumbnail.py

infra/containers/sandbox/Dockerfile

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,12 @@ COPY --chown=node:node app-templates/ /home/node/.cheatcode-template-manifests/
208208
COPY --chown=node:node app-generators/ /home/node/.cheatcode-app-generators/
209209

210210
# The generators provide source files only. Reviewed manifests and lockfiles are
211-
# copied over them, then fetched into the node user's pnpm store so project creation
212-
# can install the exact dependency graph offline. The Expo template artifact itself
213-
# is pinned: create-expo-app's mutable "default" alias is intentionally not used.
211+
# copied over them, then the exact dependency graph is installed into immutable,
212+
# sandbox-local runtimes. Persistent /workspace is Daytona object-store FUSE: it is
213+
# the right home for user source, but copying dependency trees there is both slow and
214+
# non-atomic. The source-only templates stay separate so project creation copies only
215+
# editable files. The Expo template artifact itself is pinned: create-expo-app's
216+
# mutable "default" alias is intentionally not used.
214217
ARG EXPO_TEMPLATE_VERSION=57.0.6
215218
ARG EXPO_TEMPLATE_SHA256=4c2a276f05db27f4a4dfaa326123e72be33d7ad6878fdd46b8df7eb3376def3b
216219

@@ -222,13 +225,16 @@ RUN set -eux; \
222225
cp /home/node/.cheatcode-template-manifests/next/package.json /home/node/cheatcode-next-template/package.json; \
223226
cp /home/node/.cheatcode-template-manifests/next/pnpm-lock.yaml /home/node/cheatcode-next-template/pnpm-lock.yaml; \
224227
cp /home/node/.cheatcode-template-manifests/next/pnpm-workspace.yaml /home/node/cheatcode-next-template/pnpm-workspace.yaml; \
228+
cp /home/node/.cheatcode-template-manifests/next/next.config.ts /home/node/cheatcode-next-template/next.config.ts; \
225229
rm -rf \
226230
/home/node/cheatcode-next-template/.next \
227231
/home/node/cheatcode-next-template/node_modules \
228232
/home/node/cheatcode-next-template/AGENTS.md \
229233
/home/node/cheatcode-next-template/CLAUDE.md; \
230-
cd /home/node/cheatcode-next-template; \
231-
pnpm fetch --frozen-lockfile; \
234+
mkdir -p /home/node/.cheatcode/app-runtimes; \
235+
cp -a /home/node/cheatcode-next-template /home/node/.cheatcode/app-runtimes/next; \
236+
cd /home/node/.cheatcode/app-runtimes/next; \
237+
pnpm install --frozen-lockfile; \
232238
cd /workspace; \
233239
curl -fsSL \
234240
"https://registry.npmjs.org/expo-template-default/-/expo-template-default-${EXPO_TEMPLATE_VERSION}.tgz" \
@@ -246,7 +252,9 @@ RUN set -eux; \
246252
cd /home/node/cheatcode-expo-template; \
247253
node -e 'const fs=require("node:fs");const j=JSON.parse(fs.readFileSync("app.json","utf8"));j.expo??={};j.expo.name="cheatcode-expo-template";j.expo.slug="cheatcode-expo-template";j.expo.web={...(j.expo.web??{}),bundler:"metro",output:"single"};fs.writeFileSync("app.json",JSON.stringify(j,null,2)+"\n")'; \
248254
rm -rf .git .expo node_modules package-lock.json AGENTS.md CLAUDE.md .claude; \
249-
pnpm fetch --frozen-lockfile; \
255+
cp -a /home/node/cheatcode-expo-template /home/node/.cheatcode/app-runtimes/expo; \
256+
cd /home/node/.cheatcode/app-runtimes/expo; \
257+
pnpm install --frozen-lockfile; \
250258
cd /workspace; \
251259
rm -rf \
252260
/tmp/expo-template-default.tgz \

infra/containers/sandbox/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,12 @@ the image build. Document-runtime versions are owned by its reviewed manifest an
8585
they are intentionally absent from the Worker workspace catalog. Its `uuid` override
8686
keeps ExcelJS on the patched UUID implementation while preserving the public CommonJS
8787
`v4` API that ExcelJS consumes. The Next and Expo scaffolds likewise use reviewed manifests and
88-
pnpm locks under `app-templates/`. Their locked packages are prefetched into the image
89-
so normal project creation can install offline. Expo uses an exact
88+
pnpm locks under `app-templates/`. Their locked packages are installed into immutable
89+
sandbox-local runtimes in the image. Project creation copies only source and config to
90+
the persistent Daytona volume; dependency trees and generated compiler caches stay on
91+
the sandbox's local filesystem, avoiding slow, partial writes to object-store FUSE.
92+
Additional dependencies use a project-scoped local modules directory and can be
93+
restored from the reviewed package store after a sandbox replacement. Expo uses an exact
9094
`expo-template-default` tarball with a reviewed SHA-256 rather than the mutable
9195
`default` alias. These locks prevent a snapshot rebuild from resolving a different
9296
dependency tree while the application source stays unchanged.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import type { NextConfig } from "next";
2+
3+
const nextConfig: NextConfig = {
4+
// User source is persistent object-store FUSE; generated compiler output belongs
5+
// on the sandbox's fast local disk. The managed preview owns this relative path.
6+
distDir: process.env["CHEATCODE_NEXT_DIST_DIR"] ?? ".next",
7+
};
8+
9+
export default nextConfig;

knip.jsonc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// `cloudflare:workers` and `cloudflare:workflows` are platform modules, not
55
// installable package dependencies. Knip cannot infer either edge.
66
"ignoreDependencies": ["@cheatcode/tsconfig", "cloudflare"],
7+
// Copied into the generated Next scaffold by the sandbox Dockerfile.
8+
"ignore": ["infra/containers/sandbox/app-templates/next/next.config.ts"],
79
"includeEntryExports": true,
810
"workspaces": {
911
".": {

0 commit comments

Comments
 (0)