Fix New Workspace PR worktree creation (#1565)
* Fix PR worktree creation from New Workspace * Use fixture default branch in GitHub E2E clone
This commit is contained in:
@@ -48,11 +48,17 @@ export interface GhRepoFixture {
|
||||
owner: string;
|
||||
name: string;
|
||||
fullName: string;
|
||||
defaultBranch: string;
|
||||
prs: GhPrFixture[];
|
||||
issues: GhIssueFixture[];
|
||||
cleanup(): Promise<void>;
|
||||
}
|
||||
|
||||
export interface GhDefaultBranchClone {
|
||||
path: string;
|
||||
cleanup(): Promise<void>;
|
||||
}
|
||||
|
||||
function gh(args: string[], opts?: { cwd?: string }): string {
|
||||
return execFileSync("gh", args, {
|
||||
cwd: opts?.cwd,
|
||||
@@ -77,8 +83,9 @@ async function seedPr(args: {
|
||||
authedUrl: string;
|
||||
fullName: string;
|
||||
repoName: string;
|
||||
defaultBranch: string;
|
||||
}): Promise<{ fixture: GhPrFixture; localPath: string }> {
|
||||
const { spec, branch, index, basePath, authedUrl, fullName, repoName } = args;
|
||||
const { spec, branch, index, basePath, authedUrl, fullName, repoName, defaultBranch } = args;
|
||||
|
||||
const createArgs = [
|
||||
"pr",
|
||||
@@ -86,7 +93,7 @@ async function seedPr(args: {
|
||||
"--title",
|
||||
spec.title,
|
||||
"--base",
|
||||
"main",
|
||||
defaultBranch,
|
||||
"--head",
|
||||
branch,
|
||||
"--body",
|
||||
@@ -166,10 +173,11 @@ export async function createTempGithubRepo(options: {
|
||||
const { category, prs = [], issues = [] } = options;
|
||||
const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
||||
const repoName = `${TEMP_GITHUB_REPO_PREFIX}${category}-${uniqueSuffix}`;
|
||||
const defaultBranch = "main";
|
||||
|
||||
// Bootstrap local git repo
|
||||
const basePath = await mkdtemp(path.join("/tmp", `${repoName}-base-`));
|
||||
git(["init", "-b", "main"], basePath);
|
||||
git(["init", "-b", defaultBranch], basePath);
|
||||
git(["config", "user.email", "e2e@paseo.test"], basePath);
|
||||
git(["config", "user.name", "Paseo E2E"], basePath);
|
||||
git(["config", "commit.gpgsign", "false"], basePath);
|
||||
@@ -197,7 +205,7 @@ export async function createTempGithubRepo(options: {
|
||||
await writeFile(path.join(basePath, `pr-${i + 1}.txt`), `PR ${i + 1}\n`);
|
||||
git(["add", `pr-${i + 1}.txt`], basePath);
|
||||
git(["commit", "-m", `Add PR ${i + 1}`], basePath);
|
||||
git(["checkout", "main"], basePath);
|
||||
git(["checkout", defaultBranch], basePath);
|
||||
}
|
||||
|
||||
if (branches.length > 0) {
|
||||
@@ -217,6 +225,7 @@ export async function createTempGithubRepo(options: {
|
||||
authedUrl,
|
||||
fullName,
|
||||
repoName,
|
||||
defaultBranch,
|
||||
});
|
||||
localPaths.push(localPath);
|
||||
prFixtures.push(fixture);
|
||||
@@ -232,6 +241,7 @@ export async function createTempGithubRepo(options: {
|
||||
owner,
|
||||
name: repoName,
|
||||
fullName,
|
||||
defaultBranch,
|
||||
prs: prFixtures,
|
||||
issues: issueFixtures,
|
||||
cleanup: async () => {
|
||||
@@ -247,3 +257,27 @@ export async function createTempGithubRepo(options: {
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function cloneGithubRepoDefaultBranchOnly(
|
||||
repo: Pick<GhRepoFixture, "fullName" | "name" | "defaultBranch">,
|
||||
): Promise<GhDefaultBranchClone> {
|
||||
const token = gh(["auth", "token"]);
|
||||
const authedUrl = `https://x-access-token:${token}@github.com/${repo.fullName}.git`;
|
||||
const clonePath = await mkdtemp(path.join("/tmp", `${repo.name}-${repo.defaultBranch}-only-`));
|
||||
|
||||
execFileSync(
|
||||
"git",
|
||||
["clone", "--quiet", "--single-branch", "--branch", repo.defaultBranch, authedUrl, clonePath],
|
||||
{ stdio: ["ignore", "pipe", "pipe"] },
|
||||
);
|
||||
git(["config", "user.email", "e2e@paseo.test"], clonePath);
|
||||
git(["config", "user.name", "Paseo E2E"], clonePath);
|
||||
git(["config", "commit.gpgsign", "false"], clonePath);
|
||||
|
||||
return {
|
||||
path: clonePath,
|
||||
cleanup: async () => {
|
||||
await rm(clonePath, { recursive: true, force: true });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -29,7 +29,11 @@ import {
|
||||
submitNewWorkspacePrompt,
|
||||
} from "./helpers/new-workspace";
|
||||
import { createTempGitRepo, readWorktreeBranchInfo } from "./helpers/workspace";
|
||||
import { createTempGithubRepo, hasGithubAuth } from "./helpers/github-fixtures";
|
||||
import {
|
||||
cloneGithubRepoDefaultBranchOnly,
|
||||
createTempGithubRepo,
|
||||
hasGithubAuth,
|
||||
} from "./helpers/github-fixtures";
|
||||
import { getServerId } from "./helpers/server-id";
|
||||
import {
|
||||
expectSidebarWorkspaceSelected,
|
||||
@@ -751,4 +755,50 @@ test.describe("New workspace flow", () => {
|
||||
await ghRepo.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test("selected GitHub PR creates the worktree from the PR head even when the head branch is not fetched", async ({
|
||||
page,
|
||||
}) => {
|
||||
test.skip(!hasGithubAuth(), "Requires GitHub authentication (gh auth login)");
|
||||
|
||||
const ghRepo = await createTempGithubRepo({
|
||||
category: "new-workspace-pr-worktree",
|
||||
prs: [{ title: "Checkout PR worktree", state: "open" }],
|
||||
});
|
||||
const pr = ghRepo.prs[0]!;
|
||||
const mainCheckout = await cloneGithubRepoDefaultBranchOnly(ghRepo);
|
||||
|
||||
try {
|
||||
const openedProject = await openProjectViaDaemon(client, mainCheckout.path);
|
||||
localWorkspaceIds.add(openedProject.workspaceId);
|
||||
|
||||
await gotoAppShell(page);
|
||||
await waitForSidebarHydration(page);
|
||||
await openNewWorkspaceComposer(page, {
|
||||
projectKey: openedProject.projectKey,
|
||||
projectDisplayName: openedProject.projectDisplayName,
|
||||
});
|
||||
await selectWorkspaceIsolation(page, "worktree");
|
||||
await openStartingRefPicker(page);
|
||||
await selectGitHubPrInPicker(page, pr.number);
|
||||
await submitNewWorkspaceWithoutPrompt(page);
|
||||
|
||||
const worktree = await assertNewWorkspaceSidebarAndHeader(page, {
|
||||
serverId: getServerId(),
|
||||
client,
|
||||
previousWorkspaceId: openedProject.workspaceId,
|
||||
projectDisplayName: openedProject.projectDisplayName,
|
||||
});
|
||||
createdWorktreeDirectories.add(worktree.workspaceDirectory);
|
||||
|
||||
const branchInfo = await readWorktreeBranchInfo({
|
||||
worktreePath: worktree.workspaceDirectory,
|
||||
});
|
||||
expect(branchInfo.currentBranch).toBe(pr.branch);
|
||||
expect(existsSync(path.join(worktree.workspaceDirectory, "pr-1.txt"))).toBe(true);
|
||||
} finally {
|
||||
await mainCheckout.cleanup();
|
||||
await ghRepo.cleanup();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,11 +20,13 @@ export function pickerItemToCheckoutRequest(
|
||||
switch (item.kind) {
|
||||
case "branch":
|
||||
return { action: "branch-off", refName: item.name };
|
||||
case "github-pr":
|
||||
case "github-pr": {
|
||||
const headRefName = item.item.headRefName?.trim();
|
||||
return {
|
||||
action: "checkout",
|
||||
refName: item.item.headRefName ?? "",
|
||||
...(headRefName ? { refName: headRefName } : {}),
|
||||
githubPrNumber: item.item.number,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -804,8 +804,8 @@ async function createMultiplicityWorkspace(input: {
|
||||
createFailedMessage: string;
|
||||
}): Promise<ReturnType<typeof normalizeWorkspaceDescriptor>> {
|
||||
const isWorktree = input.isolation === "worktree";
|
||||
const baseBranch = isWorktree
|
||||
? (resolveCheckoutRequest(input.selectedItem, input.currentBranch)?.refName ?? undefined)
|
||||
const checkoutRequest = isWorktree
|
||||
? resolveCheckoutRequest(input.selectedItem, input.currentBranch)
|
||||
: undefined;
|
||||
const firstAgentContext = buildFirstAgentContext({
|
||||
prompt: input.prompt,
|
||||
@@ -818,7 +818,7 @@ async function createMultiplicityWorkspace(input: {
|
||||
cwd: input.project.iconWorkingDir,
|
||||
projectId: input.project.projectKey,
|
||||
worktreeSlug: createNameId(),
|
||||
...(baseBranch ? { baseBranch } : {}),
|
||||
...checkoutRequest,
|
||||
}
|
||||
: {
|
||||
kind: "directory",
|
||||
|
||||
Reference in New Issue
Block a user