Skip to content

Commit 72cf983

Browse files
authored
Handle edge case with deleted config (#249)
1 parent 705d875 commit 72cf983

8 files changed

+65
-13
lines changed

src/services/github/v4/apiRequestV4.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ describe('apiRequestV4', () => {
6262
},
6363
})
6464
).rejects.toThrowError(
65-
new HandledError(`some error, some other error (Github v4)`)
65+
new HandledError(
66+
`some error, some other error (Unhandled Github v4 error)`
67+
)
6668
);
6769
});
6870
});

src/services/github/v4/apiRequestV4.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import axios from 'axios';
2-
import { AxiosError } from 'axios';
1+
import axios, { AxiosError } from 'axios';
32
import { HandledError } from '../../HandledError';
43
import { logger } from '../../logger';
54

@@ -71,15 +70,17 @@ export async function apiRequestV4<DataResponse>({
7170
}
7271
}
7372

74-
export function handleGithubV4Error(e: AxiosError<GithubV4Response<null>>) {
75-
// not github api error
73+
export function handleGithubV4Error(e: AxiosError<GithubV4Response<unknown>>) {
74+
// re-throw unknown error
7675
if (!e.response?.data) {
7776
return e;
7877
}
7978

8079
const errorMessages = e.response.data.errors?.map((error) => error.message);
8180
if (errorMessages) {
82-
return new HandledError(`${errorMessages.join(', ')} (Github v4)`);
81+
return new HandledError(
82+
`${errorMessages.join(', ')} (Unhandled Github v4 error)`
83+
);
8384
}
8485

8586
return new HandledError(

src/services/github/v4/enablePullRequestAutoMerge.private.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ const TEST_REPO_OWNER = 'backport-org';
1111
const TEST_REPO_NAME = 'repo-with-auto-merge-enabled';
1212
const TEST_PULL_NUMBER = 1;
1313

14+
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
15+
1416
let options: ValidConfigOptions;
1517

1618
describe('enablePullRequestAutoMerge', () => {
@@ -35,6 +37,10 @@ describe('enablePullRequestAutoMerge', () => {
3537
{ ...options, autoMergeMethod: 'merge' },
3638
TEST_PULL_NUMBER
3739
);
40+
41+
// ensure Github API reflects the change before querying
42+
await sleep(100);
43+
3844
const autoMergeMethod = await fetchPullRequestAutoMergeMethod(
3945
options,
4046
TEST_PULL_NUMBER
@@ -47,6 +53,10 @@ describe('enablePullRequestAutoMerge', () => {
4753
{ ...options, autoMergeMethod: 'rebase' },
4854
TEST_PULL_NUMBER
4955
);
56+
57+
// ensure Github API reflects the change before querying
58+
await sleep(100);
59+
5060
const autoMergeMethod = await fetchPullRequestAutoMergeMethod(
5161
options,
5262
TEST_PULL_NUMBER
@@ -59,6 +69,10 @@ describe('enablePullRequestAutoMerge', () => {
5969
{ ...options, autoMergeMethod: 'squash' },
6070
TEST_PULL_NUMBER
6171
);
72+
73+
// ensure Github API reflects the change before querying
74+
await sleep(100);
75+
6276
const autoMergeMethod = await fetchPullRequestAutoMergeMethod(
6377
options,
6478
TEST_PULL_NUMBER

src/services/github/v4/fetchCommitByPullNumber.private.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ describe('fetchCommitByPullNumber', () => {
6161
} as ValidConfigOptions & { pullNumber: number };
6262

6363
await expect(fetchCommitByPullNumber(options)).rejects.toThrowError(
64-
`Could not resolve to a PullRequest with the number of 9999999999999. (Github v4)`
64+
`Could not resolve to a PullRequest with the number of 9999999999999. (Unhandled Github v4 error)`
6565
);
6666
});
6767
});

src/services/github/v4/getOptionsFromGithub/index.private.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,23 @@ describe('getOptionsFromGithub', () => {
142142
});
143143
});
144144
});
145+
146+
describe('when a backportrc.json config was deleted from repo', () => {
147+
it('does not throw', async () => {
148+
const optionsFromConfigFiles = {
149+
username: 'sqren',
150+
accessToken: devAccessToken,
151+
githubApiBaseUrlV4: 'https://api.github.com/graphql',
152+
upstream: 'backport-org/repo-with-backportrc-removed',
153+
} as OptionsFromConfigFiles;
154+
const optionsFromCliArgs = {} as OptionsFromCliArgs;
155+
156+
const options = await getOptionsFromGithub(
157+
optionsFromConfigFiles,
158+
optionsFromCliArgs
159+
);
160+
161+
expect(options).toEqual({ sourceBranch: 'main' });
162+
});
163+
});
145164
});

src/services/github/v4/getOptionsFromGithub/index.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,30 @@ export async function getOptionsFromGithub(
5151
spinner.stop();
5252
} catch (e) {
5353
spinner.stop();
54-
const error = e as AxiosError<GithubV4Response<null>>;
54+
const error = e as AxiosError<
55+
GithubV4Response<GithubConfigOptionsResponse | null>
56+
>;
5557

5658
throwOnInvalidAccessToken({
5759
error,
5860
repoName,
5961
repoOwner,
6062
});
6163

62-
throw handleGithubV4Error(error);
64+
const configFileNotFound = error.response?.data.errors?.some(
65+
(error) =>
66+
error.type === 'NOT_FOUND' &&
67+
error.path.join('.') ===
68+
'repository.defaultBranchRef.target.jsonConfigFile.edges.0.config.file'
69+
);
70+
71+
if (configFileNotFound && error.response?.data.data) {
72+
// swallow error if caused by missing config file on Github
73+
res = error.response.data.data;
74+
} else {
75+
// throw generic Github error
76+
throw handleGithubV4Error(error);
77+
}
6378
}
6479

6580
// get the original repo (not the fork)

src/services/github/v4/throwOnInvalidAccessToken.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ describe('throwOnInvalidAccessToken', () => {
5050
'x-accepted-oauth-scopes': 'a,b,c',
5151
},
5252
data: {
53-
errors: [{ type: 'NOT_FOUND' }],
53+
errors: [{ type: 'NOT_FOUND', path: ['repository'] }],
5454
},
5555
},
5656
} as any;
@@ -73,7 +73,7 @@ describe('throwOnInvalidAccessToken', () => {
7373
'x-accepted-oauth-scopes': 'a,b,c',
7474
},
7575
data: {
76-
errors: [{ type: 'NOT_FOUND' }],
76+
errors: [{ type: 'NOT_FOUND', path: ['repository'] }],
7777
},
7878
},
7979
} as any;

src/services/github/v4/throwOnInvalidAccessToken.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function throwOnInvalidAccessToken({
1212
}: {
1313
repoOwner: string;
1414
repoName: string;
15-
error: AxiosError<GithubV4Response<null>>;
15+
error: AxiosError<GithubV4Response<unknown>>;
1616
}) {
1717
type MaybeString = string | undefined;
1818

@@ -28,7 +28,8 @@ export function throwOnInvalidAccessToken({
2828
switch (statusCode) {
2929
case 200: {
3030
const repoNotFound = error.response?.data.errors?.some(
31-
(error) => error.type === 'NOT_FOUND'
31+
(error) =>
32+
error.type === 'NOT_FOUND' && error.path.join('.') === 'repository'
3233
);
3334

3435
const grantedScopes = error.response?.headers['x-oauth-scopes'] || '';

0 commit comments

Comments
 (0)