mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-27 19:35:13 +01:00
Default worktrees to fetched origin master
This commit is contained in:
1 parent
438c2c085c
commit
9028bda6a4
2 files changed
+69
-9
No files matched your search
@@ -62,6 +62,10 @@ async function runGit(cwd: string, args: string[]) {
|
||||
await execFileAsync("git", args, { cwd });
|
||||
}
|
||||
|
||||
async function readGit(cwd: string, args: string[]) {
|
||||
return (await execFileAsync("git", args, { cwd })).stdout.trim();
|
||||
}
|
||||
|
||||
async function runPnpm(cwd: string, args: string[]) {
|
||||
await execFileAsync("pnpm", args, { cwd });
|
||||
}
|
||||
@@ -304,6 +308,57 @@ describe("ensureServerWorkspaceLinksCurrent", () => {
|
||||
});
|
||||
|
||||
describe("realizeExecutionWorkspace", () => {
|
||||
it("defaults new git worktrees to freshly fetched origin/master", async () => {
|
||||
const sourceRepo = await createTempRepo("master");
|
||||
const remoteDir = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-worktree-remote-"));
|
||||
const remotePath = path.join(remoteDir, "paperclip.git");
|
||||
await execFileAsync("git", ["clone", "--bare", sourceRepo, remotePath]);
|
||||
|
||||
const cloneRoot = await fs.mkdtemp(path.join(os.tmpdir(), "paperclip-worktree-clone-"));
|
||||
const repoRoot = path.join(cloneRoot, "paperclip");
|
||||
await execFileAsync("git", ["clone", remotePath, repoRoot]);
|
||||
await runGit(repoRoot, ["config", "user.email", "paperclip@example.com"]);
|
||||
await runGit(repoRoot, ["config", "user.name", "Paperclip Test"]);
|
||||
|
||||
await fs.writeFile(path.join(sourceRepo, "auth-fix.txt"), "cookie fix\n", "utf8");
|
||||
await runGit(sourceRepo, ["add", "auth-fix.txt"]);
|
||||
await runGit(sourceRepo, ["commit", "-m", "Add auth fix"]);
|
||||
await runGit(sourceRepo, ["push", remotePath, "master"]);
|
||||
const expectedRemoteHead = await readGit(sourceRepo, ["rev-parse", "master"]);
|
||||
expect(await readGit(repoRoot, ["rev-parse", "origin/master"])).not.toBe(expectedRemoteHead);
|
||||
|
||||
const workspace = await realizeExecutionWorkspace({
|
||||
base: {
|
||||
baseCwd: repoRoot,
|
||||
source: "project_primary",
|
||||
projectId: "project-1",
|
||||
workspaceId: "workspace-1",
|
||||
repoUrl: null,
|
||||
repoRef: null,
|
||||
},
|
||||
config: {
|
||||
workspaceStrategy: {
|
||||
type: "git_worktree",
|
||||
branchTemplate: "{{issue.identifier}}-{{slug}}",
|
||||
},
|
||||
},
|
||||
issue: {
|
||||
id: "issue-1",
|
||||
identifier: "PAP-447",
|
||||
title: "Add Worktree Support",
|
||||
},
|
||||
agent: {
|
||||
id: "agent-1",
|
||||
name: "Codex Coder",
|
||||
companyId: "company-1",
|
||||
},
|
||||
});
|
||||
|
||||
expect(workspace.baseRefSha).toBe(expectedRemoteHead);
|
||||
expect(await readGit(repoRoot, ["rev-parse", "origin/master"])).toBe(expectedRemoteHead);
|
||||
expect(await readGit(workspace.cwd, ["rev-parse", "HEAD"])).toBe(expectedRemoteHead);
|
||||
});
|
||||
|
||||
it("creates and reuses a git worktree for an issue-scoped branch", async () => {
|
||||
const repoRoot = await createTempRepo();
|
||||
|
||||
@@ -1842,7 +1897,7 @@ describe("realizeExecutionWorkspace", () => {
|
||||
config: {
|
||||
workspaceStrategy: {
|
||||
type: "git_worktree",
|
||||
// No baseRef configured — should auto-detect "master"
|
||||
// No baseRef configured — should default to origin/master.
|
||||
},
|
||||
},
|
||||
issue: {
|
||||
@@ -1860,10 +1915,10 @@ describe("realizeExecutionWorkspace", () => {
|
||||
|
||||
expect(workspace.strategy).toBe("git_worktree");
|
||||
expect(workspace.created).toBe(true);
|
||||
// The worktree should have been created successfully (baseRef resolved to "master")
|
||||
// The worktree should have been created successfully from the canonical remote base.
|
||||
const worktreeOp = operations.find(op => op.phase === "worktree_prepare" && op.metadata?.created);
|
||||
expect(worktreeOp).toBeDefined();
|
||||
expect(worktreeOp!.metadata!.baseRef).toBe("master");
|
||||
expect(worktreeOp!.metadata!.baseRef).toBe("origin/master");
|
||||
}, 10_000);
|
||||
|
||||
it("auto-detects the default branch via symbolic-ref when origin/HEAD is set", async () => {
|
||||
@@ -1894,7 +1949,7 @@ describe("realizeExecutionWorkspace", () => {
|
||||
config: {
|
||||
workspaceStrategy: {
|
||||
type: "git_worktree",
|
||||
// No baseRef configured — should auto-detect "master" via symbolic-ref
|
||||
// No baseRef configured — origin/master is preferred over the symbolic-ref.
|
||||
},
|
||||
},
|
||||
issue: {
|
||||
@@ -1914,7 +1969,7 @@ describe("realizeExecutionWorkspace", () => {
|
||||
expect(workspace.created).toBe(true);
|
||||
const worktreeOp = operations.find(op => op.phase === "worktree_prepare" && op.metadata?.created);
|
||||
expect(worktreeOp).toBeDefined();
|
||||
expect(worktreeOp!.metadata!.baseRef).toBe("master");
|
||||
expect(worktreeOp!.metadata!.baseRef).toBe("origin/master");
|
||||
}, 10_000);
|
||||
|
||||
it("removes a created git worktree and branch during cleanup", async () => {
|
||||
|
||||
@@ -691,22 +691,27 @@ async function isGitCheckout(cwd: string): Promise<boolean> {
|
||||
}
|
||||
|
||||
async function detectDefaultBranch(repoRoot: string): Promise<string | null> {
|
||||
const originMasterRef = "origin/master";
|
||||
await refreshRemoteTrackingBaseRef(repoRoot, originMasterRef);
|
||||
if (await resolveBaseRefSha(repoRoot, originMasterRef)) {
|
||||
return originMasterRef;
|
||||
}
|
||||
|
||||
// Try the explicit remote HEAD first (set by git clone or git remote set-head)
|
||||
try {
|
||||
const remoteHead = await runGit(
|
||||
["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"],
|
||||
repoRoot,
|
||||
);
|
||||
const branch = remoteHead?.startsWith("origin/") ? remoteHead.slice("origin/".length) : remoteHead;
|
||||
if (branch) return branch;
|
||||
if (remoteHead) return remoteHead;
|
||||
} catch {
|
||||
// Not set — fall through to heuristic
|
||||
}
|
||||
|
||||
// Fallback: check for common default branch names on the remote
|
||||
for (const candidate of ["main", "master"]) {
|
||||
for (const candidate of ["origin/main", "main", "master"]) {
|
||||
try {
|
||||
await runGit(["rev-parse", "--verify", `refs/remotes/origin/${candidate}`], repoRoot);
|
||||
await runGit(["rev-parse", "--verify", `${candidate}^{commit}`], repoRoot);
|
||||
return candidate;
|
||||
} catch {
|
||||
// Not found — try next
|
||||
|
||||
Reference in new issue
Block a user