Files
deploy-pages/src/internal/deployment.js

235 lines
7.9 KiB
JavaScript
Raw Normal View History

2021-12-13 23:03:09 -05:00
const core = require('@actions/core')
// All variables we need from the runtime are loaded here
const getContext = require('./context')
const {
2023-02-22 08:26:42 -06:00
getSignedArtifactUrl,
createPagesDeployment,
getPagesDeploymentStatus,
cancelPagesDeployment
} = require('./api-client')
2021-12-13 23:03:09 -05:00
const temporaryErrorStatus = {
2022-08-23 17:19:21 -07:00
unknown_status: 'Unable to get deployment status.',
not_found: 'Deployment not found.',
deployment_attempt_error: 'Deployment temporarily failed, a retry will be automatically scheduled...'
}
const finalErrorStatus = {
deployment_failed: 'Deployment failed, try again later.',
deployment_content_failed:
'Artifact could not be deployed. Please ensure the content does not contain any hard links, symlinks and total size is less than 10GB.',
deployment_cancelled: 'Deployment cancelled.',
deployment_lost: 'Deployment failed to report final status.'
}
2021-12-13 23:03:09 -05:00
class Deployment {
2022-08-23 17:19:21 -07:00
constructor() {
const context = getContext()
this.runTimeUrl = context.runTimeUrl
this.repositoryNwo = context.repositoryNwo
this.runTimeToken = context.runTimeToken
this.buildVersion = context.buildVersion
this.buildActor = context.buildActor
this.actionsId = context.actionsId
2022-08-23 17:19:21 -07:00
this.githubToken = context.githubToken
this.workflowRun = context.workflowRun
this.deploymentInfo = null
this.githubApiUrl = context.githubApiUrl
this.githubServerUrl = context.githubServerUrl
2022-08-23 17:19:21 -07:00
this.artifactName = context.artifactName
2022-09-09 18:44:03 -05:00
this.isPreview = context.isPreview === true
2022-08-23 17:19:21 -07:00
}
2021-12-13 23:03:09 -05:00
2022-08-23 17:19:21 -07:00
// Ask the runtime for the unsigned artifact URL and deploy to GitHub Pages
// by creating a deployment with that artifact
async create(idToken) {
try {
2023-03-06 22:03:56 -06:00
core.debug(`Actor: ${this.buildActor}`)
core.debug(`Action ID: ${this.actionsId}`)
core.debug(`Actions Workflow Run ID: ${this.workflowRun}`)
const artifactUrl = await getSignedArtifactUrl({
runtimeToken: this.runTimeToken,
workflowRunId: this.workflowRun,
artifactName: this.artifactName
2022-08-23 17:19:21 -07:00
})
const deployment = await createPagesDeployment({
githubToken: this.githubToken,
artifactUrl,
buildVersion: this.buildVersion,
idToken,
isPreview: this.isPreview
2022-08-23 17:19:21 -07:00
})
if (deployment) {
this.deploymentInfo = {
...deployment,
id: deployment.id || deployment.status_url?.split('/')?.pop() || this.buildVersion,
pending: true
}
2022-08-23 17:19:21 -07:00
}
core.info(`Created deployment for ${this.buildVersion}, ID: ${this.deploymentInfo?.id}`)
2023-03-06 22:03:56 -06:00
core.debug(JSON.stringify(deployment))
return deployment
2022-08-23 17:19:21 -07:00
} catch (error) {
core.error(error.stack)
2022-08-23 17:19:21 -07:00
// output raw error in debug mode.
core.debug(JSON.stringify(error))
// build customized error message based on server response
if (error.response) {
let errorMessage = `Failed to create deployment (status: ${error.status}) with build version ${this.buildVersion}. `
if (error.status === 400) {
errorMessage += `Responded with: ${error.message}`
} else if (error.status === 403) {
errorMessage += 'Ensure GITHUB_TOKEN has permission "pages: write".'
} else if (error.status === 404) {
const pagesSettingsUrl = `${this.githubServerUrl}/${this.repositoryNwo}/settings/pages`
errorMessage += `Ensure GitHub Pages has been enabled: ${pagesSettingsUrl}`
// If using GHES, add a special note about compatibility
if (new URL(this.githubServerUrl).hostname.toLowerCase() !== 'github.com') {
errorMessage +=
2023-05-16 10:15:22 -05:00
'\nNote: This action version may not yet support GitHub Enterprise Server, please check the compatibility table.'
}
} else if (error.status >= 500) {
2023-02-22 08:26:42 -06:00
errorMessage +=
'Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time.'
}
throw new Error(errorMessage)
2022-08-23 17:19:21 -07:00
} else {
2023-04-14 20:58:13 +01:00
// istanbul ignore next
2022-08-23 17:19:21 -07:00
throw error
2021-12-13 23:03:09 -05:00
}
}
2022-08-23 17:19:21 -07:00
}
2021-12-13 23:03:09 -05:00
2022-08-23 17:19:21 -07:00
// Poll the deployment endpoint for status
async check() {
// Don't attempt to check status if no deployment was created
if (!this.deploymentInfo) {
core.setFailed(temporaryErrorStatus.not_found)
return
}
if (this.deploymentInfo.pending !== true) {
core.setFailed(temporaryErrorStatus.unknown_status)
return
}
const deploymentId = this.deploymentInfo.id || this.buildVersion
const timeout = Number(core.getInput('timeout'))
const reportingInterval = Number(core.getInput('reporting_interval'))
const maxErrorCount = Number(core.getInput('error_count'))
let startTime = Date.now()
let errorCount = 0
// Time in milliseconds between two deployment status report when status errored, default 0.
let errorReportingInterval = 0
let deployment = null
let errorStatus = 0
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
while (true) {
// Handle reporting interval
await new Promise(resolve => setTimeout(resolve, reportingInterval + errorReportingInterval))
2022-08-23 17:19:21 -07:00
// Check status
try {
deployment = await getPagesDeploymentStatus({
githubToken: this.githubToken,
deploymentId
2022-08-23 17:19:21 -07:00
})
2021-12-13 23:03:09 -05:00
if (deployment.status === 'succeed') {
2022-08-23 17:19:21 -07:00
core.info('Reported success!')
core.setOutput('status', 'succeed')
this.deploymentInfo.pending = false
2022-08-23 17:19:21 -07:00
break
} else if (finalErrorStatus[deployment.status]) {
// Fall into permanent error, it may be caused by ongoing incident, malicious deployment content, exhausted automatic retry times, invalid artifact, etc.
core.setFailed(finalErrorStatus[deployment.status])
this.deploymentInfo.pending = false
2022-08-23 17:19:21 -07:00
break
} else if (temporaryErrorStatus[deployment.status]) {
2022-08-23 17:19:21 -07:00
// A temporary error happened, will query the status again
core.warning(temporaryErrorStatus[deployment.status])
2022-08-23 17:19:21 -07:00
} else {
core.info('Current status: ' + deployment.status)
2022-08-23 17:19:21 -07:00
}
// reset the error reporting interval once get the proper status back.
errorReportingInterval = 0
} catch (error) {
core.error(error.stack)
// output raw error in debug mode.
core.debug(JSON.stringify(error))
// build customized error message based on server response
if (error.response) {
errorStatus = error.status || error.response.status
2022-08-23 17:19:21 -07:00
errorCount++
2021-12-13 23:03:09 -05:00
2023-02-22 08:23:39 -06:00
// set the maximum error reporting interval greater than 15 sec but below 30 sec.
2022-08-23 17:19:21 -07:00
if (errorReportingInterval < 1000 * 15) {
errorReportingInterval = (errorReportingInterval << 1) | 1
2021-12-13 23:03:09 -05:00
}
2022-08-23 17:19:21 -07:00
}
}
2022-03-28 12:32:59 -07:00
if (errorCount >= maxErrorCount) {
core.error('Too many errors, aborting!')
core.setFailed('Failed with status code: ' + errorStatus)
// Explicitly cancel the deployment
await this.cancel()
return
}
2022-08-23 17:19:21 -07:00
// Handle timeout
if (Date.now() - startTime >= timeout) {
core.error('Timeout reached, aborting!')
core.setFailed('Timeout reached, aborting!')
// Explicitly cancel the deployment
await this.cancel()
return
2022-08-23 17:19:21 -07:00
}
2021-12-13 23:03:09 -05:00
}
}
async cancel() {
// Don't attempt to cancel if no deployment was created
if (!this.deploymentInfo || this.deploymentInfo.pending !== true) {
core.debug('No deployment to cancel')
return
}
// Cancel the deployment
try {
const deploymentId = this.deploymentInfo.id || this.buildVersion
await cancelPagesDeployment({
githubToken: this.githubToken,
deploymentId
})
core.info(`Canceled deployment with ID ${deploymentId}`)
this.deploymentInfo.pending = false
} catch (error) {
core.setFailed(error)
2023-02-22 08:23:39 -06:00
if (error.response?.data) {
core.error(JSON.stringify(error.response.data))
}
}
}
2022-08-23 17:19:21 -07:00
}
2022-08-23 17:19:21 -07:00
module.exports = { Deployment }