diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts index 998b887..e243544 100644 --- a/__tests__/main.test.ts +++ b/__tests__/main.test.ts @@ -15,7 +15,19 @@ jest.mock('../src/updater') describe('run', () => { let context: Context + let failJobSpy: any + let markJobAsProcessedSpy: any + let reportJobErrorSpy: any + beforeEach(async () => { + failJobSpy = jest.spyOn(APIClient.prototype, 'failJob') + + markJobAsProcessedSpy = jest.spyOn( + APIClient.prototype, + 'markJobAsProcessed' + ) + reportJobErrorSpy = jest.spyOn(APIClient.prototype, 'reportJobError') + jest.spyOn(core, 'info').mockImplementation(jest.fn()) jest.spyOn(core, 'setFailed').mockImplementation(jest.fn()) }) @@ -39,6 +51,14 @@ describe('run', () => { expect.stringContaining('🤖 ~fin~') ) }) + + test('it defers reporting back to dependabot-api to the updater itself', async () => { + await run(context) + + expect(markJobAsProcessedSpy).not.toHaveBeenCalled() + expect(failJobSpy).not.toHaveBeenCalled() + expect(reportJobErrorSpy).not.toHaveBeenCalled() + }) }) describe('when the action is triggered on an unsupported event', () => { @@ -48,16 +68,20 @@ describe('run', () => { context = new Context() }) - test('it explains the event is unsupported without logging to dependabot-api', async () => { + test('it fails the workflow', async () => { await run(context) expect(core.setFailed).not.toHaveBeenCalled() expect(core.info).toHaveBeenCalledWith( - expect.stringContaining( - "Dependabot Updater Action does not support 'issue_created' events." - ) + "Dependabot Updater Action does not support 'issue_created' events." ) }) + + test('it does not report this failed run to dependabot-api', async () => { + await run(context) + + expect(failJobSpy).not.toHaveBeenCalled() + }) }) describe('when there is an error retrieving job parameters', () => { @@ -73,13 +97,19 @@ describe('run', () => { context = new Context() }) - test('it relays an error to dependabot-api and marks the job as processed', async () => { + test('it fails the workflow with the raw error', async () => { await run(context) expect(core.setFailed).toHaveBeenCalledWith( - expect.stringContaining('unexpected error retrieving job params') + new Error('unexpected error retrieving job params') ) }) + + test('it does not inform dependabot-api as it cannot instantiate a client without the params', async () => { + await run(context) + + expect(failJobSpy).not.toHaveBeenCalled() + }) }) describe('when there is an error retrieving job details from DependabotAPI', () => { @@ -97,13 +127,21 @@ describe('run', () => { context = new Context() }) - test('it relays an error to dependabot-api and marks the job as processed', async () => { + test('it fails the workflow', async () => { await run(context) expect(core.setFailed).toHaveBeenCalledWith( expect.stringContaining('error getting job details') ) }) + + test('it relays a failure message to the dependabot service', async () => { + await run(context) + + expect(failJobSpy).toHaveBeenCalledWith( + new Error('error getting job details') + ) + }) }) describe('when there is an error retrieving job credentials from DependabotAPI', () => { @@ -121,17 +159,25 @@ describe('run', () => { context = new Context() }) - test('it relays an error to dependabot-api and marks the job as processed', async () => { + test('it fails the workflow', async () => { await run(context) expect(core.setFailed).toHaveBeenCalledWith( expect.stringContaining('error getting credentials') ) }) + + test('it relays a failure message to the dependabot service', async () => { + await run(context) + + expect(failJobSpy).toHaveBeenCalledWith( + new Error('error getting credentials') + ) + }) }) describe('when there is an error running the update', () => { - beforeAll(() => { + beforeEach(() => { jest .spyOn(Updater.prototype, 'runUpdater') .mockImplementationOnce( @@ -145,12 +191,20 @@ describe('run', () => { context = new Context() }) - test('it relays an error to dependabot-api and marks the job as processed', async () => { + test('it fails the workflow', async () => { await run(context) expect(core.setFailed).toHaveBeenCalledWith( expect.stringContaining('error running the update') ) }) + + test('it relays a failure message to the dependabot service', async () => { + await run(context) + + expect(failJobSpy).toHaveBeenCalledWith( + new Error('error running the update') + ) + }) }) }) diff --git a/src/api-client.ts b/src/api-client.ts index fdcdf07..3a4bea9 100644 --- a/src/api-client.ts +++ b/src/api-client.ts @@ -26,7 +26,7 @@ export type JobDetails = { } export enum JobErrorType { - Unknown = 'actions_runner_unknown' + Unknown = 'actions_workflow_unknown' } export type JobError = { diff --git a/src/main.ts b/src/main.ts index 26e6ed8..b605334 100644 --- a/src/main.ts +++ b/src/main.ts @@ -27,21 +27,33 @@ export async function run(context: Context): Promise { const client = axios.create({baseURL: params.dependabotAPIURL}) const apiClient = new APIClient(client, params) - const details = await apiClient.getJobDetails() - const credentials = await apiClient.getCredentials() - const updater = new Updater( - UPDATER_IMAGE_NAME, - PROXY_IMAGE_NAME, - apiClient, - details, - credentials - ) - await ImageService.pull(UPDATER_IMAGE_NAME) - await ImageService.pull(PROXY_IMAGE_NAME) - - await updater.runUpdater() - core.info('🤖 ~fin~') + + try { + const details = await apiClient.getJobDetails() + const credentials = await apiClient.getCredentials() + const updater = new Updater( + UPDATER_IMAGE_NAME, + PROXY_IMAGE_NAME, + apiClient, + details, + credentials + ) + await ImageService.pull(UPDATER_IMAGE_NAME) + await ImageService.pull(PROXY_IMAGE_NAME) + + await updater.runUpdater() + core.info('🤖 ~fin~') + } catch (error) { + // Update Dependabot API on the job failure + apiClient.failJob(error) + core.setFailed(error.message) + } } catch (error) { + // If we've reached this point, we do not have a viable + // API client to report back to Dependabot API. + // + // We output the raw error in the Action logs and defer + // to workflow_run monitoring to detect the job failure. core.setFailed(error) } }