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')
|
2023-02-22 08:05:32 -06:00
|
|
|
const {
|
2023-02-22 08:26:42 -06:00
|
|
|
getSignedArtifactUrl,
|
|
|
|
|
createPagesDeployment,
|
|
|
|
|
getPagesDeploymentStatus,
|
|
|
|
|
cancelPagesDeployment
|
2023-02-22 08:05:32 -06:00
|
|
|
} = require('./api-client')
|
2021-12-13 23:03:09 -05:00
|
|
|
|
2022-01-26 16:49:02 -08:00
|
|
|
const errorStatus = {
|
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...'
|
2022-01-26 16:49:02 -08:00
|
|
|
}
|
|
|
|
|
|
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
|
2023-02-22 08:01:01 -06:00
|
|
|
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
|
2022-11-17 12:35:08 -06:00
|
|
|
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 {
|
|
|
|
|
core.info(`Actor: ${this.buildActor}`)
|
|
|
|
|
core.info(`Action ID: ${this.actionsId}`)
|
2023-02-22 08:01:01 -06:00
|
|
|
core.info(`Actions Workflow Run ID: ${this.workflowRun}`)
|
2023-02-22 08:05:32 -06:00
|
|
|
|
|
|
|
|
const artifactUrl = await getSignedArtifactUrl({
|
|
|
|
|
runtimeToken: this.runTimeToken,
|
|
|
|
|
workflowRunId: this.workflowRun,
|
|
|
|
|
artifactName: this.artifactName
|
2022-08-23 17:19:21 -07:00
|
|
|
})
|
2022-08-04 15:43:58 -07:00
|
|
|
|
2023-02-22 08:15:23 -06:00
|
|
|
const deployment = await createPagesDeployment({
|
|
|
|
|
githubToken: this.githubToken,
|
|
|
|
|
artifactUrl,
|
|
|
|
|
buildVersion: this.buildVersion,
|
|
|
|
|
idToken,
|
|
|
|
|
isPreview: this.isPreview
|
2022-08-23 17:19:21 -07:00
|
|
|
})
|
2023-02-22 08:15:23 -06:00
|
|
|
this.deploymentInfo = {
|
|
|
|
|
...deployment,
|
|
|
|
|
id: deployment?.['status_url']?.split('/')?.pop() || this.buildVersion,
|
|
|
|
|
pending: true
|
2022-08-23 17:19:21 -07:00
|
|
|
}
|
2023-02-22 08:15:23 -06:00
|
|
|
|
|
|
|
|
core.info(`Created deployment for ${this.buildVersion}, ID: ${this.deploymentInfo.id}`)
|
|
|
|
|
core.info(JSON.stringify(deployment))
|
|
|
|
|
|
|
|
|
|
return deployment
|
2022-08-23 17:19:21 -07:00
|
|
|
} catch (error) {
|
2023-02-22 08:15:23 -06:00
|
|
|
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.response.status}) with build version ${this.buildVersion}. `
|
|
|
|
|
if (error.response.status == 400) {
|
|
|
|
|
let message = ''
|
|
|
|
|
if (error.response.data && error.response.data.message) {
|
|
|
|
|
message = error.response.data.message
|
|
|
|
|
} else {
|
|
|
|
|
message = error.response.data
|
2022-06-28 16:08:07 -07:00
|
|
|
}
|
2022-08-23 17:19:21 -07:00
|
|
|
errorMessage += `Responded with: ${message}`
|
|
|
|
|
} else if (error.response.status == 403) {
|
2023-02-22 08:15:23 -06:00
|
|
|
errorMessage += 'Ensure GITHUB_TOKEN has permission "pages: write".'
|
2022-08-23 17:19:21 -07:00
|
|
|
} else if (error.response.status == 404) {
|
2022-11-17 12:35:08 -06:00
|
|
|
const pagesSettingsUrl = `${this.githubServerUrl}/${this.repositoryNwo}/settings/pages`
|
|
|
|
|
errorMessage += `Ensure GitHub Pages has been enabled: ${pagesSettingsUrl}`
|
2022-08-23 17:19:21 -07:00
|
|
|
} else if (error.response.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.'
|
2021-12-20 10:53:14 -08:00
|
|
|
}
|
2023-02-22 08:15:23 -06:00
|
|
|
throw new Error(errorMessage)
|
2022-08-23 17:19:21 -07:00
|
|
|
} else {
|
|
|
|
|
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() {
|
2023-02-22 14:05:35 -06:00
|
|
|
// Don't attempt to check status if no deployment was created
|
|
|
|
|
if (!this.deploymentInfo) {
|
|
|
|
|
core.setFailed(errorStatus.not_found)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (this.deploymentInfo.pending !== true) {
|
|
|
|
|
core.setFailed(errorStatus.unknown_status)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const deploymentId = this.deploymentInfo.id || this.buildVersion
|
2023-02-22 08:22:23 -06:00
|
|
|
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
|
|
|
|
|
|
2022-08-23 17:19:21 -07:00
|
|
|
try {
|
|
|
|
|
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
|
|
|
|
|
while (true) {
|
|
|
|
|
// Handle reporting interval
|
2023-02-22 08:23:39 -06:00
|
|
|
await new Promise(resolve => setTimeout(resolve, reportingInterval + errorReportingInterval))
|
2022-08-23 17:19:21 -07:00
|
|
|
|
|
|
|
|
// Check status
|
2023-02-22 08:22:23 -06:00
|
|
|
let res = await getPagesDeploymentStatus({
|
|
|
|
|
githubToken: this.githubToken,
|
|
|
|
|
deploymentId
|
2022-08-23 17:19:21 -07:00
|
|
|
})
|
2021-12-13 23:03:09 -05:00
|
|
|
|
2023-02-22 08:23:39 -06:00
|
|
|
if (res.data.status === 'succeed') {
|
2022-08-23 17:19:21 -07:00
|
|
|
core.info('Reported success!')
|
|
|
|
|
core.setOutput('status', 'succeed')
|
2023-02-22 14:05:35 -06:00
|
|
|
this.deploymentInfo.pending = false
|
2022-08-23 17:19:21 -07:00
|
|
|
break
|
2023-02-22 08:23:39 -06:00
|
|
|
} else if (res.data.status === 'deployment_failed') {
|
2022-08-23 17:19:21 -07:00
|
|
|
// Fall into permanent error, it may be caused by ongoing incident or malicious deployment content or exhausted automatic retry times.
|
|
|
|
|
core.setFailed('Deployment failed, try again later.')
|
2023-02-22 14:05:35 -06:00
|
|
|
this.deploymentInfo.pending = false
|
2022-08-23 17:19:21 -07:00
|
|
|
break
|
2023-02-22 08:23:39 -06:00
|
|
|
} else if (res.data.status === 'deployment_content_failed') {
|
2022-08-23 17:19:21 -07:00
|
|
|
// The uploaded artifact is invalid.
|
|
|
|
|
core.setFailed(
|
|
|
|
|
'Artifact could not be deployed. Please ensure the content does not contain any hard links, symlinks and total size is less than 10GB.'
|
|
|
|
|
)
|
2023-02-22 14:05:35 -06:00
|
|
|
this.deploymentInfo.pending = false
|
2022-08-23 17:19:21 -07:00
|
|
|
break
|
|
|
|
|
} else if (errorStatus[res.data.status]) {
|
|
|
|
|
// A temporary error happened, will query the status again
|
2023-02-22 08:23:39 -06:00
|
|
|
core.warning(errorStatus[res.data.status])
|
2022-08-23 17:19:21 -07:00
|
|
|
} else {
|
|
|
|
|
core.info('Current status: ' + res.data.status)
|
|
|
|
|
}
|
2022-01-26 16:49:02 -08:00
|
|
|
|
2023-02-22 08:23:39 -06:00
|
|
|
if (res.status !== 200 || !!errorStatus[res.data.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
|
|
|
} else {
|
|
|
|
|
// reset the error reporting interval once get the proper status back.
|
|
|
|
|
errorReportingInterval = 0
|
|
|
|
|
}
|
2022-03-28 12:32:59 -07:00
|
|
|
|
2022-08-23 17:19:21 -07:00
|
|
|
if (errorCount >= maxErrorCount) {
|
2023-02-22 08:23:39 -06:00
|
|
|
core.error('Too many errors, aborting!')
|
2022-08-23 17:19:21 -07:00
|
|
|
core.setFailed('Failed with status code: ' + res.status)
|
2022-08-23 21:32:40 -07:00
|
|
|
|
|
|
|
|
// Explicitly cancel the deployment
|
|
|
|
|
await this.cancel()
|
|
|
|
|
return
|
2021-12-13 23:03:09 -05:00
|
|
|
}
|
2022-08-23 17:19:21 -07:00
|
|
|
|
|
|
|
|
// Handle timeout
|
|
|
|
|
if (Date.now() - startTime >= timeout) {
|
2023-02-22 08:23:39 -06:00
|
|
|
core.error('Timeout reached, aborting!')
|
2022-08-23 17:19:21 -07:00
|
|
|
core.setFailed('Timeout reached, aborting!')
|
2022-08-23 21:32:40 -07:00
|
|
|
|
|
|
|
|
// Explicitly cancel the deployment
|
|
|
|
|
await this.cancel()
|
2022-08-23 17:19:21 -07:00
|
|
|
return
|
2021-12-20 10:13:46 -08:00
|
|
|
}
|
2021-12-13 23:03:09 -05:00
|
|
|
}
|
2022-08-23 17:19:21 -07:00
|
|
|
} 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
|
|
|
}
|
2021-12-13 23:03:09 -05:00
|
|
|
}
|
|
|
|
|
}
|
2022-08-23 21:32:40 -07:00
|
|
|
|
|
|
|
|
async cancel() {
|
2023-02-22 08:15:23 -06:00
|
|
|
// Don't attempt to cancel if no deployment was created
|
2023-02-22 14:05:35 -06:00
|
|
|
if (!this.deploymentInfo || this.deploymentInfo.pending !== true) {
|
2022-08-23 21:32:40 -07:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cancel the deployment
|
|
|
|
|
try {
|
2023-02-22 14:05:35 -06:00
|
|
|
const deploymentId = this.deploymentInfo.id || this.buildVersion
|
2023-02-22 08:23:03 -06:00
|
|
|
await cancelPagesDeployment({
|
|
|
|
|
githubToken: this.githubToken,
|
|
|
|
|
deploymentId
|
|
|
|
|
})
|
|
|
|
|
core.info(`Canceled deployment with ID ${deploymentId}`)
|
|
|
|
|
|
2023-02-22 14:05:35 -06:00
|
|
|
this.deploymentInfo.pending = false
|
2022-08-23 21:32:40 -07:00
|
|
|
} 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 21:32:40 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-08-23 17:19:21 -07:00
|
|
|
}
|
|
|
|
|
module.exports = { Deployment }
|