From bd62d0d0d03cf74e361d2246d7817ffdb1423d0d Mon Sep 17 00:00:00 2001 From: HenryNguyen5 <6404866+HenryNguyen5@users.noreply.github.com> Date: Wed, 11 Sep 2024 11:09:06 -0700 Subject: [PATCH] Add better error handling (#14395) --- .github/scripts/jira/axios.ts | 97 +++++++++++++++++++ .../scripts/jira/create-jira-traceability.ts | 26 +++-- .github/scripts/jira/enforce-jira-issue.ts | 4 +- .github/scripts/jira/lib.ts | 18 +++- .github/scripts/jira/package.json | 3 + .github/scripts/jira/pnpm-lock.yaml | 11 ++- .github/scripts/jira/tsconfig.json | 2 +- 7 files changed, 145 insertions(+), 16 deletions(-) create mode 100644 .github/scripts/jira/axios.ts diff --git a/.github/scripts/jira/axios.ts b/.github/scripts/jira/axios.ts new file mode 100644 index 00000000000..3a912797713 --- /dev/null +++ b/.github/scripts/jira/axios.ts @@ -0,0 +1,97 @@ +import { + AxiosRequestConfig, + AxiosResponse, + AxiosError, + InternalAxiosRequestConfig, +} from "axios"; +import { Readable } from "stream"; + +interface AxiosErrorFormat { + config: Pick; + code?: string; + response: Partial, (typeof RESPONSE_KEYS)[number]>>; + isAxiosError: boolean; +} + +interface AxiosErrorFormatError + extends Error, + AxiosErrorFormat {} + +export function formatAxiosError( + origErr: AxiosError +): AxiosErrorFormatError { + const { message, name, stack, code, config, response, isAxiosError } = + origErr; + + const err: AxiosErrorFormatError = { + ...new Error(message), + name, + stack, + code, + isAxiosError, + config: {}, + response: {}, + }; + + for (const k of CONFIG_KEYS) { + if (config?.[k] === undefined) { + continue; + } + + err.config[k] = formatValue(config[k], k); + } + + for (const k of RESPONSE_KEYS) { + if (response?.[k] === undefined) { + continue; + } + + err.response[k] = formatValue(response[k], k); + } + + return err as any; +} + +const CONFIG_KEYS: (keyof InternalAxiosRequestConfig)[] = [ + "url", + "method", + "baseURL", + "params", + "data", + "timeout", + "timeoutErrorMessage", + "withCredentials", + "auth", + "responseType", + "xsrfCookieName", + "xsrfHeaderName", + "maxContentLength", + "maxBodyLength", + "maxRedirects", + "socketPath", + "proxy", + "decompress", +] as const; + +const RESPONSE_KEYS: (keyof AxiosResponse)[] = [ + "data", + "status", + "statusText", +] as const; + +function formatValue( + value: any, + key: (typeof CONFIG_KEYS)[number] | (typeof RESPONSE_KEYS)[number] +): any { + if (key !== "data") { + return value; + } + + if (process.env.BROWSER !== "true") { + if (value instanceof Readable) { + return "[Readable]"; + } + } + + return value; +} diff --git a/.github/scripts/jira/create-jira-traceability.ts b/.github/scripts/jira/create-jira-traceability.ts index b151c9d5eab..cda038a7cc9 100644 --- a/.github/scripts/jira/create-jira-traceability.ts +++ b/.github/scripts/jira/create-jira-traceability.ts @@ -5,6 +5,7 @@ import { generateIssueLabel, generateJiraIssuesLink, getJiraEnvVars, + handleError, } from "./lib"; import * as core from "@actions/core"; @@ -191,15 +192,24 @@ async function main() { const client = createJiraClient(); const label = generateIssueLabel(product, baseRef, headRef); - await addTraceabillityToJiraIssues( - client, - jiraIssueNumbers, - label, - artifactUrl - ); + try { + await addTraceabillityToJiraIssues( + client, + jiraIssueNumbers, + label, + artifactUrl + ); + } catch (e) { + handleError(e); - const { jiraHost } = getJiraEnvVars() - core.summary.addLink("Jira Issues", generateJiraIssuesLink(`${jiraHost}/issues/`, label)); + process.exit(1); + } + + const { jiraHost } = getJiraEnvVars(); + core.summary.addLink( + "Jira Issues", + generateJiraIssuesLink(`${jiraHost}/issues/`, label) + ); core.summary.write(); } main(); diff --git a/.github/scripts/jira/enforce-jira-issue.ts b/.github/scripts/jira/enforce-jira-issue.ts index 9d8c6e490ee..153f397e021 100644 --- a/.github/scripts/jira/enforce-jira-issue.ts +++ b/.github/scripts/jira/enforce-jira-issue.ts @@ -1,6 +1,6 @@ import * as core from "@actions/core"; import jira from "jira.js"; -import { createJiraClient, getGitTopLevel, parseIssueNumberFrom } from "./lib"; +import { createJiraClient, getGitTopLevel, handleError, parseIssueNumberFrom } from "./lib"; import { promises as fs } from "fs"; import { join } from "path"; @@ -36,7 +36,7 @@ async function doesIssueExist( return true; } catch (e) { - core.debug(e as any); + handleError(e) return false; } } diff --git a/.github/scripts/jira/lib.ts b/.github/scripts/jira/lib.ts index 0d0983f5c3e..8be295ab14f 100644 --- a/.github/scripts/jira/lib.ts +++ b/.github/scripts/jira/lib.ts @@ -4,7 +4,8 @@ import * as jira from "jira.js"; import { exec } from "child_process"; import { promisify } from "util"; import { join } from "path"; - +import { isAxiosError } from "axios"; +import { formatAxiosError } from "./axios"; export function generateJiraIssuesLink(baseUrl: string, label: string) { // https://smartcontract-it.atlassian.net/issues/?jql=labels%20%3D%20%22review-artifacts-automation-base%3A8d818ea265ff08887e61ace4f83364a3ee149ef0-head%3A3c45b71f3610de28f429cef0163936eaa448e63c%22 const jqlQuery = `labels = "${label}"`; @@ -129,3 +130,18 @@ export function createJiraClient() { }, }); } + +export function handleError(e: unknown) { + if (e instanceof Error) { + if (isAxiosError(e)) { + core.error(formatAxiosError(e)); + } else if (isAxiosError(e.cause)) { + core.error(formatAxiosError(e.cause)); + } else { + core.error(e); + } + } else { + core.error(JSON.stringify(e)); + } + core.setFailed("Error adding traceability to Jira issues"); +} diff --git a/.github/scripts/jira/package.json b/.github/scripts/jira/package.json index 94a805314af..6081e489818 100644 --- a/.github/scripts/jira/package.json +++ b/.github/scripts/jira/package.json @@ -27,5 +27,8 @@ "@types/node": "^20.14.10", "typescript": "^5.5.3", "vitest": "^2.0.3" + }, + "peerDependencies": { + "axios": "^1.7.7" } } diff --git a/.github/scripts/jira/pnpm-lock.yaml b/.github/scripts/jira/pnpm-lock.yaml index 4deeef7f339..a52fa9dd0c8 100644 --- a/.github/scripts/jira/pnpm-lock.yaml +++ b/.github/scripts/jira/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@actions/core': specifier: ^1.10.1 version: 1.10.1 + axios: + specifier: ^1.7.7 + version: 1.7.7 jira.js: specifier: ^4.0.1 version: 4.0.1 @@ -311,8 +314,8 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.7.2: - resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} + axios@1.7.7: + resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} @@ -811,7 +814,7 @@ snapshots: asynckit@0.4.0: {} - axios@1.7.2: + axios@1.7.7: dependencies: follow-redirects: 1.15.6 form-data: 4.0.0 @@ -918,7 +921,7 @@ snapshots: jira.js@4.0.1: dependencies: - axios: 1.7.2 + axios: 1.7.7 form-data: 4.0.0 tslib: 2.6.3 transitivePeerDependencies: diff --git a/.github/scripts/jira/tsconfig.json b/.github/scripts/jira/tsconfig.json index 746f76774b0..3e3216e6e05 100644 --- a/.github/scripts/jira/tsconfig.json +++ b/.github/scripts/jira/tsconfig.json @@ -11,7 +11,7 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "ES2020", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "ES2022", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */