Workflow for staging PR deployments on azure (#24039)

* add workflow for staging PR deployments on azure

* add workflow to clean up azure staging resources
This commit is contained in:
Mike Surowiec 2022-01-24 11:59:56 -06:00 коммит произвёл GitHub
Родитель a5af5e4665
Коммит 6c8911eea0
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
4 изменённых файлов: 280 добавлений и 1 удалений

118
.github/workflows/staging-build-and-deploy-azure.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,118 @@
name: Staging - Azure Deploy App Service
# **What it does**: Build and deploy staging PRs to Azure
# **Why we have it**: It's our new staging deployment mechanism, only applicable to docs-internal
# **Who does it impact**: All contributors.
# This whole workflow is only guaranteed to be secure in the *private
# repo* and because we repo-sync these files over the to the public one,
# IT'S IMPORTANT THAT THIS WORKFLOW IS ONLY ENABLED IN docs-internal!
on:
# The advantage of 'pull_request' over 'pull_request_target' is that we
# can make changes to this file and test them in a pull request, instead
# of relying on landing it in 'main' first.
# From a security point of view, its arguably safer this way because
# unlike 'pull_request_target', these only have secrets if the pull
# request creator has permission to access secrets.
pull_request:
workflow_dispatch:
permissions:
contents: read
deployments: write
# This allows one deploy workflow to interrupt another
concurrency:
group: '${{ github.workflow }} @ ${{ github.event.pull_request.head.label }}'
cancel-in-progress: true
jobs:
build-and-deploy:
if: ${{ github.repository == 'github/docs-internal' }}
name: Build and deploy image to staging App Service
runs-on: ubuntu-latest
timeout-minutes: 15
environment:
name: staging-pr-${{ github.event.number }}
url: ${{ steps.deploy.outputs.defaultHostName }}
env:
GITHUB_EVENT_NUMBER: ${{ github.event.number }}
NONPROD_REGISTRY_USERNAME: ghdocs
# Image tag is unique to each workflow run so that it always triggers a new deployment
DOCKER_IMAGE: ${{ secrets.NONPROD_REGISTRY_SERVER }}/${{ github.repository }}/pr-${{ github.event.number }}:${{ github.event.pull_request.head.sha }}-${{ github.run_number }}-${{ github.run_attempt }}
steps:
- name: 'Set env vars'
id: vars
run: |
REPO_NAME=${GITHUB_REPOSITORY#*\/}
echo "REPO_NAME=${REPO_NAME}" >> $GITHUB_ENV
echo "APP_NAME=gh${REPO_NAME}-staging-${GITHUB_EVENT_NUMBER}" >> $GITHUB_ENV
- name: 'Az CLI login'
uses: azure/login@66d2e78565ab7af265d2b627085bc34c73ce6abb
with:
creds: ${{ secrets.NONPROD_AZURE_CREDENTIALS }}
- name: 'Docker login'
uses: azure/docker-login@81744f9799e7eaa418697cb168452a2882ae844a
with:
login-server: ${{ secrets.NONPROD_REGISTRY_SERVER }}
username: ${{ env.NONPROD_REGISTRY_USERNAME }}
password: ${{ secrets.NONPROD_REGISTRY_PASSWORD }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25
- name: Check out repo
uses: actions/checkout@1e204e9a9253d643386038d443f96446fa156a97
with:
ref: ${{ github.event.pull_request.head.sha }}
# To prevent issues with cloning early access content later
persist-credentials: 'false'
lfs: 'true'
- name: Check out LFS objects
run: git lfs checkout
- name: Setup node
uses: actions/setup-node@04c56d2f954f1e4c69436aa54cfef261a018f458
with:
node-version: 16.13.x
cache: npm
- if: ${{ github.repository == 'github/docs-internal' }}
name: Clone early access
env:
DOCUBOT_REPO_PAT: ${{ secrets.DOCUBOT_REPO_PAT }}
GIT_BRANCH: ${{ github.event.pull_request.head.sha }}
run: npm install dotenv && node script/early-access/clone-for-build.js
- name: 'Build and push image'
uses: docker/build-push-action@a66e35b9cbcf4ad0ea91ffcaf7bbad63ad9e0229
with:
context: .
push: true
target: ${{ fromJSON('["production", "production_early_access"]')[github.repository == 'github/docs-internal'] }}
tags: ${{ env.DOCKER_IMAGE }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Deploy ARM template is idempotent
# Note: once the resources exist the image tag must change for a new deployment to occur (the image tag includes workflow run number, run attempt, as well as sha)
- name: Run ARM deploy
id: deploy
uses: azure/arm-deploy@841b12551939c88af8f6df767c24c38a5620fd0d
with:
resourceGroupName: docs-nonprod
subscriptionId: ${{ secrets.NONPROD_SUBSCRIPTION_ID }}
template: ./staging-azure-deploy-template.json
parameters: appName="${{ env.APP_NAME }}"
location="East US"
linuxFxVersion="DOCKER|${{ env.DOCKER_IMAGE }}"
dockerRegistryUrl="https://${{ secrets.NONPROD_REGISTRY_SERVER }}"
dockerRegistryUsername="${{ env.NONPROD_REGISTRY_USERNAME }}"
dockerRegistryPassword="${{ secrets.NONPROD_REGISTRY_PASSWORD }}"
- run: echo ${{ steps.deploy.outputs.defaultHostName }}

50
.github/workflows/staging-undeploy-azure.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,50 @@
name: Staging - Undeploy Azure PR
# **What it does**: To undeploy PRs from Azure staging environment, i.e. destroy the App and associated resources.
# **Why we have it**: To save money spent on deployments for closed PRs.
# **Who does it impact**: All contributors.
on:
pull_request:
types:
- closed
- locked
jobs:
undeploy:
name: Undeploy
runs-on: ubuntu-latest
timeout-minutes: 5
env:
GITHUB_EVENT_NUMBER: ${{ github.event.number }}
IMAGE_REPO: ${{ github.repository }}/pr-${{ github.event.number }}
steps:
- name: 'Set env vars'
id: vars
run: |
REPO_NAME=${GITHUB_REPOSITORY#*\/}
echo "APP_NAME=gh${REPO_NAME}-staging-${GITHUB_EVENT_NUMBER}" >> $GITHUB_ENV
- name: 'Az CLI login'
uses: azure/login@66d2e78565ab7af265d2b627085bc34c73ce6abb
with:
creds: ${{ secrets.NONPROD_AZURE_CREDENTIALS }}
# Delete web app (which will also delete the App Service plan)
# This will succeed even if the app doesn't exist / has already been deleted
- name: 'Delete App Service App (which will also delete the App Service plan)'
run: |
az webapp delete -n $APP_NAME -g docs-nonprod
# Untag all images under this PR's container registry repo - the container registry will automatically remove untagged images.
# This will fail if the IMAGE_REPO doesn't exist, but we don't care
- name: 'Untag all docker images for this PR'
run: |
az acr repository delete -n ghdocs --repository ${{ env.IMAGE_REPO }} -y || true
# Remove all deployments from this environment and remove the environment
- uses: strumwolf/delete-deployment-environment@45c821e46baa405e25410700fe2e9643929706a0
with:
token: ${{ secrets.DOCUBOT_REPO_PAT }}
environment: staging-pr-${{ github.event.number }}

2
.github/workflows/workflow-lint.yml поставляемый
Просмотреть файл

@ -30,4 +30,4 @@ jobs:
- name: Run linter
uses: cschleiden/actions-linter@caffd707beda4fc6083926a3dff48444bc7c24aa
with:
workflows: '[".github/workflows/*.yml", ".github/workflows/*.yaml", "!.github/workflows/remove-from-fr-board.yaml", "!.github/workflows/staging-deploy-pr.yml", "!.github/workflows/triage-issue-comments.yml"]'
workflows: '[".github/workflows/*.yml", ".github/workflows/*.yaml", "!.github/workflows/remove-from-fr-board.yaml", "!.github/workflows/staging-deploy-pr.yml", "!.github/workflows/triage-issue-comments.yml", "!.github/workflows/staging-build-and-deploy-azure.yml"]'

Просмотреть файл

@ -0,0 +1,111 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"appName": {
"type": "String"
},
"location": {
"type": "String"
},
"linuxFxVersion": {
"type": "String"
},
"dockerRegistryUrl": {
"type": "String"
},
"dockerRegistryUsername": {
"type": "String"
},
"dockerRegistryPassword": {
"type": "SecureString"
}
},
"variables": {
"appServicePlanName": "[concat('ASP-', parameters('appName'))]"
},
"resources": [
{
"type": "Microsoft.Web/serverfarms",
"apiVersion": "2021-02-01",
"name": "[variables('appServicePlanName')]",
"location": "[parameters('location')]",
"sku": {
"name": "B2"
},
"kind": "linux",
"properties": {
"reserved": true
}
},
{
"type": "Microsoft.Web/sites",
"apiVersion": "2018-11-01",
"name": "[parameters('appName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', variables('appServicePlanName'))]"
],
"tags": {},
"properties": {
"name": "[parameters('appName')]",
"siteConfig": {
"appSettings": [
{
"name": "DOCKER_REGISTRY_SERVER_URL",
"value": "[parameters('dockerRegistryUrl')]"
},
{
"name": "DOCKER_REGISTRY_SERVER_USERNAME",
"value": "[parameters('dockerRegistryUsername')]"
},
{
"name": "DOCKER_REGISTRY_SERVER_PASSWORD",
"value": "[parameters('dockerRegistryPassword')]"
},
{
"name": "WEBSITES_ENABLE_APP_SERVICE_STORAGE",
"value": "false"
},
{
"name": "NODE_ENV",
"value": "production"
},
{
"name": "PORT",
"value": "4000"
},
{
"name": "DEPLOYMENT_ENV",
"value": "azure"
},
{
"name": "WEB_CONCURRENCY",
"value": "1"
},
{
"name": "ENABLED_LANGUAGES",
"value": "en,ja"
}
],
"linuxFxVersion": "[parameters('linuxFxVersion')]",
"appCommandLine": "",
"alwaysOn": false,
"numberOfWorkers": 1,
"healthCheckPath": "/healthz",
"httpLoggingEnabled": true,
"logsDirectorySizeLimit": 35
},
"serverFarmId": "[concat('/subscriptions/', subscription().id, '/resourcegroups/', resourceGroup().name, '/providers/Microsoft.Web/serverfarms/', variables('appServicePlanName'))]",
"clientAffinityEnabled": false
}
}
],
"outputs": {
"defaultHostName": {
"type": "string",
"value": "[concat('https://', parameters('appName'), '.azurewebsites.net')]"
}
}
}