forked from github/docs
-
Notifications
You must be signed in to change notification settings - Fork 3
151 lines (131 loc) · 5.61 KB
/
staging-undeploy-pr.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
name: Staging - Undeploy PR
# **What it does**: To undeploy PRs from a Heroku staging environment, i.e. destroy the Heroku App.
# **Why we have it**: To save money spent on deployments for closed PRs.
# **Who does it impact**: All contributors.
on:
pull_request_target:
types:
- closed
permissions:
contents: read
deployments: write
pull-requests: write
# This prevents one Undeploy workflow run from interrupting another
concurrency:
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label }}'
cancel-in-progress: false
jobs:
debug:
runs-on: ubuntu-latest
steps:
- name: Dump full context for debugging
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
run: echo "$GITHUB_CONTEXT"
cancel-jobs-before-undeploy:
if: ${{ github.repository == 'github/docs-internal' || github.repository == 'github/docs' }}
runs-on: ubuntu-latest
# This interrupts Build and Deploy workflow runs in progress for this PR
# branch. However, it does so with an intentionally short, independent job
# so that the following `undeploy` job cannot be cancelled once started!
concurrency:
group: 'PR Staging @ ${{ github.event.pull_request.head.label }}'
cancel-in-progress: true
steps:
- name: Cancelling other deployments via concurrency configuration
run: |
echo 'Cancelling other deployment runs (if any)...'
undeploy:
needs: cancel-jobs-before-undeploy
if: ${{ github.repository == 'github/docs-internal' || github.repository == 'github/docs' }}
runs-on: ubuntu-latest
timeout-minutes: 5
# IMPORTANT: Intentionally OMIT a `concurrency` configuration from this job!
steps:
- name: Add a label to the PR to block deployment during undeployment
uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
with:
script: |
const { owner, repo } = context.repo
await github.issues.addLabels({
owner,
repo,
issue_number: process.env.PR_NUMBER,
labels: ['automated-block-deploy']
})
- name: Check out repo's default branch
uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f
with:
# For enhanced security: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
persist-credentials: 'false'
- name: Setup node
uses: actions/setup-node@38d90ce44d5275ad62cc48384b3d8a58c500bb5f
with:
node-version: 16.8.x
cache: npm
- name: Install dependencies
run: npm ci
- name: Install one-off development-only dependencies
run: npm install --no-save --include=optional esm
- name: Undeploy
uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEROKU_API_TOKEN: ${{ secrets.HEROKU_API_TOKEN }}
with:
script: |
const { GITHUB_TOKEN, HEROKU_API_TOKEN } = process.env
// Exit if GitHub Actions PAT is not found
if (!GITHUB_TOKEN) {
throw new Error('You must supply a GITHUB_TOKEN environment variable!')
}
// Exit if Heroku API token is not found
if (!HEROKU_API_TOKEN) {
throw new Error('You must supply a HEROKU_API_TOKEN environment variable!')
}
// Workaround to allow us to load ESM files with `require(...)`
const esm = require('esm')
require = esm({})
const { default: getOctokit } = require('./script/helpers/github')
const { default: undeployFromStaging } = require('./script/deployment/undeploy-from-staging')
// This helper uses the `GITHUB_TOKEN` implicitly!
// We're using our usual version of Octokit vs. the provided `github`
// instance to avoid versioning discrepancies.
const octokit = getOctokit()
try {
await undeployFromStaging({
octokit,
pullRequest: context.payload.pull_request,
runId: context.runId
})
} catch (error) {
console.error(`Failed to undeploy from staging: ${error.message}`)
console.error(error)
throw error
}
- if: ${{ always() }}
name: Remove the label from the PR to unblock deployment
uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
with:
script: |
const { owner, repo } = context.repo
await github.issues.removeLabel({
owner,
repo,
issue_number: process.env.PR_NUMBER,
name: 'automated-block-deploy'
})
- name: Send Slack notification if workflow failed
uses: someimportantcompany/github-actions-slack-message@0b470c14b39da4260ed9e3f9a4f1298a74ccdefd
if: ${{ failure() }}
with:
channel: ${{ secrets.DOCS_STAGING_DEPLOYMENT_FAILURES_SLACK_CHANNEL_ID }}
bot-token: ${{ secrets.SLACK_DOCS_BOT_TOKEN }}
color: failure
text: Staging undeployment failed for PR ${{ github.event.pull_request.html_url }} at commit ${{ github.head_sha }}. See https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}.