@@ -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+
2329jobs :
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 }}
0 commit comments