Wrap Twirp responses like Octokit responses for consistency

This commit is contained in:
James M. Greene
2023-12-22 11:40:57 -06:00
parent c704b8a6e2
commit 72ab98158a
4 changed files with 88 additions and 20 deletions

9
package-lock.json generated
View File

@@ -11,7 +11,9 @@
"dependencies": { "dependencies": {
"@actions/artifact": "^2.0.0", "@actions/artifact": "^2.0.0",
"@actions/core": "^1.10.1", "@actions/core": "^1.10.1",
"@actions/github": "^6.0.0" "@actions/github": "^6.0.0",
"@octokit/request-error": "^5.0.1",
"http-status-messages": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@vercel/ncc": "^0.38.1", "@vercel/ncc": "^0.38.1",
@@ -4670,6 +4672,11 @@
"integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
"dev": true "dev": true
}, },
"node_modules/http-status-messages": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/http-status-messages/-/http-status-messages-1.1.0.tgz",
"integrity": "sha512-zq9mKkNX6w6qYtNE0aAiH+urvEenUUIyoq8eAWQh2prhA5o03NETCOm/D2GIVt/qCFItt+23Sm1E7HyelPvi6w=="
},
"node_modules/human-signals": { "node_modules/human-signals": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",

View File

@@ -6,7 +6,9 @@
"dependencies": { "dependencies": {
"@actions/artifact": "^2.0.0", "@actions/artifact": "^2.0.0",
"@actions/core": "^1.10.1", "@actions/core": "^1.10.1",
"@actions/github": "^6.0.0" "@actions/github": "^6.0.0",
"@octokit/request-error": "^5.0.1",
"http-status-messages": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {
"@vercel/ncc": "^0.38.1", "@vercel/ncc": "^0.38.1",

View File

@@ -1,16 +1,80 @@
const core = require('@actions/core') const core = require('@actions/core')
const github = require('@actions/github') const github = require('@actions/github')
const { DefaultArtifactClient } = require('@actions/artifact') const { DefaultArtifactClient } = require('@actions/artifact')
const { RequestError } = require('@octokit/request-error')
const HttpStatusMessages = require('http-status-messages')
function wrapTwirpResponseLikeOctokit(twirpResponse, requestOptions) {
// Specific response shape aligned with Octokit
const response = {
url: requestOptions.url,
status: 200,
headers: {
...requestOptions.headers
},
data: twirpResponse
}
return response
}
// Mimic the errors thrown by Octokit for consistency.
function wrapTwirpErrorLikeOctokit(twirpError, requestOptions) {
const rawErrorMsg = twirpError?.message || twirpError?.toString() || ''
const statusCodeMatch = rawErrorMsg.match(/Failed request: \((?<statusCode>\d+)\)/)
const statusCode = statusCodeMatch?.groups?.statusCode ?? 500
// Try to provide the best error message
const errorMsg =
rawErrorMsg ||
// Fallback to the HTTP status message based on the status code
HttpStatusMessages[statusCode] ||
// Or if the status code is unexpected...
`Unknown error (${statusCode})`
// RequestError is an Octokit-specific class
return new RequestError(errorMsg, statusCode, {
response: {
url: requestOptions.url,
status: statusCode,
headers: {
...requestOptions.headers
},
data: rawErrorMsg ? { message: rawErrorMsg } : ''
},
request: requestOptions
})
}
function getArtifactsServiceOrigin() {
const resultsUrl = process.env.ACTIONS_RESULTS_URL
return resultsUrl ? new URL(resultsUrl).origin : ''
}
async function getArtifactMetadata({ artifactName }) { async function getArtifactMetadata({ artifactName }) {
const artifactClient = new DefaultArtifactClient() const artifactClient = new DefaultArtifactClient()
// Primarily for debugging purposes, accuracy is not critical
const requestOptions = {
method: 'POST',
url: `${getArtifactsServiceOrigin()}/twirp/github.actions.results.api.v1.ArtifactService/ListArtifacts`,
headers: {
'content-type': 'application/json'
}
}
try { try {
core.info(`Fetching artifact metadata for ${artifactName} in this workflow run`) core.info(`Fetching artifact metadata for ${artifactName} in this workflow run`)
const response = await artifactClient.listArtifacts() let response
try {
const twirpResponse = await artifactClient.listArtifacts()
response = wrapTwirpResponseLikeOctokit(twirpResponse, requestOptions)
} catch (twirpError) {
const octokitError = wrapTwirpErrorLikeOctokit(twirpError, requestOptions)
throw octokitError
}
const filteredArtifacts = response.artifacts.filter(artifact => artifact.name === artifactName) const filteredArtifacts = response.data.artifacts.filter(artifact => artifact.name === artifactName)
const artifactCount = filteredArtifacts.length const artifactCount = filteredArtifacts.length
core.debug(`List artifact count: ${artifactCount}`) core.debug(`List artifact count: ${artifactCount}`)

View File

@@ -62,20 +62,15 @@ class Deployment {
core.debug(`Action ID: ${this.actionsId}`) core.debug(`Action ID: ${this.actionsId}`)
core.debug(`Actions Workflow Run ID: ${this.workflowRun}`) core.debug(`Actions Workflow Run ID: ${this.workflowRun}`)
let artifactData
try { try {
artifactData = await getArtifactMetadata({ artifactName: this.artifactName }) const artifactData = await getArtifactMetadata({ artifactName: this.artifactName })
} catch (error) {
throw new Error(`Failed to create deployment: ${error.message}`)
}
if (artifactData?.size > ONE_GIGABYTE) { if (artifactData?.size > ONE_GIGABYTE) {
core.warning( core.warning(
`Uploaded artifact size of ${artifactData?.size} bytes exceeds the allowed size of ${SIZE_LIMIT_DESCRIPTION}. Deployment might fail.` `Uploaded artifact size of ${artifactData?.size} bytes exceeds the allowed size of ${SIZE_LIMIT_DESCRIPTION}. Deployment might fail.`
) )
} }
try {
const deployment = await createPagesDeployment({ const deployment = await createPagesDeployment({
githubToken: this.githubToken, githubToken: this.githubToken,
artifactId: artifactData.id, artifactId: artifactData.id,
@@ -103,14 +98,14 @@ class Deployment {
// build customized error message based on server response // build customized error message based on server response
if (error.response) { if (error.response) {
let errorMessage = `Failed to create deployment (status: ${error.status}) with build version ${this.buildVersion}. ` let errorMessage = `Failed to create deployment (status: ${error.status}) with build version ${this.buildVersion}.`
if (error.status === 400) { if (error.status === 400) {
errorMessage += `Responded with: ${error.message}` errorMessage += ` Responded with: ${error.message}`
} else if (error.status === 403) { } else if (error.status === 403) {
errorMessage += 'Ensure GITHUB_TOKEN has permission "pages: write".' errorMessage += ' Ensure GITHUB_TOKEN has permission "pages: write".'
} else if (error.status === 404) { } else if (error.status === 404) {
const pagesSettingsUrl = `${this.githubServerUrl}/${this.repositoryNwo}/settings/pages` const pagesSettingsUrl = `${this.githubServerUrl}/${this.repositoryNwo}/settings/pages`
errorMessage += `Ensure GitHub Pages has been enabled: ${pagesSettingsUrl}` errorMessage += ` Ensure GitHub Pages has been enabled: ${pagesSettingsUrl}`
// If using GHES, add a special note about compatibility // If using GHES, add a special note about compatibility
if (new URL(this.githubServerUrl).hostname.toLowerCase() !== 'github.com') { if (new URL(this.githubServerUrl).hostname.toLowerCase() !== 'github.com') {
errorMessage += errorMessage +=
@@ -118,7 +113,7 @@ class Deployment {
} }
} else if (error.status >= 500) { } else if (error.status >= 500) {
errorMessage += errorMessage +=
'Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.' ' Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.'
} }
throw new Error(errorMessage) throw new Error(errorMessage)
} else { } else {