1
0
mirror of synced 2025-12-21 10:57:10 -05:00
Files
docs/script/deployment/undeploy-from-staging.js
James M. Greene 50fbcc62d3 Deploy/undeploy PRs to/from staging via an Actions workflow (#19865)
* Add 'script/deploy' to enable manual deploys to Heroku

* Pass API tokens into 'deploy-to-staging' module usage

* Construct Octokit instance to pass in

* Get PR branch name and verify state

* Reorganize

* Rename option to 'octokit'

* Add missing option

* Actually use the convenience methods for convenience

* Simplify top-level script

* Top-level script revisions

* Add parse-pr-url module

* Add create-staging-app-name module

* Remove misplaced comment

* Pass in owner

* Use owner param

* More variables

* Pass owner along more

* Correct prNumber param reference

* Add WIP deploy-to-staging module

* Prevent 'scripts/' and '.github/actions-scripts/' files from being modified in open source repo

* Extract PR author earlier

* Add note about optionally supplying DOCUBOT_REPO_PAT env var

* Override Heroku env var during AppSetup creation instead of later to avoid triggering a second deploy

* Updates to deploy-to-staging module

* Lots of updates

* Add dyno start-up monitoring and warmup requests

* Ignore 'script/deploy' in the repository-references test

* Correct path to Octokit helper

* Temporarily add a 'gha-' prefix to environment names

* Log whole error if terminal. Good for Octokit errors!

* Correct Octokit preview configuration

* Add more logging around Heroku build and release

* Added more timings to log messages

* Monitor dyno states specifically from the dyno list view to avoid 404 oddities when Free dynos are dropped and non-Free dynos are added

* Don't wait for AppSetup status as it includes the Build time

* Updating logging since we don't see DeploymentStatus update messages in the UI =(

* Refactor to extract more properties from the PR object

* Add a workflow to deploy PRs to Staging

* Fix workflow description

* Add skeleton workflow and module for undeploying

* Remove commented out code

* Update undeployment module

* Add '--destroy' flag to 'script/deploy' options

* Add timeout and concurrency key for undeployment

* Add timeout and concurrency key for deployment

* Remove dangling unneeded function declaration

* Add ant-man preview for inactive deployment state setting

* Fix reference to pull request number

* Fix reference to pull request number

* Refactor to extract more properties from the PR object

* Fix reference to pull request number

* Remove workflow

* Add workflow to undeploy closed PRs from staging

* Add repository filters to jobs

* Update to using actions/github-script@4.0.2

* Pass more environment variables that affect deployments

* Add explicit .js extensions to local require in Actions workflow

* Check out the code and install deps first, of course

* Try local requires without the explicit .js extension

* Use our usual version of Octokit instead of the provided 'github' instance to avoid versioning discrepancies

* Explicitly pass in GITHUB_TOKEN to github-script

* Point to the workflow run page as a default log_url

* Exclude staging deployment workflows from the workflow linter until we can get support for the 'concurrency' key (https://github.com/cschleiden/actions-linter/issues/79)

* Remove 'gha-' prefix
2021-06-17 18:02:02 +00:00

106 lines
3.3 KiB
JavaScript

const Heroku = require('heroku-client')
const createStagingAppName = require('./create-staging-app-name')
module.exports = async function undeployFromStaging ({
herokuToken,
octokit,
pullRequest,
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: herokuToken })
// 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
}
}