Default git shipping to pull requests

This commit is contained in:
Mohamed Boudra
2026-06-05 22:56:33 +07:00
parent e09a1591ac
commit 20c03355f9
3 changed files with 44 additions and 9 deletions
+22 -4
View File
@@ -49,7 +49,7 @@ function createInput(overrides: Partial<BuildGitActionsInput> = {}): BuildGitAct
aheadOfOrigin: 0, aheadOfOrigin: 0,
behindOfOrigin: 0, behindOfOrigin: 0,
shouldPromoteArchive: false, shouldPromoteArchive: false,
shipDefault: "merge", shipDefault: "pr",
runtime: { runtime: {
commit: { commit: {
disabled: false, disabled: false,
@@ -387,13 +387,31 @@ describe("git-actions-policy", () => {
}); });
}); });
it("promotes local merge over update-from-base", () => { it("promotes Create PR over push and local merge when PR is the ship default", () => {
const actions = buildGitActions(
createInput({
hasRemote: true,
isOnBaseBranch: false,
aheadCount: 2,
aheadOfOrigin: 2,
behindBaseCount: 3,
}),
);
expect(actions.primary).toMatchObject({
id: "pr",
label: "Create PR",
});
});
it("uses local merge when merge is the stored ship default", () => {
const actions = buildGitActions( const actions = buildGitActions(
createInput({ createInput({
hasRemote: true, hasRemote: true,
isOnBaseBranch: false, isOnBaseBranch: false,
aheadCount: 2, aheadCount: 2,
behindBaseCount: 3, behindBaseCount: 3,
shipDefault: "merge",
}), }),
); );
@@ -665,7 +683,7 @@ describe("git-actions-policy", () => {
}), }),
); );
expect(actions.primary).toMatchObject({ id: "merge-branch", label: "Merge locally" }); expect(actions.primary).toMatchObject({ id: "pr", label: "View PR" });
expect(actions.secondary.some((action) => action.id.startsWith("enable-pr-auto-merge"))).toBe( expect(actions.secondary.some((action) => action.id.startsWith("enable-pr-auto-merge"))).toBe(
false, false,
); );
@@ -766,7 +784,7 @@ describe("git-actions-policy", () => {
}), }),
); );
expect(actions.primary).toMatchObject({ id: "merge-branch", label: "Merge locally" }); expect(actions.primary).toMatchObject({ id: "pr", label: "View PR" });
expect( expect(
actions.secondary.some((action) => actions.secondary.some((action) =>
["merge-pr-squash", "merge-pr-merge", "merge-pr-rebase"].includes(action.id), ["merge-pr-squash", "merge-pr-merge", "merge-pr-rebase"].includes(action.id),
+16 -3
View File
@@ -333,9 +333,6 @@ function getPrimaryActionId(input: BuildGitActionsInput): GitActionId | null {
if (canPull(input)) { if (canPull(input)) {
return "pull"; return "pull";
} }
if (canPush(input)) {
return "push";
}
if (canMergePr(input)) { if (canMergePr(input)) {
return getDefaultDirectPullRequestMergeActionId(input); return getDefaultDirectPullRequestMergeActionId(input);
} }
@@ -345,6 +342,12 @@ function getPrimaryActionId(input: BuildGitActionsInput): GitActionId | null {
if (hasEnabledPrAutoMerge(input)) { if (hasEnabledPrAutoMerge(input)) {
return "pr"; return "pr";
} }
if (input.shipDefault === "pr" && canUsePullRequestActionAsShipDefault(input)) {
return "pr";
}
if (canPush(input)) {
return "push";
}
if (!input.isOnBaseBranch && input.aheadCount > 0) { if (!input.isOnBaseBranch && input.aheadCount > 0) {
return "merge-branch"; return "merge-branch";
} }
@@ -502,6 +505,16 @@ function canMergeFromBase(input: BuildGitActionsInput): boolean {
); );
} }
function canUsePullRequestActionAsShipDefault(input: BuildGitActionsInput): boolean {
if (input.isOnBaseBranch || !input.githubFeaturesEnabled) {
return false;
}
if (input.hasPullRequest) {
return input.pullRequestUrl !== null;
}
return input.aheadCount > 0;
}
function canMergePr(input: BuildGitActionsInput): boolean { function canMergePr(input: BuildGitActionsInput): boolean {
const github = input.pullRequestGithub; const github = input.pullRequestGithub;
const canMergeFromPullRequestStatus = const canMergeFromPullRequestStatus =
+6 -2
View File
@@ -155,7 +155,7 @@ interface UseGitActionsResult {
export function useGitActions({ serverId, cwd, icons }: UseGitActionsInput): UseGitActionsResult { export function useGitActions({ serverId, cwd, icons }: UseGitActionsInput): UseGitActionsResult {
const toast = useToast(); const toast = useToast();
const [postShipArchiveSuggested, setPostShipArchiveSuggested] = useState(false); const [postShipArchiveSuggested, setPostShipArchiveSuggested] = useState(false);
const [shipDefault, setShipDefault] = useState<"merge" | "pr">("merge"); const [shipDefault, setShipDefault] = useState<"merge" | "pr">("pr");
const { status, isLoading: isStatusLoading } = useCheckoutStatusQuery({ serverId, cwd }); const { status, isLoading: isStatusLoading } = useCheckoutStatusQuery({ serverId, cwd });
const gitStatus = status && status.isGit ? status : null; const gitStatus = status && status.isGit ? status : null;
@@ -186,15 +186,19 @@ export function useGitActions({ serverId, cwd, icons }: UseGitActionsInput): Use
useEffect(() => { useEffect(() => {
if (!shipDefaultStorageKey) { if (!shipDefaultStorageKey) {
setShipDefault("pr");
return; return;
} }
let isActive = true; let isActive = true;
setShipDefault("pr");
AsyncStorage.getItem(shipDefaultStorageKey) AsyncStorage.getItem(shipDefaultStorageKey)
.then((value) => { .then((value) => {
if (!isActive) return; if (!isActive) return;
if (value === "pr" || value === "merge") { if (value === "pr" || value === "merge") {
setShipDefault(value); setShipDefault(value);
return;
} }
setShipDefault("pr");
return; return;
}) })
.catch(() => undefined); .catch(() => undefined);
@@ -210,7 +214,7 @@ export function useGitActions({ serverId, cwd, icons }: UseGitActionsInput): Use
try { try {
await AsyncStorage.setItem(shipDefaultStorageKey, next); await AsyncStorage.setItem(shipDefaultStorageKey, next);
} catch { } catch {
// Ignore persistence failures; default will reset to "merge". // Ignore persistence failures; default will reset to "pr".
} }
}, },
[shipDefaultStorageKey], [shipDefaultStorageKey],