Skip to content

Commit ffcd563

Browse files
grichaclaude
andauthored
Fix Docker-in-Docker and add Playwright to workspace image (#24)
Two changes: 1. Add /var/lib/docker volume mount to workspace containers - Without this, nested Docker tries to use overlay-on-overlay which fails - This matches the original subroutinecom/workspace behavior - Creates workspace-{name}-docker volume alongside workspace-{name} volume - Volume is cleaned up on workspace deletion 2. Pre-install Playwright Chromium in workspace image - Enables running Playwright tests inside workspaces - Only dependency that was missing for full Perry development With these changes, the full Perry validation loop (bun run validate) works inside a workspace, enabling Perry-in-Perry development. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 9f4b56a commit ffcd563

File tree

2 files changed

+25
-2
lines changed

2 files changed

+25
-2
lines changed

perry/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ RUN curl -fsSL https://claude.ai/install.sh | bash
180180

181181
RUN curl -fsSL https://opencode.ai/install | bash
182182

183+
RUN bunx playwright install chromium --with-deps
184+
183185
USER root
184186

185187
ENV PATH="/home/workspace/.opencode/bin:${PATH}"

src/workspace/manager.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -539,14 +539,22 @@ export class WorkspaceManager {
539539
containerEnv.WORKSPACE_REPO_URL = clone;
540540
}
541541

542+
const dockerVolumeName = `${VOLUME_PREFIX}${name}-docker`;
543+
if (!(await docker.volumeExists(dockerVolumeName))) {
544+
await docker.createVolume(dockerVolumeName);
545+
}
546+
542547
const containerId = await docker.createContainer({
543548
name: containerName,
544549
image: workspaceImage,
545550
hostname: name,
546551
privileged: true,
547552
restartPolicy: 'unless-stopped',
548553
env: containerEnv,
549-
volumes: [{ source: volumeName, target: '/home/workspace', readonly: false }],
554+
volumes: [
555+
{ source: volumeName, target: '/home/workspace', readonly: false },
556+
{ source: dockerVolumeName, target: '/var/lib/docker', readonly: false },
557+
],
550558
ports: [{ hostPort: sshPort, containerPort: 22, protocol: 'tcp' }],
551559
labels: {
552560
'workspace.name': name,
@@ -620,14 +628,22 @@ export class WorkspaceManager {
620628
containerEnv.WORKSPACE_REPO_URL = workspace.repo;
621629
}
622630

631+
const dockerVolumeName = `${VOLUME_PREFIX}${name}-docker`;
632+
if (!(await docker.volumeExists(dockerVolumeName))) {
633+
await docker.createVolume(dockerVolumeName);
634+
}
635+
623636
const containerId = await docker.createContainer({
624637
name: containerName,
625638
image: workspaceImage,
626639
hostname: name,
627640
privileged: true,
628641
restartPolicy: 'unless-stopped',
629642
env: containerEnv,
630-
volumes: [{ source: volumeName, target: '/home/workspace', readonly: false }],
643+
volumes: [
644+
{ source: volumeName, target: '/home/workspace', readonly: false },
645+
{ source: dockerVolumeName, target: '/var/lib/docker', readonly: false },
646+
],
631647
ports: [{ hostPort: sshPort, containerPort: 22, protocol: 'tcp' }],
632648
labels: {
633649
'workspace.name': name,
@@ -695,6 +711,7 @@ export class WorkspaceManager {
695711

696712
const containerName = getContainerName(name);
697713
const volumeName = `${VOLUME_PREFIX}${name}`;
714+
const dockerVolumeName = `${VOLUME_PREFIX}${name}-docker`;
698715

699716
if (await docker.containerExists(containerName)) {
700717
await docker.removeContainer(containerName, true);
@@ -704,6 +721,10 @@ export class WorkspaceManager {
704721
await docker.removeVolume(volumeName, true);
705722
}
706723

724+
if (await docker.volumeExists(dockerVolumeName)) {
725+
await docker.removeVolume(dockerVolumeName, true);
726+
}
727+
707728
await this.state.deleteWorkspace(name);
708729
}
709730

0 commit comments

Comments
 (0)