diff --git a/scripts/provision-worktree.sh b/scripts/provision-worktree.sh index 7b84acaf..1dd481b1 100644 --- a/scripts/provision-worktree.sh +++ b/scripts/provision-worktree.sh @@ -346,7 +346,6 @@ list_base_node_modules_paths() { ! -path './.paperclip/*' \ | sed 's#^\./##' } - if [[ -f "$worktree_cwd/package.json" && -f "$worktree_cwd/pnpm-lock.yaml" ]]; then needs_install=0 diff --git a/server/src/__tests__/workspace-runtime.test.ts b/server/src/__tests__/workspace-runtime.test.ts index b5e1dc31..3b1bee0d 100644 --- a/server/src/__tests__/workspace-runtime.test.ts +++ b/server/src/__tests__/workspace-runtime.test.ts @@ -899,6 +899,106 @@ describe("realizeExecutionWorkspace", () => { ); }, 30_000); + it("provisions worktree-local pnpm node_modules instead of reusing base-repo links", async () => { + const repoRoot = await createTempRepo(); + await fs.mkdir(path.join(repoRoot, "scripts"), { recursive: true }); + await fs.mkdir(path.join(repoRoot, "packages", "shared"), { recursive: true }); + await fs.mkdir(path.join(repoRoot, "server"), { recursive: true }); + await fs.writeFile( + path.join(repoRoot, "package.json"), + JSON.stringify( + { + name: "workspace-root", + private: true, + packageManager: "pnpm@9.15.4", + }, + null, + 2, + ), + "utf8", + ); + await fs.writeFile( + path.join(repoRoot, "pnpm-workspace.yaml"), + ["packages:", " - packages/*", " - server", ""].join("\n"), + "utf8", + ); + await fs.writeFile( + path.join(repoRoot, "packages", "shared", "package.json"), + JSON.stringify( + { + name: "@repo/shared", + version: "1.0.0", + private: true, + type: "module", + exports: "./index.js", + }, + null, + 2, + ), + "utf8", + ); + await fs.writeFile(path.join(repoRoot, "packages", "shared", "index.js"), "export const value = 'shared';\n", "utf8"); + await fs.writeFile( + path.join(repoRoot, "server", "package.json"), + JSON.stringify( + { + name: "server", + private: true, + type: "module", + dependencies: { + "@repo/shared": "workspace:*", + }, + }, + null, + 2, + ), + "utf8", + ); + await fs.writeFile(path.join(repoRoot, "server", "index.js"), "export {};\n", "utf8"); + await fs.copyFile(provisionWorktreeScriptPath, path.join(repoRoot, "scripts", "provision-worktree.sh")); + await fs.chmod(path.join(repoRoot, "scripts", "provision-worktree.sh"), 0o755); + await runPnpm(repoRoot, ["install"]); + await runGit(repoRoot, ["add", "."]); + await runGit(repoRoot, ["commit", "-m", "Add pnpm workspace fixture"]); + + const workspace = await realizeExecutionWorkspace({ + base: { + baseCwd: repoRoot, + source: "project_primary", + projectId: "project-1", + workspaceId: "workspace-1", + repoUrl: null, + repoRef: "HEAD", + }, + config: { + workspaceStrategy: { + type: "git_worktree", + branchTemplate: "{{issue.identifier}}-{{slug}}", + provisionCommand: "bash ./scripts/provision-worktree.sh", + }, + }, + issue: { + id: "issue-1", + identifier: "PAP-551", + title: "Provision local workspace dependencies", + }, + agent: { + id: "agent-1", + name: "Codex Coder", + companyId: "company-1", + }, + }); + + expect((await fs.lstat(path.join(workspace.cwd, "node_modules"))).isSymbolicLink()).toBe(false); + expect((await fs.lstat(path.join(workspace.cwd, "server", "node_modules"))).isSymbolicLink()).toBe(false); + await expect(fs.realpath(path.join(workspace.cwd, "server", "node_modules", "@repo", "shared"))).resolves.toBe( + await fs.realpath(path.join(workspace.cwd, "packages", "shared")), + ); + await expect(fs.realpath(path.join(repoRoot, "server", "node_modules", "@repo", "shared"))).resolves.toBe( + await fs.realpath(path.join(repoRoot, "packages", "shared")), + ); + }); + it("records worktree setup and provision operations when a recorder is provided", async () => { const repoRoot = await createTempRepo(); const { recorder, operations } = createWorkspaceOperationRecorderDouble();