diff --git a/src/container.ts b/src/container.ts index 4b2fa92..8e1ab78 100644 --- a/src/container.ts +++ b/src/container.ts @@ -66,22 +66,24 @@ export class ContainerManager { private async ensureImage(): Promise { const imageName = this.config.dockerImage || "claude-code-sandbox:latest"; - // Check if image already exists + // If a custom dockerfile is specified, always rebuild to ensure changes are applied + if (this.config.dockerfile) { + console.log(chalk.blue(`• Building image from custom Dockerfile: ${imageName}...`)); + await this.buildImage(this.config.dockerfile, imageName); + return; + } + + // For default builds, check if image already exists try { await this.docker.getImage(imageName).inspect(); console.log(chalk.green(`✓ Using existing image: ${imageName}`)); return; } catch (error) { - console.log(chalk.blue(`• Building image: ${imageName}...`)); + console.log(chalk.blue(`• Building default image: ${imageName}...`)); } - // Check if we need to build from Dockerfile - if (this.config.dockerfile) { - await this.buildImage(this.config.dockerfile, imageName); - } else { - // Use default Dockerfile - await this.buildDefaultImage(imageName); - } + // Use default Dockerfile + await this.buildDefaultImage(imageName); } private async buildDefaultImage(imageName: string): Promise { @@ -502,18 +504,6 @@ exec claude --dangerously-skip-permissions' > /start-claude.sh && \\ const { execSync } = require("child_process"); const fs = require("fs"); - // Helper function to get tar flags safely - const getTarFlags = () => { - try { - // Test if --no-xattrs is supported by checking tar help - execSync("tar --help 2>&1 | grep -q no-xattrs", { stdio: "pipe" }); - return "--no-xattrs"; - } catch { - // --no-xattrs not supported, use standard tar - return ""; - } - }; - try { // Get list of git-tracked files (including uncommitted changes) const trackedFiles = execSync("git ls-files", { @@ -590,19 +580,18 @@ exec claude --dangerously-skip-permissions' > /start-claude.sh && \\ // Also copy .git directory to preserve git history console.log(chalk.blue("• Copying git history...")); const gitTarFile = `/tmp/claude-sandbox-git-${Date.now()}.tar`; - // Exclude macOS resource fork files and .DS_Store when creating git archive - // Also strip extended attributes to prevent macOS xattr issues in Docker - const tarFlags = getTarFlags(); - // On macOS, also exclude extended attributes that cause Docker issues - const additionalFlags = (process.platform as string) === "darwin" ? "--no-xattrs --no-fflags" : ""; - const combinedFlags = `${tarFlags} ${additionalFlags}`.trim(); - execSync( - `tar -cf "${gitTarFile}" --exclude="._*" --exclude=".DS_Store" ${combinedFlags} .git`, - { - cwd: workDir, - stdio: "pipe", - }, - ); + + // On macOS, use --no-xattrs and --no-fflags to prevent extended attribute issues + let tarCommand = `tar -cf "${gitTarFile}" --exclude="._*" --exclude=".DS_Store"`; + if (process.platform === "darwin") { + tarCommand += " --no-xattrs --no-fflags"; + } + tarCommand += " .git"; + + execSync(tarCommand, { + cwd: workDir, + stdio: "pipe", + }); try { const gitStream = fs.createReadStream(gitTarFile); @@ -636,18 +625,6 @@ exec claude --dangerously-skip-permissions' > /start-claude.sh && \\ const path = require("path"); const { execSync } = require("child_process"); - // Helper function to get tar flags safely - const getTarFlags = () => { - try { - // Test if --no-xattrs is supported by checking tar help - execSync("tar --help 2>&1 | grep -q no-xattrs", { stdio: "pipe" }); - return "--no-xattrs"; - } catch { - // --no-xattrs not supported, use standard tar - return ""; - } - }; - try { // First, try to get credentials from macOS Keychain if on Mac if (process.platform === "darwin") { @@ -779,24 +756,26 @@ exec claude --dangerously-skip-permissions' > /start-claude.sh && \\ // Copy .claude directory if it exists (but skip if we already copied from Keychain) const claudeDir = path.join(os.homedir(), ".claude"); + const skipClaudeDir = process.platform === "darwin"; if ( fs.existsSync(claudeDir) && fs.statSync(claudeDir).isDirectory() && - process.platform !== "darwin" + !skipClaudeDir ) { console.log(chalk.blue("• Copying .claude directory...")); const tarFile = `/tmp/claude-dir-${Date.now()}.tar`; - const tarFlags = getTarFlags(); - // On macOS, also exclude extended attributes that cause Docker issues - const additionalFlags = (process.platform as string) === "darwin" ? "--no-xattrs --no-fflags" : ""; - const combinedFlags = `${tarFlags} ${additionalFlags}`.trim(); - execSync( - `tar -cf "${tarFile}" ${combinedFlags} -C "${os.homedir()}" .claude`, - { - stdio: "pipe", - }, - ); + + // On macOS, use --no-xattrs and --no-fflags to prevent extended attribute issues + let tarCommand = `tar -cf "${tarFile}"`; + if (process.platform === "darwin") { + tarCommand += " --no-xattrs --no-fflags"; + } + tarCommand += ` -C "${os.homedir()}" .claude`; + + execSync(tarCommand, { + stdio: "pipe", + }); const stream = fs.createReadStream(tarFile); await container.putArchive(stream, {