Skip to content

Commit b3e277c

Browse files
authored
feat: make git pushes more robust (#156)
1 parent ffd6f39 commit b3e277c

File tree

4 files changed

+68
-1
lines changed

4 files changed

+68
-1
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"axios": "^1.4.0",
2626
"axios-retry": "^4.0.0",
2727
"diff": "^5.1.0",
28+
"exponential-backoff": "3.1.1",
2829
"extract-zip": "^2.0.1",
2930
"gcp-metadata": "^6.0.0",
3031
"nodemailer": "^6.7.8",

src/domain/create-entry.spec.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createTwoFilesPatch } from "diff";
2+
import { BackoffOptions, backOff } from "exponential-backoff";
23
import { Mocked, mocked } from "jest-mock";
34
import { randomUUID } from "node:crypto";
45
import fs, { PathLike } from "node:fs";
@@ -37,6 +38,9 @@ jest.mock("../infrastructure/github");
3738
jest.mock("./integrity-hash");
3839
jest.mock("./release-archive");
3940
jest.mock("node:fs");
41+
jest.mock("exponential-backoff");
42+
43+
const realExponentialBackoff = jest.requireActual("exponential-backoff");
4044

4145
const mockedFileReads: { [path: string]: string } = {};
4246
const EXTRACTED_MODULE_PATH = "/fake/path/to/MODULE.bazel";
@@ -1061,6 +1065,17 @@ describe("commitEntryToNewBranch", () => {
10611065
});
10621066

10631067
describe("pushEntryToFork", () => {
1068+
beforeEach(() => {
1069+
// Reduce the exponential-backoff delay to 0 for tests
1070+
(backOff as unknown as jest.SpyInstance).mockImplementation(
1071+
(request: () => Promise<void>, options?: BackoffOptions) =>
1072+
realExponentialBackoff.backOff(request, {
1073+
...options,
1074+
startingDelay: 0,
1075+
})
1076+
);
1077+
});
1078+
10641079
test("acquires an authenticated remote url for the bcr fork", async () => {
10651080
const bcrRepo = CANONICAL_BCR;
10661081
const bcrForkRepo = new Repository("bazel-central-registry", "aspect");
@@ -1136,6 +1151,45 @@ describe("pushEntryToFork", () => {
11361151
branchName
11371152
);
11381153
});
1154+
1155+
test("retries 5 times if it fails", async () => {
1156+
const bcrRepo = CANONICAL_BCR;
1157+
const bcrForkRepo = new Repository("bazel-central-registry", "aspect");
1158+
const branchName = `repo/[email protected]`;
1159+
1160+
(backOff as unknown as jest.SpyInstance).mockImplementation(
1161+
(request: () => Promise<any>, options?: BackoffOptions) => {
1162+
return realExponentialBackoff.backOff(request, {
1163+
...options,
1164+
startingDelay: 0,
1165+
});
1166+
}
1167+
);
1168+
1169+
mockGitClient.push
1170+
.mockRejectedValueOnce(new Error("failed push"))
1171+
.mockRejectedValueOnce(new Error("failed push"))
1172+
.mockRejectedValueOnce(new Error("failed push"))
1173+
.mockRejectedValueOnce(new Error("failed push"))
1174+
.mockResolvedValueOnce(undefined);
1175+
1176+
await createEntryService.pushEntryToFork(bcrForkRepo, bcrRepo, branchName);
1177+
1178+
expect(mockGitClient.push).toHaveBeenCalledTimes(5);
1179+
});
1180+
1181+
test("fails after the 5th retry", async () => {
1182+
const bcrRepo = CANONICAL_BCR;
1183+
const bcrForkRepo = new Repository("bazel-central-registry", "aspect");
1184+
const branchName = `repo/[email protected]`;
1185+
1186+
mockGitClient.push.mockRejectedValue(new Error("failed push"));
1187+
1188+
await expect(
1189+
createEntryService.pushEntryToFork(bcrForkRepo, bcrRepo, branchName)
1190+
).rejects.toThrow();
1191+
expect(mockGitClient.push).toHaveBeenCalledTimes(5);
1192+
});
11391193
});
11401194

11411195
function mockRulesetFiles(

src/domain/create-entry.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createTwoFilesPatch, parsePatch } from "diff";
2+
import { backOff } from "exponential-backoff";
23
import { randomBytes } from "node:crypto";
34
import fs, { readFileSync } from "node:fs";
45
import path from "node:path";
@@ -170,7 +171,13 @@ export class CreateEntryService {
170171
await this.gitClient.push(bcr.diskPath, "origin", branch);
171172
return;
172173
}
173-
await this.gitClient.push(bcr.diskPath, "authed-fork", branch);
174+
175+
await backOff(
176+
() => this.gitClient.push(bcr.diskPath, "authed-fork", branch),
177+
{
178+
numOfAttempts: 5,
179+
}
180+
);
174181
}
175182

176183
private addPatches(

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2408,6 +2408,11 @@ expect@^28.0.0, expect@^28.1.3:
24082408
jest-message-util "^28.1.3"
24092409
jest-util "^28.1.3"
24102410

2411+
2412+
version "3.1.1"
2413+
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
2414+
integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw==
2415+
24112416
express@^4.14.0, express@^4.16.4:
24122417
version "4.18.2"
24132418
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"

0 commit comments

Comments
 (0)