Add pull action and restructure git actions policy
This commit is contained in:
@@ -780,6 +780,61 @@ describe("DaemonClient", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("requests checkout pull via RPC", async () => {
|
||||
const logger = createMockLogger();
|
||||
const mock = createMockTransport();
|
||||
|
||||
const client = new DaemonClient({
|
||||
url: "ws://test",
|
||||
clientId: "clsk_unit_test",
|
||||
logger,
|
||||
reconnect: { enabled: false },
|
||||
transportFactory: () => mock.transport,
|
||||
});
|
||||
clients.push(client);
|
||||
|
||||
const connectPromise = client.connect();
|
||||
mock.triggerOpen();
|
||||
await connectPromise;
|
||||
|
||||
const promise = client.checkoutPull("/tmp/project", "req-pull");
|
||||
|
||||
expect(mock.sent).toHaveLength(1);
|
||||
const request = JSON.parse(mock.sent[0]) as {
|
||||
type: "session";
|
||||
message: {
|
||||
type: "checkout_pull_request";
|
||||
cwd: string;
|
||||
requestId: string;
|
||||
};
|
||||
};
|
||||
expect(request.message.type).toBe("checkout_pull_request");
|
||||
expect(request.message.cwd).toBe("/tmp/project");
|
||||
expect(request.message.requestId).toBe("req-pull");
|
||||
|
||||
mock.triggerMessage(
|
||||
JSON.stringify({
|
||||
type: "session",
|
||||
message: {
|
||||
type: "checkout_pull_response",
|
||||
payload: {
|
||||
cwd: "/tmp/project",
|
||||
requestId: "req-pull",
|
||||
success: true,
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(promise).resolves.toEqual({
|
||||
cwd: "/tmp/project",
|
||||
requestId: "req-pull",
|
||||
success: true,
|
||||
error: null,
|
||||
});
|
||||
});
|
||||
|
||||
test("resubscribes checkout diff streams after reconnect", async () => {
|
||||
const logger = createMockLogger();
|
||||
const mock = createMockTransport();
|
||||
|
||||
@@ -25,6 +25,7 @@ import type {
|
||||
CheckoutCommitResponse,
|
||||
CheckoutMergeResponse,
|
||||
CheckoutMergeFromBaseResponse,
|
||||
CheckoutPullResponse,
|
||||
CheckoutPushResponse,
|
||||
CheckoutPrCreateResponse,
|
||||
CheckoutPrStatusResponse,
|
||||
@@ -218,6 +219,7 @@ type CheckoutDiffPayload = Omit<SubscribeCheckoutDiffPayload, "subscriptionId">;
|
||||
type CheckoutCommitPayload = CheckoutCommitResponse["payload"];
|
||||
type CheckoutMergePayload = CheckoutMergeResponse["payload"];
|
||||
type CheckoutMergeFromBasePayload = CheckoutMergeFromBaseResponse["payload"];
|
||||
type CheckoutPullPayload = CheckoutPullResponse["payload"];
|
||||
type CheckoutPushPayload = CheckoutPushResponse["payload"];
|
||||
type CheckoutPrCreatePayload = CheckoutPrCreateResponse["payload"];
|
||||
type CheckoutPrStatusPayload = CheckoutPrStatusResponse["payload"];
|
||||
@@ -2339,6 +2341,18 @@ export class DaemonClient {
|
||||
});
|
||||
}
|
||||
|
||||
async checkoutPull(cwd: string, requestId?: string): Promise<CheckoutPullPayload> {
|
||||
return this.sendCorrelatedSessionRequest({
|
||||
requestId,
|
||||
message: {
|
||||
type: "checkout_pull_request",
|
||||
cwd,
|
||||
},
|
||||
responseType: "checkout_pull_response",
|
||||
timeout: 60000,
|
||||
});
|
||||
}
|
||||
|
||||
async checkoutPush(cwd: string, requestId?: string): Promise<CheckoutPushPayload> {
|
||||
return this.sendCorrelatedSessionRequest({
|
||||
requestId,
|
||||
|
||||
@@ -157,6 +157,7 @@ import {
|
||||
commitChanges,
|
||||
mergeToBase,
|
||||
mergeFromBase,
|
||||
pullCurrentBranch,
|
||||
pushCurrentBranch,
|
||||
createPullRequest,
|
||||
getPullRequestStatus,
|
||||
@@ -1767,6 +1768,10 @@ export class Session {
|
||||
await this.handleCheckoutMergeFromBaseRequest(msg);
|
||||
break;
|
||||
|
||||
case "checkout_pull_request":
|
||||
await this.handleCheckoutPullRequest(msg);
|
||||
break;
|
||||
|
||||
case "checkout_push_request":
|
||||
await this.handleCheckoutPushRequest(msg);
|
||||
break;
|
||||
@@ -4729,6 +4734,37 @@ export class Session {
|
||||
}
|
||||
}
|
||||
|
||||
private async handleCheckoutPullRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "checkout_pull_request" }>,
|
||||
): Promise<void> {
|
||||
const { cwd, requestId } = msg;
|
||||
|
||||
try {
|
||||
await pullCurrentBranch(cwd);
|
||||
this.checkoutDiffManager.scheduleRefreshForCwd(cwd);
|
||||
|
||||
this.emit({
|
||||
type: "checkout_pull_response",
|
||||
payload: {
|
||||
cwd,
|
||||
success: true,
|
||||
error: null,
|
||||
requestId,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
this.emit({
|
||||
type: "checkout_pull_response",
|
||||
payload: {
|
||||
cwd,
|
||||
success: false,
|
||||
error: toCheckoutError(error),
|
||||
requestId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async handleCheckoutPushRequest(
|
||||
msg: Extract<SessionInboundMessage, { type: "checkout_push_request" }>,
|
||||
): Promise<void> {
|
||||
|
||||
@@ -1029,6 +1029,12 @@ export const CheckoutMergeFromBaseRequestSchema = z.object({
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const CheckoutPullRequestSchema = z.object({
|
||||
type: z.literal("checkout_pull_request"),
|
||||
cwd: z.string(),
|
||||
requestId: z.string(),
|
||||
});
|
||||
|
||||
export const CheckoutPushRequestSchema = z.object({
|
||||
type: z.literal("checkout_push_request"),
|
||||
cwd: z.string(),
|
||||
@@ -1420,6 +1426,7 @@ export const SessionInboundMessageSchema = z.discriminatedUnion("type", [
|
||||
CheckoutCommitRequestSchema,
|
||||
CheckoutMergeRequestSchema,
|
||||
CheckoutMergeFromBaseRequestSchema,
|
||||
CheckoutPullRequestSchema,
|
||||
CheckoutPushRequestSchema,
|
||||
CheckoutPrCreateRequestSchema,
|
||||
CheckoutPrStatusRequestSchema,
|
||||
@@ -2186,6 +2193,16 @@ export const CheckoutMergeFromBaseResponseSchema = z.object({
|
||||
}),
|
||||
});
|
||||
|
||||
export const CheckoutPullResponseSchema = z.object({
|
||||
type: z.literal("checkout_pull_response"),
|
||||
payload: z.object({
|
||||
cwd: z.string(),
|
||||
success: z.boolean(),
|
||||
error: CheckoutErrorSchema.nullable(),
|
||||
requestId: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const CheckoutPushResponseSchema = z.object({
|
||||
type: z.literal("checkout_push_response"),
|
||||
payload: z.object({
|
||||
@@ -2658,6 +2675,7 @@ export const SessionOutboundMessageSchema = z.discriminatedUnion("type", [
|
||||
CheckoutCommitResponseSchema,
|
||||
CheckoutMergeResponseSchema,
|
||||
CheckoutMergeFromBaseResponseSchema,
|
||||
CheckoutPullResponseSchema,
|
||||
CheckoutPushResponseSchema,
|
||||
CheckoutPrCreateResponseSchema,
|
||||
CheckoutPrStatusResponseSchema,
|
||||
@@ -2874,6 +2892,8 @@ export type CheckoutMergeRequest = z.infer<typeof CheckoutMergeRequestSchema>;
|
||||
export type CheckoutMergeResponse = z.infer<typeof CheckoutMergeResponseSchema>;
|
||||
export type CheckoutMergeFromBaseRequest = z.infer<typeof CheckoutMergeFromBaseRequestSchema>;
|
||||
export type CheckoutMergeFromBaseResponse = z.infer<typeof CheckoutMergeFromBaseResponseSchema>;
|
||||
export type CheckoutPullRequest = z.infer<typeof CheckoutPullRequestSchema>;
|
||||
export type CheckoutPullResponse = z.infer<typeof CheckoutPullResponseSchema>;
|
||||
export type CheckoutPushRequest = z.infer<typeof CheckoutPushRequestSchema>;
|
||||
export type CheckoutPushResponse = z.infer<typeof CheckoutPushResponseSchema>;
|
||||
export type CheckoutPrCreateRequest = z.infer<typeof CheckoutPrCreateRequestSchema>;
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
MergeConflictError,
|
||||
MergeFromBaseConflictError,
|
||||
NotGitRepoError,
|
||||
pullCurrentBranch,
|
||||
pushCurrentBranch,
|
||||
resolveRepositoryDefaultBranch,
|
||||
parseWorktreeList,
|
||||
@@ -534,6 +535,28 @@ const x = 1;
|
||||
expect(() => execSync("git rev-parse -q --verify MERGE_HEAD", { cwd: repoDir })).toThrow();
|
||||
});
|
||||
|
||||
it("pulls the current branch from origin", async () => {
|
||||
const remoteDir = join(tempDir, "remote.git");
|
||||
execSync(`git init --bare -b main ${remoteDir}`);
|
||||
execSync(`git remote add origin ${remoteDir}`, { cwd: repoDir });
|
||||
execSync("git push -u origin main", { cwd: repoDir });
|
||||
|
||||
const otherClone = join(tempDir, "other-clone");
|
||||
execSync(`git clone ${remoteDir} ${otherClone}`);
|
||||
execSync("git config user.email 'test@test.com'", { cwd: otherClone });
|
||||
execSync("git config user.name 'Test'", { cwd: otherClone });
|
||||
writeFileSync(join(otherClone, "pulled.txt"), "remote\n");
|
||||
execSync("git add pulled.txt", { cwd: otherClone });
|
||||
execSync("git -c commit.gpgsign=false commit -m 'remote pull commit'", { cwd: otherClone });
|
||||
const remoteCommit = execSync("git rev-parse HEAD", { cwd: otherClone }).toString().trim();
|
||||
execSync("git push", { cwd: otherClone });
|
||||
|
||||
await pullCurrentBranch(repoDir);
|
||||
|
||||
execSync(`git merge-base --is-ancestor ${remoteCommit} HEAD`, { cwd: repoDir });
|
||||
expect(readFileSync(join(repoDir, "pulled.txt"), "utf8")).toBe("remote\n");
|
||||
});
|
||||
|
||||
it("pushes the current branch to origin", async () => {
|
||||
const remoteDir = join(tempDir, "remote.git");
|
||||
execSync(`git init --bare -b main ${remoteDir}`);
|
||||
|
||||
@@ -1757,6 +1757,19 @@ export async function mergeFromBase(
|
||||
}
|
||||
}
|
||||
|
||||
export async function pullCurrentBranch(cwd: string): Promise<void> {
|
||||
await requireGitRepo(cwd);
|
||||
const currentBranch = await getCurrentBranch(cwd);
|
||||
if (!currentBranch || currentBranch === "HEAD") {
|
||||
throw new Error("Unable to determine current branch for pull");
|
||||
}
|
||||
const hasRemote = await hasOriginRemote(cwd);
|
||||
if (!hasRemote) {
|
||||
throw new Error("Remote 'origin' is not configured.");
|
||||
}
|
||||
await execAsync("git pull", { cwd });
|
||||
}
|
||||
|
||||
export async function pushCurrentBranch(cwd: string): Promise<void> {
|
||||
await requireGitRepo(cwd);
|
||||
const currentBranch = await getCurrentBranch(cwd);
|
||||
|
||||
Reference in New Issue
Block a user