Skip to content

Commit 0746c88

Browse files
authored
Merge pull request #27806 from github/brannon-staging-deploy-2
Add initial staging deployment workflow.
2 parents 7de540f + 240f750 commit 0746c88

File tree

2 files changed

+167
-2
lines changed

2 files changed

+167
-2
lines changed

.github/workflows/azure-staging-build-deploy.yml

Lines changed: 134 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,144 @@ permissions:
2020
contents: read
2121
deployments: write
2222

23+
# This allows a subsequently queued workflow run to take priority over
24+
# previously queued runs but NOT interrupt currently executing runs
25+
concurrency:
26+
group: 'staging-env @ ${{ github.head_ref || github.run_id }} for ${{ github.event.number || github.event.inputs.PR_NUMBER }}'
27+
cancel-in-progress: true
28+
2329
jobs:
2430
azure-staging-build-and-deploy:
2531
if: ${{ github.repository == 'github/docs-internal' }}
2632
runs-on: ubuntu-latest
33+
timeout-minutes: 20
34+
environment:
35+
# TODO: Update name and url to point to a specific slot for the branch/PR
36+
name: staging-env
37+
url: ${{ env.APP_URL }}
38+
env:
39+
PR_NUMBER: ${{ github.event.number || github.event.inputs.PR_NUMBER || github.run_id }}
40+
COMMIT_REF: ${{ github.event.pull_request.head.sha || github.event.inputs.COMMIT_REF }}
41+
IMAGE_REPO: ${{ github.repository }}/pr-${{ github.event.number || github.event.inputs.PR_NUMBER || github.run_id }}
42+
RESOURCE_GROUP_NAME: docs-staging
43+
APP_SERVICE_NAME: ghdocs-staging
44+
SLOT_NAME: canary
2745

2846
steps:
29-
- name: 'No-op'
47+
- name: 'Az CLI login'
48+
uses: azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
49+
with:
50+
creds: ${{ secrets.PROD_AZURE_CREDENTIALS }}
51+
52+
- name: 'Docker login'
53+
uses: azure/docker-login@81744f9799e7eaa418697cb168452a2882ae844a
54+
with:
55+
login-server: ${{ secrets.NONPROD_REGISTRY_SERVER }}
56+
username: ${{ secrets.NONPROD_REGISTRY_USERNAME }}
57+
password: ${{ secrets.NONPROD_REGISTRY_PASSWORD }}
58+
59+
- name: Set up Docker Buildx
60+
uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25
61+
62+
- name: Check out repo
63+
uses: actions/checkout@dcd71f646680f2efd8db4afa5ad64fdcba30e748
64+
with:
65+
ref: ${{ env.COMMIT_REF }}
66+
# To prevent issues with cloning early access content later
67+
persist-credentials: 'false'
68+
lfs: 'true'
69+
70+
- name: Check out LFS objects
71+
run: git lfs checkout
72+
73+
- name: 'Set env vars'
74+
run: |
75+
# Set APP_URL
76+
echo "APP_URL=${{ secrets.STAGING_APP_URL }}" >> $GITHUB_ENV
77+
# Image tag is unique to each workflow run so that it always triggers a new deployment
78+
echo "DOCKER_IMAGE=${{ secrets.NONPROD_REGISTRY_SERVER }}/${{ env.IMAGE_REPO }}:${{ env.COMMIT_REF }}-${{ github.run_number }}-${{ github.run_attempt }}" >> $GITHUB_ENV
79+
80+
- name: Setup node
81+
uses: actions/setup-node@1f8c6b94b26d0feae1e387ca63ccbdc44d27b561
82+
with:
83+
node-version: 16.15.x
84+
cache: npm
85+
86+
- name: Clone docs-early-access
87+
uses: actions/checkout@dcd71f646680f2efd8db4afa5ad64fdcba30e748
88+
with:
89+
repository: github/docs-early-access
90+
token: ${{ secrets.DOCUBOT_REPO_PAT }}
91+
path: docs-early-access
92+
ref: main
93+
94+
- name: Merge docs-early-access repo's folders
95+
run: .github/actions-scripts/merge-early-access.sh
96+
97+
- name: 'Build and push image'
98+
uses: docker/build-push-action@7f9d37fa544684fb73bfe4835ed7214c255ce02b
99+
with:
100+
context: .
101+
push: true
102+
target: production
103+
tags: ${{ env.DOCKER_IMAGE }}
104+
build-args: |
105+
BUILD_SHA=${{ env.COMMIT_REF }}
106+
107+
- name: 'Update docker-compose.staging.yaml template file'
108+
run: |
109+
sed 's|#{IMAGE}#|${{ env.DOCKER_IMAGE }}|g' docker-compose.staging.tmpl.yaml > docker-compose.staging.yaml
110+
111+
- name: 'Apply updated docker-compose.staging.yaml config to deployment slot'
112+
run: |
113+
az webapp config container set --multicontainer-config-type COMPOSE --multicontainer-config-file docker-compose.staging.yaml --slot ${{ env.SLOT_NAME }} -n ${{ env.APP_SERVICE_NAME }} -g ${{ env.RESOURCE_GROUP_NAME }}
114+
115+
# Watch deployment slot instances to see when all the instances are ready
116+
- name: Check that deployment slot is ready
117+
uses: actions/github-script@2b34a689ec86a68d8ab9478298f91d5401337b7d
118+
env:
119+
CHECK_INTERVAL: 10000
120+
with:
121+
script: |
122+
const { execSync } = require('child_process')
123+
124+
const slotName = process.env.SLOT_NAME
125+
const appServiceName = process.env.APP_SERVICE_NAME
126+
const resourceGroupName = process.env.RESOURCE_GROUP_NAME
127+
128+
const getStatesForSlot = (slot, appService, resourceGroup) => {
129+
return JSON.parse(
130+
execSync(
131+
`az webapp list-instances --slot ${slot} --query "[].state" -n ${appService} -g ${resourceGroup}`,
132+
{ encoding: 'utf8' }
133+
)
134+
)
135+
}
136+
137+
let hasStopped = false
138+
const waitDuration = parseInt(process.env.CHECK_INTERVAL, 10) || 10000
139+
async function doCheck() {
140+
const states = getStatesForSlot(slotName, appServiceName, resourceGroupName)
141+
console.log(`Instance states:`, states)
142+
143+
// We must wait until at-least 1 instance has STOPPED to know we're looking at the "next" deployment and not the "previous" one
144+
// That way we don't immediately succeed just because all the previous instances were READY
145+
if (!hasStopped) {
146+
hasStopped = states.some((s) => s === 'STOPPED')
147+
}
148+
149+
const isAllReady = states.every((s) => s === 'READY')
150+
151+
if (hasStopped && isAllReady) {
152+
process.exit(0) // success
153+
}
154+
155+
console.log(`checking again in ${waitDuration}ms`)
156+
setTimeout(doCheck, waitDuration)
157+
}
158+
159+
doCheck()
160+
161+
- name: 'Swap deployment slot to production'
30162
run: |
31-
echo "No-op"
163+
az webapp deployment slot swap --slot ${{ env.SLOT_NAME }} --target-slot production -n ${{ env.APP_SERVICE_NAME }} -g ${{ env.RESOURCE_GROUP_NAME }}

docker-compose.staging.tmpl.yaml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
version: '3.7'
2+
3+
services:
4+
ghdocs-staging:
5+
image: '#{IMAGE}#'
6+
ports:
7+
- '4000:4000'
8+
environment:
9+
NODE_ENV: ${NODE_ENV}
10+
NODE_OPTIONS: ${NODE_OPTIONS}
11+
DD_API_KEY: ${DD_API_KEY}
12+
COOKIE_SECRET: ${COOKIE_SECRET}
13+
HYDRO_ENDPOINT: ${HYDRO_ENDPOINT}
14+
HYDRO_SECRET: ${HYDRO_SECRET}
15+
HAYSTACK_URL: ${HAYSTACK_URL}
16+
HEROKU_APP_NAME: ${HEROKU_APP_NAME}
17+
ENABLED_LANGUAGES: ${ENABLED_LANGUAGES}
18+
DEPLOYMENT_ENV: ${DEPLOYMENT_ENV}
19+
HEROKU_PRODUCTION_APP: true
20+
PORT: 4000
21+
DD_AGENT_HOST: datadog-agent
22+
depends_on:
23+
- datadog-agent
24+
restart: always
25+
26+
datadog-agent:
27+
image: datadog/dogstatsd:7.32.4
28+
ports:
29+
- '8125:8125'
30+
environment:
31+
DD_API_KEY: ${DD_API_KEY}
32+
DD_AGENT_HOST: datadog-agent
33+
DD_HISTOGRAM_PERCENTILES: 0.99 0.95 0.50

0 commit comments

Comments
 (0)