mirror of
https://github.com/alkimake/paperclip.git
synced 2026-06-17 03:10:38 +09:00
Provision local node_modules in issue worktrees
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
parent
27accb1bdb
commit
22af797ca3
2 changed files with 143 additions and 0 deletions
|
|
@ -335,6 +335,44 @@ disable_seeded_routines() {
|
||||||
|
|
||||||
disable_seeded_routines
|
disable_seeded_routines
|
||||||
|
|
||||||
|
if [[ -f "$worktree_cwd/package.json" && -f "$worktree_cwd/pnpm-lock.yaml" ]]; then
|
||||||
|
needs_install=0
|
||||||
|
|
||||||
|
while IFS= read -r relative_path; do
|
||||||
|
[[ -n "$relative_path" ]] || continue
|
||||||
|
target_path="$worktree_cwd/$relative_path"
|
||||||
|
|
||||||
|
if [[ -L "$target_path" ]]; then
|
||||||
|
rm "$target_path"
|
||||||
|
needs_install=1
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -e "$target_path" ]]; then
|
||||||
|
needs_install=1
|
||||||
|
fi
|
||||||
|
done < <(
|
||||||
|
cd "$base_cwd" &&
|
||||||
|
find . \
|
||||||
|
-mindepth 1 \
|
||||||
|
-maxdepth 3 \
|
||||||
|
-type d \
|
||||||
|
-name node_modules \
|
||||||
|
! -path './.git/*' \
|
||||||
|
! -path './.paperclip/*' \
|
||||||
|
| sed 's#^\./##'
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ "$needs_install" -eq 1 ]]; then
|
||||||
|
(
|
||||||
|
cd "$worktree_cwd"
|
||||||
|
pnpm install --frozen-lockfile
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
while IFS= read -r relative_path; do
|
while IFS= read -r relative_path; do
|
||||||
[[ -n "$relative_path" ]] || continue
|
[[ -n "$relative_path" ]] || continue
|
||||||
source_path="$base_cwd/$relative_path"
|
source_path="$base_cwd/$relative_path"
|
||||||
|
|
|
||||||
|
|
@ -50,11 +50,16 @@ if (!embeddedPostgresSupport.supported) {
|
||||||
`Skipping embedded Postgres workspace-runtime tests on this host: ${embeddedPostgresSupport.reason ?? "unsupported environment"}`,
|
`Skipping embedded Postgres workspace-runtime tests on this host: ${embeddedPostgresSupport.reason ?? "unsupported environment"}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const provisionWorktreeScriptPath = new URL("../../../scripts/provision-worktree.sh", import.meta.url);
|
||||||
|
|
||||||
async function runGit(cwd: string, args: string[]) {
|
async function runGit(cwd: string, args: string[]) {
|
||||||
await execFileAsync("git", args, { cwd });
|
await execFileAsync("git", args, { cwd });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function runPnpm(cwd: string, args: string[]) {
|
||||||
|
await execFileAsync("pnpm", args, { cwd });
|
||||||
|
}
|
||||||
|
|
||||||
async function createTempRepo(defaultBranch = "main") {
|
async function createTempRepo(defaultBranch = "main") {
|
||||||
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-worktree-repo-"));
|
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-worktree-repo-"));
|
||||||
await runGit(repoRoot, ["init"]);
|
await runGit(repoRoot, ["init"]);
|
||||||
|
|
@ -557,6 +562,106 @@ describe("realizeExecutionWorkspace", () => {
|
||||||
}
|
}
|
||||||
}, 15_000);
|
}, 15_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 () => {
|
it("records worktree setup and provision operations when a recorder is provided", async () => {
|
||||||
const repoRoot = await createTempRepo();
|
const repoRoot = await createTempRepo();
|
||||||
const { recorder, operations } = createWorkspaceOperationRecorderDouble();
|
const { recorder, operations } = createWorkspaceOperationRecorderDouble();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue