Skip to content

Commit

Permalink
Report specific type of error when credentials can't be fetched
Browse files Browse the repository at this point in the history
This way, we can inform users what went wrong.

I've decided to try to stick to the error format we use in our internal
infrastructure as much as possible, without updating the JobError.
Because if that, this is accompanied by a small change to our API that
handles these errors.
  • Loading branch information
Jurre Stender committed Dec 17, 2021
1 parent 935d321 commit 28a99c0
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 33 deletions.
24 changes: 23 additions & 1 deletion __tests__/api_client.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as core from '@actions/core'
import {ApiClient} from '../src/api-client'
import {ApiClient, CredentialFetchingError} from '../src/api-client'

describe('ApiClient', () => {
const mockAxios: any = {
Expand Down Expand Up @@ -88,4 +88,26 @@ describe('ApiClient', () => {
expect(core.setSecret).toHaveBeenCalledWith('qux-password')
expect(core.setSecret).toHaveBeenCalledWith('qux-token')
})

test('job credentials errors', async () => {
const apiResponse = {
errors: [
{
status: 422,
title: 'Secret Not Found',
detail: 'MISSING_SECRET_NAME'
}
]
}

mockAxios.get.mockRejectedValue({
isAxiosError: true,
response: {status: 422, data: apiResponse}
})
await expect(api.getCredentials()).rejects.toThrowError(
new CredentialFetchingError(
'fetching credentials: received code 422: {"errors":[{"status":422,"title":"Secret Not Found","detail":"MISSING_SECRET_NAME"}]}'
)
)
})
})
50 changes: 36 additions & 14 deletions dist/main/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/main/index.js.map

Large diffs are not rendered by default.

42 changes: 27 additions & 15 deletions src/api-client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as core from '@actions/core'
import {AxiosInstance} from 'axios'
import axios, {AxiosInstance} from 'axios'
import {JobParameters} from './inputs'

// JobDetails are information about the repository and dependencies to be updated
Expand All @@ -26,6 +26,8 @@ export type Credential = {
token?: string
}

export class CredentialFetchingError extends Error {}

export class ApiClient {
constructor(
private readonly client: AxiosInstance,
Expand All @@ -52,24 +54,34 @@ export class ApiClient {

async getCredentials(): Promise<Credential[]> {
const credentialsURL = `/update_jobs/${this.params.jobId}/credentials`
const res: any = await this.client.get(credentialsURL, {
headers: {Authorization: this.params.credentialsToken}
})
if (res.status !== 200) {
throw new Error(`Unexpected status code: ${res.status}`)
}
try {
const res: any = await this.client.get(credentialsURL, {
headers: {Authorization: this.params.credentialsToken}
})

// Mask any secrets we've just retrieved from Actions logs
for (const credential of res.data.data.attributes.credentials) {
if (credential.password) {
core.setSecret(credential.password)
// Mask any secrets we've just retrieved from Actions logs
for (const credential of res.data.data.attributes.credentials) {
if (credential.password) {
core.setSecret(credential.password)
}
if (credential.token) {
core.setSecret(credential.token)
}
}
if (credential.token) {
core.setSecret(credential.token)

return res.data.data.attributes.credentials
} catch (error) {
if (axios.isAxiosError(error)) {
const err = error
throw new CredentialFetchingError(
`fetching credentials: received code ${
err.response?.status
}: ${JSON.stringify(err.response?.data)}`
)
} else {
throw error
}
}

return res.data.data.attributes.credentials
}

async reportJobError(error: JobError): Promise<void> {
Expand Down
10 changes: 8 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Context} from '@actions/github/lib/context'
import {getJobParameters} from './inputs'
import {ImageService} from './image-service'
import {Updater, UpdaterFetchError} from './updater'
import {ApiClient} from './api-client'
import {ApiClient, CredentialFetchingError} from './api-client'
import axios from 'axios'

export const UPDATER_IMAGE_NAME =
Expand Down Expand Up @@ -93,7 +93,13 @@ export async function run(context: Context): Promise<void> {
}
botSay('finished')
} catch (error) {
await failJob(apiClient, error)
if (error instanceof CredentialFetchingError) {
core.error('Error retrieving update job credentials')
await failJob(apiClient, error, DependabotErrorType.UpdateRun)
} else {
await failJob(apiClient, error)
}

return
}
} catch (error) {
Expand Down

0 comments on commit 28a99c0

Please sign in to comment.