Skip to content

Commit 05b1033

Browse files
committed
fix: fix and improve error messaging
1 parent 655a3e8 commit 05b1033

File tree

2 files changed

+66
-15
lines changed

2 files changed

+66
-15
lines changed

src/github-repo.spec.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ describe('GithubRepo', () => {
323323
let deleteReleaseAsset: sinon.SinonStub;
324324

325325
before(function() {
326-
process.env.TEST_UPLOAD_RELEASE_ASSET_TIMEOUT = '1000';
326+
process.env.TEST_UPLOAD_RELEASE_ASSET_TIMEOUT = '500';
327327
});
328328

329329
after(function() {
@@ -457,6 +457,33 @@ describe('GithubRepo', () => {
457457
expect(githubRepo._uploadAsset).to.have.been.calledTwice;
458458
});
459459

460+
it('eventually fails if oktokit continues to fail', async function() {
461+
const release = {
462+
name: 'release',
463+
tag: 'v0.8.0',
464+
notes: '',
465+
};
466+
467+
getReleaseByTag.resolves({});
468+
469+
githubRepo._uploadAsset = sinon
470+
.stub()
471+
.rejects({ message: 'request failed', status: 500 });
472+
473+
try {
474+
await githubRepo.uploadReleaseAsset(release.tag, {
475+
path: '/foo/bar/upload.zip',
476+
contentType: 'xyz',
477+
});
478+
expect.fail('Expected uploadReleaseAsset to throw');
479+
} catch (err) {
480+
expect(err)
481+
.to.have.property('message')
482+
.match(/Failed to upload asset upload.zip after \d+ attempts/)
483+
.match(/Octokit request failed/);
484+
}
485+
});
486+
460487
it('fails if no release can be found', async() => {
461488
const release = {
462489
name: 'release',

src/github-repo.ts

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,32 +73,37 @@ async function waitFor<T>(
7373
fn: (...args: unknown[]) => Promise<T>,
7474
timeout = 60_000,
7575
interval = 100,
76-
message: string | ((err: any) => string) = 'Timeout exceeded for task',
76+
message:
77+
| string
78+
| ((err: any, attempts: number) => string) = 'Timeout exceeded for task',
7779
signal?: AbortSignal
7880
): Promise<T> {
7981
let lastError: any;
82+
let attempts = 0;
8083
const controller = new AbortController();
8184
// eslint-disable-next-line chai-friendly/no-unused-expressions
8285
signal?.addEventListener('abort', () => {
8386
controller.abort(signal.reason);
8487
});
8588
const tid = setTimeout(() => {
8689
controller.abort(
87-
signal?.reason ??
88-
new Error(typeof message === 'function' ? message(lastError) : message)
90+
new Error(
91+
typeof message === 'function' ? message(lastError, attempts) : message
92+
)
8993
);
9094
}, timeout);
9195
try {
9296
while (!controller.signal.aborted) {
9397
try {
98+
attempts++;
9499
return await fn();
95100
} catch (err) {
96101
lastError = err;
97102
await setTimeoutAsync(interval);
98103
}
99104
}
100105
// If we ended up here either timeout expired or passed signal was aborted,
101-
// either way this inter
106+
// either way this internal controller was aborted with a correct reason
102107
throw controller.signal.reason;
103108
} finally {
104109
clearTimeout(tid);
@@ -285,13 +290,16 @@ export class GithubRepo {
285290
},
286291
process.env.TEST_UPLOAD_RELEASE_ASSET_TIMEOUT
287292
? Number(process.env.TEST_UPLOAD_RELEASE_ASSET_TIMEOUT)
288-
// File upload is slow, we allow 5 minutes per file to allow for additional retries
289-
: 60_000 * 5,
293+
// File upload is slow, we allow 10 minutes per file to allow for additional retries
294+
: 60_000 * 10,
290295
100,
291-
(lastError) => {
292-
return `Failed to upload asset ${asset.name}` + lastError
293-
? `\n\nLast encountered error:\n\n${lastError?.stack}`
294-
: '';
296+
(lastError, attempts) => {
297+
return (
298+
`Failed to upload asset ${path.basename(asset.path)} after ${attempts} attempts` +
299+
(lastError
300+
? `\n\nLast encountered error:\n\n${this._getErrorMessage(lastError)}`
301+
: '')
302+
);
295303
},
296304
controller.signal
297305
);
@@ -325,10 +333,13 @@ export class GithubRepo {
325333
? Number(process.env.TEST_GET_RELEASE_TIMEOUT)
326334
: 60_000,
327335
100,
328-
(lastError) => {
329-
return "Can't fetch releases from GitHub" + lastError
330-
? `\n\nLast encountered error:\n\n${lastError?.stack}`
331-
: '';
336+
(lastError, attempts) => {
337+
return (
338+
`Failed to fetch releases from GitHub after ${attempts} attempts` +
339+
(lastError
340+
? `\n\nLast encountered error:\n\n${this._getErrorMessage(lastError)}`
341+
: '')
342+
);
332343
},
333344
controller.signal
334345
);
@@ -502,4 +513,17 @@ export class GithubRepo {
502513
error.errors[0].code === 'already_exists'
503514
);
504515
}
516+
517+
private _getErrorMessage(_err: any): string {
518+
if (_err.status) {
519+
return (
520+
(_err as OctokitRequestError).errors
521+
?.map((err) => err.message ?? null)
522+
.filter((msg): msg is string => !!msg)
523+
.join('\n\n')
524+
.trim() || `Octokit request failed with ${_err.name} (${_err.status})`
525+
);
526+
}
527+
return _err.stack ?? _err.message;
528+
}
505529
}

0 commit comments

Comments
 (0)