feat(app): add pull-and-push git action (#627)
Combines `git pull` followed by `git push` behind a single action in the git actions split-button. Push is skipped if pull fails, and errors are surfaced through the same toast pathway as the existing pull and push actions.
This commit is contained in:
@@ -93,6 +93,72 @@ describe("checkout-git-actions-store", () => {
|
||||
expect(store.getStatus({ serverId, cwd, actionId: "commit" })).toBe("idle");
|
||||
});
|
||||
|
||||
it("runs pull then push sequentially for pull-and-push", async () => {
|
||||
const order: string[] = [];
|
||||
const client = {
|
||||
checkoutPull: vi.fn(async () => {
|
||||
order.push("pull");
|
||||
return {};
|
||||
}),
|
||||
checkoutPush: vi.fn(async () => {
|
||||
order.push("push");
|
||||
return {};
|
||||
}),
|
||||
};
|
||||
useSessionStore.setState((state) => ({
|
||||
...state,
|
||||
sessions: {
|
||||
...state.sessions,
|
||||
[serverId]: { client } as unknown as (typeof state.sessions)[string],
|
||||
},
|
||||
}));
|
||||
|
||||
await useCheckoutGitActionsStore.getState().pullAndPush({ serverId, cwd });
|
||||
|
||||
expect(order).toEqual(["pull", "push"]);
|
||||
expect(client.checkoutPull).toHaveBeenCalledWith(cwd);
|
||||
expect(client.checkoutPush).toHaveBeenCalledWith(cwd);
|
||||
});
|
||||
|
||||
it("does not push when pull fails for pull-and-push", async () => {
|
||||
const client = {
|
||||
checkoutPull: vi.fn(async () => ({ error: { message: "pull conflict" } })),
|
||||
checkoutPush: vi.fn(async () => ({})),
|
||||
};
|
||||
useSessionStore.setState((state) => ({
|
||||
...state,
|
||||
sessions: {
|
||||
...state.sessions,
|
||||
[serverId]: { client } as unknown as (typeof state.sessions)[string],
|
||||
},
|
||||
}));
|
||||
|
||||
await expect(
|
||||
useCheckoutGitActionsStore.getState().pullAndPush({ serverId, cwd }),
|
||||
).rejects.toThrow("pull conflict");
|
||||
expect(client.checkoutPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("surfaces push errors from pull-and-push after a successful pull", async () => {
|
||||
const client = {
|
||||
checkoutPull: vi.fn(async () => ({})),
|
||||
checkoutPush: vi.fn(async () => ({ error: { message: "push rejected" } })),
|
||||
};
|
||||
useSessionStore.setState((state) => ({
|
||||
...state,
|
||||
sessions: {
|
||||
...state.sessions,
|
||||
[serverId]: { client } as unknown as (typeof state.sessions)[string],
|
||||
},
|
||||
}));
|
||||
|
||||
await expect(
|
||||
useCheckoutGitActionsStore.getState().pullAndPush({ serverId, cwd }),
|
||||
).rejects.toThrow("push rejected");
|
||||
expect(client.checkoutPull).toHaveBeenCalledTimes(1);
|
||||
expect(client.checkoutPush).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("invalidates checkout PR status and every PR pane timeline for a checkout", async () => {
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ export type CheckoutGitAsyncActionId =
|
||||
| "commit"
|
||||
| "pull"
|
||||
| "push"
|
||||
| "pull-and-push"
|
||||
| "create-pr"
|
||||
| "merge-branch"
|
||||
| "merge-from-base"
|
||||
@@ -234,6 +235,7 @@ interface CheckoutGitActionsStoreState {
|
||||
commit: (params: { serverId: string; cwd: string }) => Promise<void>;
|
||||
pull: (params: { serverId: string; cwd: string }) => Promise<void>;
|
||||
push: (params: { serverId: string; cwd: string }) => Promise<void>;
|
||||
pullAndPush: (params: { serverId: string; cwd: string }) => Promise<void>;
|
||||
createPr: (params: { serverId: string; cwd: string }) => Promise<void>;
|
||||
mergeBranch: (params: { serverId: string; cwd: string; baseRef: string }) => Promise<void>;
|
||||
mergeFromBase: (params: { serverId: string; cwd: string; baseRef: string }) => Promise<void>;
|
||||
@@ -347,6 +349,25 @@ export const useCheckoutGitActionsStore = create<CheckoutGitActionsStoreState>()
|
||||
});
|
||||
},
|
||||
|
||||
pullAndPush: async ({ serverId, cwd }) => {
|
||||
await runCheckoutAction({
|
||||
serverId,
|
||||
cwd,
|
||||
actionId: "pull-and-push",
|
||||
run: async () => {
|
||||
const client = resolveClient(serverId);
|
||||
const pullPayload = await client.checkoutPull(cwd);
|
||||
if (pullPayload.error) {
|
||||
throw new Error(pullPayload.error.message);
|
||||
}
|
||||
const pushPayload = await client.checkoutPush(cwd);
|
||||
if (pushPayload.error) {
|
||||
throw new Error(pushPayload.error.message);
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
createPr: async ({ serverId, cwd }) => {
|
||||
await runCheckoutAction({
|
||||
serverId,
|
||||
|
||||
Reference in New Issue
Block a user