* Add a Staging build workflow * Remove all commented out code from build workflow It will be handled in https://github.com/github/docs-engineering/issues/726 * Use pinned version of upload-artifact action * Tweaks to build * Minor deployment script refactoring * Update the Staging deployment workflow * Missed refactoring tweak * Add relevant comments * Update Heroku app naming convention for Actions deploy to include 'gha-' prefix * Update Heroku app ConfigVars and SourceBlob for optional prebuilt app * Remove obsolete 'dist/' dir from PR build artifact See https://github.com/github/docs-internal/pull/20405 * Ensure a new enough version of npm is used * Switch to creating a tarball for upload * Remove obsolete 'layouts' dir from file list * Ditch the verbosity for 'tar'... too many files * Add tarball support to deploy * Add esm workaround to deploy script See https://github.com/actions/github-script/issues/168 * Temporarily ignore staging deploy workflow from workflow linter * Update deployment to use a Heroku Build Source instead of a GitHub Actions Artifact * Update undeploy workflow to use ESM workaround See https://github.com/actions/github-script/issues/168 * Add 'esm' package to optionalDependencies to better support workaround See https://github.com/actions/github-script/issues/168 * Add Slack notifications for workflow failures * Wrap AppSetup polling in try-catch * Improve dyno monitoring * Rename 'script/deploy' to have a .js extension #esm * Update script references to include the extension * Use non-deprecated Sources API for Heroku * Use normal quotes * Stub in a step to mark deployment inactive after timing out * Apply suggestions from code review Co-authored-by: Rachael Sewell <rachmari@github.com> Co-authored-by: Rachael Sewell <rachmari@github.com>
111 lines
3.4 KiB
JavaScript
111 lines
3.4 KiB
JavaScript
#!/usr/bin/env node
|
|
import Heroku from 'heroku-client'
|
|
import createStagingAppName from './create-staging-app-name.js'
|
|
|
|
export default async function undeployFromStaging({
|
|
octokit,
|
|
pullRequest,
|
|
// These parameters will only be set by Actions
|
|
runId = null,
|
|
}) {
|
|
// Start a timer so we can report how long the deployment takes
|
|
const startTime = Date.now()
|
|
|
|
// Extract some important properties from the PR
|
|
const {
|
|
number: pullNumber,
|
|
base: {
|
|
repo: {
|
|
name: repo,
|
|
owner: { login: owner },
|
|
},
|
|
},
|
|
head: { ref: branch },
|
|
} = pullRequest
|
|
|
|
const workflowRunLog = runId ? `https://github.com/${owner}/${repo}/actions/runs/${runId}` : null
|
|
const logUrl = workflowRunLog
|
|
|
|
const appName = createStagingAppName({ repo, pullNumber, branch })
|
|
|
|
try {
|
|
const title = `from the 'staging' environment as '${appName}'`
|
|
|
|
console.log(`About to undeploy ${title}...`)
|
|
|
|
// Time to talk to Heroku...
|
|
const heroku = new Heroku({ token: process.env.HEROKU_API_TOKEN })
|
|
|
|
// Is there already a Heroku App for this PR?
|
|
let appExists = true
|
|
try {
|
|
await heroku.get(`/apps/${appName}`)
|
|
} catch (error) {
|
|
appExists = false
|
|
}
|
|
|
|
// If there is an existing app, delete it
|
|
if (appExists) {
|
|
try {
|
|
await heroku.delete(`/apps/${appName}`)
|
|
|
|
console.log(`Heroku app '${appName}' deleted`)
|
|
} catch (error) {
|
|
throw new Error(`Failed to delete Heroku app '${appName}'. Error: ${error}`)
|
|
}
|
|
}
|
|
|
|
// Get the latest deployment environment to signal its deactivation
|
|
const { data: deployments } = await octokit.repos.listDeployments({
|
|
owner,
|
|
repo,
|
|
|
|
// In the GitHub API, there can only be one active deployment per environment.
|
|
// For our many staging apps, we must use the unique appName as the environment.
|
|
environment: appName,
|
|
})
|
|
|
|
if (deployments.length === 0) {
|
|
console.log('🚀 No deployments to deactivate!')
|
|
console.log(
|
|
`Finished undeploying after ${Math.round((Date.now() - startTime) / 1000)} seconds`
|
|
)
|
|
return
|
|
}
|
|
|
|
console.log(`Found ${deployments.length} GitHub Deployments`, deployments)
|
|
|
|
// Deactivate ALL of the deployments
|
|
for (const deployment of deployments) {
|
|
const { data: deploymentStatus } = await octokit.repos.createDeploymentStatus({
|
|
owner,
|
|
repo,
|
|
deployment_id: deployment.id,
|
|
state: 'inactive',
|
|
description: 'The app was undeployed',
|
|
...(logUrl && { log_url: logUrl }),
|
|
// The 'ant-man' preview is required for `state` values of 'inactive', as well as
|
|
// the use of the `log_url`, `environment_url`, and `auto_inactive` parameters.
|
|
// The 'flash' preview is required for `state` values of 'in_progress' and 'queued'.
|
|
mediaType: {
|
|
previews: ['ant-man', 'flash'],
|
|
},
|
|
})
|
|
console.log(
|
|
`🚀 Deployment status (ID: ${deployment.id}): ${deploymentStatus.state} - ${deploymentStatus.description}`
|
|
)
|
|
}
|
|
|
|
console.log(`Finished undeploying after ${Math.round((Date.now() - startTime) / 1000)} seconds`)
|
|
} catch (error) {
|
|
// Report failure!
|
|
const failureMessage = `Undeployment failed after ${Math.round(
|
|
(Date.now() - startTime) / 1000
|
|
)} seconds. See logs for more information.`
|
|
console.error(failureMessage)
|
|
|
|
// Re-throw the error to bubble up
|
|
throw error
|
|
}
|
|
}
|