2021-12-13 23:03:09 -05:00
require ( 'regenerator-runtime/runtime' )
const core = require ( '@actions/core' )
const axios = require ( 'axios' )
// All variables we need from the runtime are loaded here
const getContext = require ( './context' )
2022-01-26 16:49:02 -08:00
const errorStatus = {
'unknown_status' : 'Unable to get deployment status.' ,
'not_found' : 'Deployment not found.' ,
'deployment_attempt_error' : 'Deployment temporarily failed, a retry will be automatically scheduled...'
}
2021-12-13 23:03:09 -05:00
class Deployment {
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 . workflowRun
this . githubToken = context . githubToken
this . workflowRun = context . workflowRun
this . requestedDeployment = false
2021-12-21 17:37:44 -08:00
this . deploymentInfo = null
2022-08-04 17:09:46 -07:00
this . githubApiUrl = context . githubApiUrl
this . artifactName = context . artifactName
2021-12-13 23:03:09 -05: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 } ` )
2022-08-04 15:43:58 -07:00
const pagesDeployEndpoint = ` ${ this . githubApiUrl } /repos/ ${ this . repositoryNwo } /pages/deployment `
2021-12-13 23:03:09 -05:00
const artifactExgUrl = ` ${ this . runTimeUrl } _apis/pipelines/workflows/ ${ this . workflowRun } /artifacts?api-version=6.0-preview `
core . info ( ` Artifact URL: ${ artifactExgUrl } ` )
const { data } = await axios . get ( artifactExgUrl , {
headers : {
Authorization : ` Bearer ${ this . runTimeToken } ` ,
'Content-Type' : 'application/json'
}
} )
core . info ( JSON . stringify ( data ) )
2022-08-04 17:11:26 -07:00
const artifactRawUrl = data ? . value ? . find ( artifact => artifact . name === this . artifactName ) ? . url
2022-08-04 15:43:58 -07:00
if ( ! artifactRawUrl ) {
throw new Error ( 'No uploaded artifact was found! Please check if there are any errors at build step, or uploaded artifact name is correct.' )
2021-12-13 23:03:09 -05:00
}
2022-08-04 15:43:58 -07:00
const artifactUrl = ` ${ artifactRawUrl } &%24expand=SignedContent `
2021-12-13 23:03:09 -05:00
const payload = {
artifact _url : artifactUrl ,
pages _build _version : this . buildVersion ,
oidc _token : idToken
}
core . info ( ` Creating deployment with payload: \n ${ JSON . stringify ( payload , null , '\t' ) } ` )
const response = await axios . post ( pagesDeployEndpoint , payload , {
headers : {
Accept : 'application/vnd.github.v3+json' ,
Authorization : ` Bearer ${ this . githubToken } ` ,
'Content-type' : 'application/json'
}
} )
this . requestedDeployment = true
core . info ( ` Created deployment for ${ this . buildVersion } ` )
2022-06-28 18:18:42 -07:00
if ( response && response . data ) {
core . info ( JSON . stringify ( response . data ) )
this . deploymentInfo = response . data
}
2021-12-13 23:03:09 -05:00
} catch ( error ) {
2022-06-28 17:38:14 -07:00
core . info ( error . stack )
2022-06-28 16:08:07 -07:00
2022-06-28 17:58:10 -07:00
// output raw error in debug mode.
core . debug ( JSON . stringify ( error ) )
2022-06-28 16:08:07 -07:00
// 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 ) {
2022-06-28 18:18:42 -07:00
let message = ""
if ( error . response . data && error . response . data . message ) {
message = error . response . data . message
} else {
message = error . response . data
}
errorMessage += ` Responded with: ${ message } `
2022-06-28 16:08:07 -07:00
}
else if ( error . response . status == 403 ) {
errorMessage += ` Ensure GITHUB_TOKEN has permission "pages: write". `
} else if ( error . response . status == 404 ) {
errorMessage += ` Ensure GitHub Pages has been enabled. `
}
else if ( error . response . status >= 500 ) {
2022-06-29 10:16:32 -07:00
errorMessage += ` Server error, is githubstatus.com reporting a Pages outage? Please re-run the deployment at a later time. `
2022-06-28 16:08:07 -07:00
}
throw errorMessage
} else {
throw error
2021-12-20 10:53:14 -08:00
}
2021-12-13 23:03:09 -05:00
}
}
// Poll the deployment endpoint for status
async check ( ) {
try {
2021-12-21 17:37:44 -08:00
const statusUrl = this . deploymentInfo != null ?
this . deploymentInfo [ "status_url" ] :
2022-08-04 15:43:58 -07:00
` ${ this . githubApiUrl } /repos/ ${ this . repositoryNwo } /pages/deployment/status/ ${ process . env [ 'GITHUB_SHA' ] } `
2022-01-12 13:07:46 -08:00
core . setOutput ( 'page_url' , this . deploymentInfo != null ? this . deploymentInfo [ "page_url" ] : "" )
2022-03-28 11:19:50 -07:00
const timeout = Number ( core . getInput ( 'timeout' ) )
2022-01-26 16:49:02 -08:00
const reportingInterval = Number ( core . getInput ( 'reporting_interval' ) )
2022-03-28 11:19:50 -07:00
const maxErrorCount = Number ( core . getInput ( 'error_count' ) )
2021-12-13 23:03:09 -05:00
var startTime = Date . now ( )
var errorCount = 0
2022-01-26 16:49:02 -08:00
// Time in milliseconds between two deployment status report when status errored, default 0.
var errorReportingInterval = 0
2021-12-13 23:03:09 -05:00
/*eslint no-constant-condition: ["error", { "checkLoops": false }]*/
while ( true ) {
// Handle reporting interval
2022-01-26 16:49:02 -08:00
await new Promise ( r => setTimeout ( r , reportingInterval + errorReportingInterval ) )
2021-12-13 23:03:09 -05:00
// Check status
var res = await axios . get ( statusUrl , {
headers : {
Authorization : ` token ${ this . githubToken } `
}
} )
if ( res . data . status == 'succeed' ) {
core . info ( 'Reported success!' )
core . setOutput ( 'status' , 'succeed' )
break
} else if ( res . data . status == 'deployment_failed' ) {
// Fall into permanent error, it may be caused by ongoing incident or malicious deployment content or exhausted automatic retry times.
2021-12-16 14:40:12 -08:00
core . setFailed ( 'Deployment failed, try again later.' )
2021-12-13 23:03:09 -05:00
break
2021-12-16 16:10:28 -08:00
} else if ( res . data . status == 'deployment_content_failed' ) {
// 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.' )
break
2022-01-26 16:49:02 -08:00
} else if ( errorStatus [ res . data . status ] ) {
// A temporary error happened, will query the status again
core . info ( errorStatus [ res . data . status ] )
2021-12-13 23:03:09 -05:00
} else {
core . info ( 'Current status: ' + res . data . status )
}
2022-01-26 16:49:02 -08:00
if ( res . status != 200 || ! ! errorStatus [ res . data . status ] ) {
2021-12-13 23:03:09 -05:00
errorCount ++
2022-01-26 16:49:02 -08:00
// set the Maximum error reporting interval greater than 15 sec but below 30 sec.
if ( errorReportingInterval < 1000 * 15 ) {
errorReportingInterval = errorReportingInterval << 1 | 1
}
} else {
// reset the error reporting interval once get the proper status back.
errorReportingInterval = 0
2021-12-13 23:03:09 -05:00
}
if ( errorCount >= maxErrorCount ) {
core . info ( 'Too many errors, aborting!' )
core . setFailed ( 'Failed with status code: ' + res . status )
break
}
2022-03-28 12:32:59 -07:00
// Handle timeout
if ( Date . now ( ) - startTime >= timeout ) {
core . info ( 'Timeout reached, aborting!' )
core . setFailed ( 'Timeout reached, aborting!' )
return
}
2021-12-13 23:03:09 -05:00
}
} catch ( error ) {
core . setFailed ( error )
2021-12-20 10:13:46 -08:00
if ( error . response && error . response . data ) {
2021-12-20 11:07:47 -08:00
core . info ( JSON . stringify ( error . response . data ) )
2021-12-20 10:13:46 -08:00
}
2021-12-13 23:03:09 -05:00
}
}
}
module . exports = { Deployment }