Skip to content

Commit b7860a8

Browse files
committed
Wait for GitHub to process git push before updating PR
Updating the pull request (with `gh pr edit`) right after a git push was causing the `synchronize` webhook to be fired twice. This results in GitHub Actions being started twice as well. This commit adds a check before editing a pull request that compares the current HEAD commit with the head id on GitHub. If these don't match, it means that GitHub has not yet processed the git push. We check this up to five times, waiting one second between checks. Fixes #189
1 parent 27a4e3d commit b7860a8

File tree

4 files changed

+65
-4
lines changed

4 files changed

+65
-4
lines changed

commands/pick/steps/git-pick.ts

+33-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { colors, log } from '../../../deps.ts';
2-
import { runQuietly } from '../../../lib/shell/shell.ts';
3-
import { GH } from '../../../lib/github/gh.ts';
42
import { Commit } from '../../../lib/git/commit.ts';
53
import { Git } from '../../../lib/git/git.ts';
4+
import { GH } from '../../../lib/github/gh.ts';
5+
import { runQuietly } from '../../../lib/shell/shell.ts';
6+
import { sleep } from '../../../lib/sleep.ts';
67

78
export type GitPickSettings = Readonly<{
89
push: boolean;
@@ -77,6 +78,8 @@ export async function runCherryPick(settings: GitPickSettings): Promise<void> {
7778
draftPR: settings.draftPR,
7879
};
7980
if (settings.updatePR) {
81+
await waitForGitHubPRToMatchCommitSHA();
82+
8083
log.info(colors.green('▶️ Updating pull request'));
8184
await GH.editPullRequest(prSettings);
8285
} else {
@@ -95,3 +98,31 @@ export async function runCherryPick(settings: GitPickSettings): Promise<void> {
9598
}
9699
}
97100
}
101+
102+
async function waitForGitHubPRToMatchCommitSHA() {
103+
const attempts = 5;
104+
105+
log.info('Verifying pull request was updated...');
106+
for (let i = 0; i < attempts; i++) {
107+
const prInfo = await GH.getPullRequestInfoForCurrentBranch();
108+
log.debug(`headRefOid of PR ${prInfo.number} is: ${prInfo.headRefOid}`);
109+
110+
const headSHA = await Git.getHEADCommitSha();
111+
log.debug(`commit SHA of HEAD is: ${headSHA}`);
112+
113+
if (!prInfo.headRefOid.startsWith(headSHA)) {
114+
log.debug(
115+
`✗ local HEAD (${headSHA}}) does not match with the current PR head (${prInfo.headRefOid})`,
116+
);
117+
await sleep(1000);
118+
} else {
119+
log.debug(
120+
`✔ local HEAD (${headSHA}}) matches with the current PR head (${prInfo.headRefOid})`,
121+
);
122+
log.info('✔ Changes pushed to PR');
123+
return;
124+
}
125+
}
126+
127+
log.error(`PR HEAD on GitHub did not match local HEAD after ${attempts} attempts`);
128+
}

lib/git/git.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { log } from '../../deps.ts';
2+
import { isDebugModeEnabled } from '../pr-cli/debug.ts';
23
import { runAndCapture, runQuietly } from '../shell/shell.ts';
34
import { Commit, CommitWithBody } from './commit.ts';
4-
import { isDebugModeEnabled } from '../pr-cli/debug.ts';
55

66
export class Git {
77
public static verifyAndExpandCommitSHAs = verifyAndExpandCommitSHAs;
@@ -15,6 +15,7 @@ export class Git {
1515
public static isValidBranchName = isValidBranchName;
1616
public static getCurrentBranch = getCurrentBranch;
1717
public static getHeadOfRemote = getHeadOfRemote;
18+
public static getHEADCommitSha = getHEADCommitSha;
1819
}
1920

2021
async function verifyAndExpandCommitSHAs(
@@ -143,13 +144,23 @@ async function listRemotes(): Promise<string[]> {
143144

144145
async function doesBranchExist(branch: string): Promise<boolean> {
145146
try {
146-
await runQuietly('git', 'rev-parse', '--verify', branch);
147+
await revParse(branch, { verify: true });
147148
return true;
148149
} catch {
149150
return false;
150151
}
151152
}
152153

154+
async function getHEADCommitSha() {
155+
return await revParse('HEAD');
156+
}
157+
158+
async function revParse(revision: string, options?: { verify?: boolean }) {
159+
const args: string[] = [];
160+
options?.verify && args.push('--verify');
161+
return await runQuietly('git', 'rev-parse', ...args, revision);
162+
}
163+
153164
async function isValidBranchName(branchName: string): Promise<boolean> {
154165
try {
155166
await runQuietly('git', 'check-ref-format', '--branch', branchName);

lib/github/gh.ts

+16
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export const GH = {
66
editPullRequest,
77
doesBranchHavePullRequest,
88
listPullRequests,
9+
getPullRequestInfoForCurrentBranch,
910
};
1011

1112
interface BasePROptions {
@@ -35,6 +36,21 @@ type PullRequestOptions =
3536
& (ManualTitleAndBody | AutomaticTitleAndBody);
3637
type EditPullRequestOptions = BasePROptions & ManualTitleAndBody;
3738

39+
async function getPullRequestInfoForCurrentBranch() {
40+
const json = await runAndCapture(
41+
'gh',
42+
'pr',
43+
'view',
44+
'--json',
45+
'headRefOid,number',
46+
);
47+
const pr = JSON.parse(json);
48+
return {
49+
headRefOid: pr.headRefOid as string,
50+
number: pr.number as number,
51+
};
52+
}
53+
3854
async function createPullRequest(options: PullRequestOptions) {
3955
const args: string[] = [];
4056
options.baseBranch && args.push('--base', options.baseBranch);

lib/sleep.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function sleep(ms: number): Promise<void> {
2+
return new Promise((resolve) => setTimeout(resolve, ms));
3+
}

0 commit comments

Comments
 (0)