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
2023-03-03 15:42:55 -06: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...'
2022-01-26 16:49:02 -08:00
}
2023-03-03 15:42:55 -06:00
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
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 {
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 } ` )
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-03-09 08:19:19 -06:00
if ( deployment ) {
this . deploymentInfo = {
... deployment ,
2023-03-09 08:20:32 -06:00
id : deployment . id || deployment . status _url ? . split ( '/' ) ? . pop ( ) || this . buildVersion ,
2023-03-09 08:19:19 -06:00
pending : true
}
2022-08-23 17:19:21 -07:00
}
2023-02-22 08:15:23 -06:00
2023-03-09 08:19:19 -06: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 ) )
2023-02-22 08:15:23 -06:00
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 ) {
2023-03-08 19:43:11 -06:00
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 ) {
2023-02-22 08:15:23 -06:00
errorMessage += 'Ensure GITHUB_TOKEN has permission "pages: write".'
2023-03-08 19:43:11 -06:00
} else if ( error . 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 } `
2023-05-16 10:13:05 -05:00
// 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.'
2023-05-16 10:13:05 -05:00
}
2023-03-08 19:43:11 -06:00
} 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.'
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 {
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 ( ) {
2023-02-22 14:05:35 -06:00
// Don't attempt to check status if no deployment was created
if ( ! this . deploymentInfo ) {
2023-03-03 15:42:55 -06:00
core . setFailed ( temporaryErrorStatus . not _found )
2023-02-22 14:05:35 -06:00
return
}
if ( this . deploymentInfo . pending !== true ) {
2023-03-03 15:42:55 -06:00
core . setFailed ( temporaryErrorStatus . unknown _status )
2023-02-22 14:05:35 -06:00
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
2023-03-03 15:42:55 -06:00
let deployment = null
let errorStatus = 0
2023-02-22 08:22:23 -06:00
2023-03-03 15:42:55 -06:00
/*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
2023-03-03 15:42:55 -06:00
// Check status
try {
deployment = await getPagesDeploymentStatus ( {
2023-02-22 08:22:23 -06:00
githubToken : this . githubToken ,
deploymentId
2022-08-23 17:19:21 -07:00
} )
2021-12-13 23:03:09 -05:00
2023-03-03 15:42:55 -06:00
if ( deployment . 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-03-03 15:42:55 -06:00
} 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 ] )
2023-02-22 14:05:35 -06:00
this . deploymentInfo . pending = false
2022-08-23 17:19:21 -07:00
break
2023-03-03 15:42:55 -06:00
} else if ( temporaryErrorStatus [ deployment . status ] ) {
2022-08-23 17:19:21 -07:00
// A temporary error happened, will query the status again
2023-03-03 15:42:55 -06:00
core . warning ( temporaryErrorStatus [ deployment . status ] )
2022-08-23 17:19:21 -07:00
} else {
2023-03-03 15:42:55 -06:00
core . info ( 'Current status: ' + deployment . status )
2022-08-23 17:19:21 -07:00
}
2022-01-26 16:49:02 -08:00
2023-03-03 15:42:55 -06: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 ) {
2023-03-09 08:23:21 -06:00
errorStatus = error . status || error . response . status
2023-03-03 15:42:55 -06:00
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
}
2023-03-03 15:42:55 -06:00
}
2022-03-28 12:32:59 -07:00
2023-03-03 15:42:55 -06:00
if ( errorCount >= maxErrorCount ) {
core . error ( 'Too many errors, aborting!' )
core . setFailed ( 'Failed with status code: ' + errorStatus )
2022-08-23 21:32:40 -07:00
2023-03-03 15:42:55 -06:00
// Explicitly cancel the deployment
await this . cancel ( )
return
}
2022-08-23 17:19:21 -07:00
2023-03-03 15:42:55 -06:00
// Handle timeout
if ( Date . now ( ) - startTime >= timeout ) {
core . error ( 'Timeout reached, aborting!' )
core . setFailed ( 'Timeout reached, aborting!' )
2022-08-23 21:32:40 -07:00
2023-03-03 15:42:55 -06:00
// Explicitly cancel the deployment
await this . cancel ( )
return
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 ) {
2023-04-14 21:20:52 +01:00
core . debug ( 'No deployment to cancel' )
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
}
2023-03-03 15:42:55 -06:00
2022-08-23 17:19:21 -07:00
module . exports = { Deployment }