Deploy Web Vault to EUQA from main #2835
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Deploy Web Vault | |
run-name: Deploy Web Vault to ${{ inputs.environment }} from ${{ inputs.branch-or-tag }} | |
on: | |
workflow_dispatch: | |
inputs: | |
environment: | |
description: 'Environment' | |
default: 'USQA' | |
type: choice | |
options: | |
- USQA | |
- EUQA | |
- USPROD | |
- EUPROD | |
- USDEV | |
branch-or-tag: | |
description: "Branch or Tag name to deploy (examples: 'main', 'feature/sm', 'web-v2023.12.0')" | |
type: string | |
default: main | |
force-delete-destination: | |
description: "Delete remote files that are not found locally" | |
type: boolean | |
default: false | |
debug: | |
description: "Debug mode" | |
type: boolean | |
default: true | |
build-web-run-id: | |
description: "Build-web workflow Run ID to use for artifact download" | |
type: string | |
required: false | |
workflow_call: | |
inputs: | |
environment: | |
description: 'Environment' | |
default: 'USQA' | |
type: string | |
branch-or-tag: | |
description: "Branch or Tag name to deploy (examples: 'main', 'feature/sm', 'web-v2023.12.0')" | |
type: string | |
default: main | |
force-delete-destination: | |
description: "Delete remote files that are not found locally" | |
type: boolean | |
default: false | |
debug: | |
description: "Debug mode" | |
type: boolean | |
default: true | |
build-web-run-id: | |
description: "Build-web workflow Run ID to use for artifact download" | |
type: string | |
required: false | |
permissions: | |
deployments: write | |
jobs: | |
setup: | |
name: Setup | |
runs-on: ubuntu-22.04 | |
outputs: | |
environment: ${{ steps.config.outputs.environment }} | |
environment-url: ${{ steps.config.outputs.environment-url }} | |
environment-name: ${{ steps.config.outputs.environment-name }} | |
environment-artifact: ${{ steps.config.outputs.environment-artifact }} | |
azure-login-creds: ${{ steps.config.outputs.azure-login-creds }} | |
retrieve-secrets-keyvault: ${{ steps.config.outputs.retrieve-secrets-keyvault }} | |
sync-utility: ${{ steps.config.outputs.sync-utility }} | |
sync-delete-destination-files: ${{ steps.config.outputs.sync-delete-destination-files }} | |
slack-channel-name: ${{ steps.config.outputs.slack-channel-name }} | |
steps: | |
- name: Configure | |
id: config | |
run: | | |
ENV_NAME_LOWER=$(echo "${{ inputs.environment }}" | awk '{print tolower($0)}') | |
echo "configuring the Web deploy for ${{ inputs.environment }}" | |
echo "environment=${{ inputs.environment }}" >> $GITHUB_OUTPUT | |
case ${{ inputs.environment }} in | |
"USQA") | |
echo "azure-login-creds=AZURE_KV_US_QA_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT | |
echo "retrieve-secrets-keyvault=bw-webvault-rlktusqa-kv" >> $GITHUB_OUTPUT | |
echo "environment-artifact=web-*-cloud-QA.zip" >> $GITHUB_OUTPUT | |
echo "environment-name=Web Vault - US QA Cloud" >> $GITHUB_OUTPUT | |
echo "environment-url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT | |
echo "slack-channel-name=alerts-deploy-qa" >> $GITHUB_OUTPUT | |
;; | |
"EUQA") | |
echo "azure-login-creds=AZURE_KV_EU_QA_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT | |
echo "retrieve-secrets-keyvault=webvaulteu-westeurope-qa" >> $GITHUB_OUTPUT | |
echo "environment-artifact=web-*-cloud-euqa.zip" >> $GITHUB_OUTPUT | |
echo "environment-name=Web Vault - EU QA Cloud" >> $GITHUB_OUTPUT | |
echo "environment-url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT | |
echo "slack-channel-name=alerts-deploy-qa" >> $GITHUB_OUTPUT | |
;; | |
"USPROD") | |
echo "azure-login-creds=AZURE_KV_US_PROD_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT | |
echo "retrieve-secrets-keyvault=bw-webvault-klrt-kv" >> $GITHUB_OUTPUT | |
echo "environment-artifact=web-*-cloud-COMMERCIAL.zip" >> $GITHUB_OUTPUT | |
echo "environment-name=Web Vault - US Production Cloud" >> $GITHUB_OUTPUT | |
echo "environment-url=http://vault.bitwarden.com" >> $GITHUB_OUTPUT | |
echo "slack-channel-name=alerts-deploy-prd" >> $GITHUB_OUTPUT | |
;; | |
"EUPROD") | |
echo "azure-login-creds=AZURE_KV_EU_PRD_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT | |
echo "retrieve-secrets-keyvault=webvault-westeurope-prod" >> $GITHUB_OUTPUT | |
echo "environment-artifact=web-*-cloud-euprd.zip" >> $GITHUB_OUTPUT | |
echo "environment-name=Web Vault - EU Production Cloud" >> $GITHUB_OUTPUT | |
echo "environment-url=http://vault.bitwarden.eu" >> $GITHUB_OUTPUT | |
echo "slack-channel-name=alerts-deploy-prd" >> $GITHUB_OUTPUT | |
;; | |
"USDEV") | |
echo "azure-login-creds=AZURE_KV_US_DEV_SERVICE_PRINCIPAL" >> $GITHUB_OUTPUT | |
echo "retrieve-secrets-keyvault=webvault-eastus-dev" >> $GITHUB_OUTPUT | |
echo "environment-artifact=web-*-cloud-usdev.zip" >> $GITHUB_OUTPUT | |
echo "environment-name=Web Vault - US Development Cloud" >> $GITHUB_OUTPUT | |
echo "environment-url=http://vault.$ENV_NAME_LOWER.bitwarden.pw" >> $GITHUB_OUTPUT | |
echo "slack-channel-name=alerts-deploy-dev" >> $GITHUB_OUTPUT | |
;; | |
esac | |
# Set the sync utility to use for deployment to the environment (az-sync or azcopy) | |
echo "sync-utility=azcopy" >> $GITHUB_OUTPUT | |
- name: Environment Protection | |
env: | |
BUILD_WEB_RUN_ID: ${{ inputs.build-web-run-id }} | |
GH_TOKEN: ${{ github.token }} | |
run: | | |
BRANCH_OR_TAG_LOWER="" | |
if [[ "$BUILD_WEB_RUN_ID" == "" ]]; then | |
BRANCH_OR_TAG_LOWER=$(echo ${{ inputs.branch-or-tag }} | awk '{print tolower($0)}') | |
else | |
BRANCH_OR_TAG_LOWER=$(gh api /repos/bitwarden/clients/actions/runs/$BUILD_WEB_RUN_ID/artifacts --jq '.artifacts[0].workflow_run.head_branch' | awk '{print tolower($0)}') | |
fi | |
echo "Branch/Tag: $BRANCH_OR_TAG_LOWER" | |
PROD_ENV_PATTERN='USPROD|EUPROD' | |
PROD_ALLOWED_TAGS_PATTERN='web-v[0-9]+\.[0-9]+\.[0-9]+' | |
QA_ENV_PATTERN='USQA|EUQA' | |
QA_ALLOWED_TAGS_PATTERN='.*' | |
DEV_ENV_PATTERN='USDEV' | |
DEV_ALLOWED_TAGS_PATTERN='main' | |
if [[ \ | |
${{ inputs.environment }} =~ \.*($PROD_ENV_PATTERN)\.* && \ | |
! "$BRANCH_OR_TAG_LOWER" =~ ^($PROD_ALLOWED_TAGS_PATTERN).* \ | |
]] || [[ \ | |
${{ inputs.environment }} =~ \.*($QA_ENV_PATTERN)\.* && \ | |
! "$BRANCH_OR_TAG_LOWER" =~ ^($QA_ALLOWED_TAGS_PATTERN).* \ | |
]] || [[ \ | |
${{ inputs.environment }} =~ \.*($DEV_ENV_PATTERN)\.* && \ | |
$BRANCH_OR_TAG_LOWER != $DEV_ALLOWED_TAGS_PATTERN \ | |
]]; then | |
echo "!Deployment blocked!" | |
echo "Attempting to deploy a tag that is not allowed in ${{ inputs.environment }} environment" | |
echo | |
echo "Environment: ${{ inputs.environment }}" | |
echo "Tag: $BRANCH_OR_TAG_LOWER" | |
exit 1 | |
else | |
echo "The input Branch/Tag: '$BRANCH_OR_TAG_LOWER' is allowed to deploy on ${{ inputs.environment }} environment" | |
fi | |
approval: | |
name: Approval for Deployment to ${{ needs.setup.outputs.environment-name }} | |
needs: setup | |
runs-on: ubuntu-22.04 | |
environment: ${{ needs.setup.outputs.environment-name }} | |
steps: | |
- name: Success Code | |
run: exit 0 | |
artifact-check: | |
name: Check if Web artifact is present | |
runs-on: ubuntu-22.04 | |
needs: setup | |
env: | |
_ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment-artifact }} | |
outputs: | |
artifact-build-commit: ${{ steps.set-artifact-commit.outputs.commit }} | |
steps: | |
- name: 'Download latest cloud asset using GitHub Run ID: ${{ inputs.build-web-run-id }}' | |
if: ${{ inputs.build-web-run-id }} | |
uses: bitwarden/gh-actions/download-artifacts@main | |
id: download-latest-artifacts-run-id | |
continue-on-error: true | |
with: | |
workflow: build-web.yml | |
path: apps/web | |
workflow_conclusion: success | |
run_id: ${{ inputs.build-web-run-id }} | |
artifacts: ${{ env._ENVIRONMENT_ARTIFACT }} | |
- name: 'Download latest cloud asset from branch/tag: ${{ inputs.branch-or-tag }}' | |
if: ${{ !inputs.build-web-run-id }} | |
uses: bitwarden/gh-actions/download-artifacts@main | |
id: download-latest-artifacts | |
continue-on-error: true | |
with: | |
workflow: build-web.yml | |
path: apps/web | |
workflow_conclusion: success | |
branch: ${{ inputs.branch-or-tag }} | |
artifacts: ${{ env._ENVIRONMENT_ARTIFACT }} | |
- name: Login to Azure | |
if: ${{ steps.download-latest-artifacts.outcome == 'failure' }} | |
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 | |
with: | |
creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} | |
- name: Retrieve secrets for Build trigger | |
if: ${{ steps.download-latest-artifacts.outcome == 'failure' }} | |
id: retrieve-secret | |
uses: bitwarden/gh-actions/get-keyvault-secrets@main | |
with: | |
keyvault: "bitwarden-ci" | |
secrets: "github-pat-bitwarden-devops-bot-repo-scope" | |
- name: 'Trigger build web for missing branch/tag ${{ inputs.branch-or-tag }}' | |
if: ${{ steps.download-latest-artifacts.outcome == 'failure' }} | |
uses: convictional/trigger-workflow-and-wait@f69fa9eedd3c62a599220f4d5745230e237904be # v1.6.5 | |
id: trigger-build-web | |
with: | |
owner: bitwarden | |
repo: clients | |
github_token: ${{ steps.retrieve-secret.outputs.github-pat-bitwarden-devops-bot-repo-scope }} | |
workflow_file_name: build-web.yml | |
ref: ${{ inputs.branch-or-tag }} | |
wait_interval: 100 | |
- name: Set artifact build commit | |
id: set-artifact-commit | |
env: | |
GH_TOKEN: ${{ github.token }} | |
run: | | |
# If run-id was used, get the commit from the download-latest-artifacts-run-id step | |
if [ "${{ inputs.build-web-run-id }}" ]; then | |
echo "commit=${{ steps.download-latest-artifacts-run-id.outputs.artifact-build-commit }}" >> $GITHUB_OUTPUT | |
elif [ "${{ steps.download-latest-artifacts.outcome }}" == "failure" ]; then | |
# If the download-latest-artifacts step failed, query the GH API to get the commit SHA of the artifact that was just built with trigger-build-web. | |
commit=$(gh api /repos/bitwarden/clients/actions/runs/${{ steps.trigger-build-web.outputs.workflow_id }}/artifacts --jq '.artifacts[0].workflow_run.head_sha') | |
echo "commit=$commit" >> $GITHUB_OUTPUT | |
else | |
# Set the commit to the output of step download-latest-artifacts. | |
echo "commit=${{ steps.download-latest-artifacts.outputs.artifact-build-commit }}" >> $GITHUB_OUTPUT | |
fi | |
notify-start: | |
name: Notify Slack with start message | |
needs: | |
- approval | |
- setup | |
- artifact-check | |
runs-on: ubuntu-22.04 | |
if: ${{ always() && ( contains( inputs.environment , 'QA' ) || contains( inputs.environment , 'DEV' ) ) }} | |
outputs: | |
channel_id: ${{ steps.slack-message.outputs.channel_id }} | |
ts: ${{ steps.slack-message.outputs.ts }} | |
steps: | |
- uses: bitwarden/gh-actions/report-deployment-status-to-slack@main | |
id: slack-message | |
with: | |
project: Clients | |
environment: ${{ needs.setup.outputs.environment-name }} | |
tag: ${{ inputs.branch-or-tag }} | |
slack-channel: ${{ needs.setup.outputs.slack-channel-name }} | |
event: 'start' | |
commit-sha: ${{ needs.artifact-check.outputs.artifact-build-commit }} | |
url: https://github.com/bitwarden/clients/actions/runs/${{ github.run_id }} | |
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} | |
update-summary: | |
name: Display commit | |
needs: artifact-check | |
runs-on: ubuntu-22.04 | |
steps: | |
- name: Display commit SHA | |
run: | | |
REPO_URL="https://github.com/bitwarden/clients/commit" | |
COMMIT_SHA="${{ needs.artifact-check.outputs.artifact-build-commit }}" | |
echo ":steam_locomotive: View [commit]($REPO_URL/$COMMIT_SHA)" >> $GITHUB_STEP_SUMMARY | |
azure-deploy: | |
name: Deploy Web Vault to ${{ inputs.environment }} Storage Account | |
needs: | |
- setup | |
- artifact-check | |
- approval | |
runs-on: ubuntu-22.04 | |
env: | |
_ENVIRONMENT: ${{ needs.setup.outputs.environment }} | |
_ENVIRONMENT_URL: ${{ needs.setup.outputs.environment-url }} | |
_ENVIRONMENT_NAME: ${{ needs.setup.outputs.environment-name }} | |
_ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment-artifact }} | |
steps: | |
- name: Create GitHub deployment | |
uses: chrnorm/deployment-action@55729fcebec3d284f60f5bcabbd8376437d696b1 # v2.0.7 | |
id: deployment | |
with: | |
token: '${{ secrets.GITHUB_TOKEN }}' | |
initial-status: 'in_progress' | |
environment-url: ${{ env._ENVIRONMENT_URL }} | |
environment: ${{ env._ENVIRONMENT_NAME }} | |
task: 'deploy' | |
description: 'Deployment from branch/tag: ${{ inputs.branch-or-tag }}' | |
ref: ${{ needs.artifact-check.outputs.artifact-build-commit }} | |
- name: Login to Azure | |
uses: Azure/login@e15b166166a8746d1a47596803bd8c1b595455cf # v1.6.0 | |
with: | |
creds: ${{ secrets[needs.setup.outputs.azure-login-creds] }} | |
- name: Retrieve Storage Account connection string for az sync | |
if: ${{ needs.setup.outputs.sync-utility == 'az-sync' }} | |
id: retrieve-secrets-az-sync | |
uses: bitwarden/gh-actions/get-keyvault-secrets@main | |
with: | |
keyvault: ${{ needs.setup.outputs.retrieve-secrets-keyvault }} | |
secrets: "sa-bitwarden-web-vault-dev-key-temp" | |
- name: Retrieve Storage Account name and SPN credentials for azcopy | |
if: ${{ needs.setup.outputs.sync-utility == 'azcopy' }} | |
id: retrieve-secrets-azcopy | |
uses: bitwarden/gh-actions/get-keyvault-secrets@main | |
with: | |
keyvault: ${{ needs.setup.outputs.retrieve-secrets-keyvault }} | |
secrets: "sa-bitwarden-web-vault-name,sp-bitwarden-web-vault-password,sp-bitwarden-web-vault-appid,sp-bitwarden-web-vault-tenant" | |
- name: 'Download latest cloud asset using GitHub Run ID: ${{ inputs.build-web-run-id }}' | |
if: ${{ inputs.build-web-run-id }} | |
uses: bitwarden/gh-actions/download-artifacts@main | |
id: download-latest-artifacts | |
continue-on-error: true | |
with: | |
workflow: build-web.yml | |
path: apps/web | |
workflow_conclusion: success | |
run_id: ${{ inputs.build-web-run-id }} | |
artifacts: ${{ env._ENVIRONMENT_ARTIFACT }} | |
- name: 'Download cloud asset from branch/tag: ${{ inputs.branch-or-tag }}' | |
if: ${{ !inputs.build-web-run-id }} | |
uses: bitwarden/gh-actions/download-artifacts@main | |
with: | |
workflow: build-web.yml | |
path: apps/web | |
workflow_conclusion: success | |
branch: ${{ inputs.branch-or-tag }} | |
artifacts: ${{ env._ENVIRONMENT_ARTIFACT }} | |
- name: Unzip build asset | |
working-directory: apps/web | |
run: unzip ${{ env._ENVIRONMENT_ARTIFACT }} | |
- name: Sync to Azure Storage Account using az storage blob sync | |
if: ${{ needs.setup.outputs.sync-utility == 'az-sync' }} | |
working-directory: apps/web | |
run: | | |
az storage blob sync \ | |
--source "./build" \ | |
--container '$web' \ | |
--connection-string "${{ steps.retrieve-secrets-az-sync.outputs.sa-bitwarden-web-vault-dev-key-temp }}" \ | |
--delete-destination=${{ inputs.force-delete-destination }} | |
- name: Sync to Azure Storage Account using azcopy | |
if: ${{ needs.setup.outputs.sync-utility == 'azcopy' }} | |
working-directory: apps/web | |
env: | |
AZCOPY_AUTO_LOGIN_TYPE: SPN | |
AZCOPY_SPA_APPLICATION_ID: ${{ steps.retrieve-secrets-azcopy.outputs.sp-bitwarden-web-vault-appid }} | |
AZCOPY_SPA_CLIENT_SECRET: ${{ steps.retrieve-secrets-azcopy.outputs.sp-bitwarden-web-vault-password }} | |
AZCOPY_TENANT_ID: ${{ steps.retrieve-secrets-azcopy.outputs.sp-bitwarden-web-vault-tenant }} | |
run: | | |
azcopy sync ./build 'https://${{ steps.retrieve-secrets-azcopy.outputs.sa-bitwarden-web-vault-name }}.blob.core.windows.net/$web/' \ | |
--delete-destination=${{ inputs.force-delete-destination }} --compare-hash="MD5" | |
- name: Debug sync logs | |
if: ${{ inputs.debug }} | |
run: cat /home/runner/.azcopy/*.log | |
- name: Debug index.html | |
if: ${{ inputs.debug }} | |
run: cat apps/web/build/index.html | |
- name: Update deployment status to Success | |
if: success() | |
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3 | |
with: | |
token: '${{ secrets.GITHUB_TOKEN }}' | |
environment-url: ${{ env._ENVIRONMENT_URL }} | |
state: 'success' | |
deployment-id: ${{ steps.deployment.outputs.deployment_id }} | |
- name: Update deployment status to Failure | |
if: failure() | |
uses: chrnorm/deployment-status@9a72af4586197112e0491ea843682b5dc280d806 # v2.0.3 | |
with: | |
token: '${{ secrets.GITHUB_TOKEN }}' | |
environment-url: ${{ env._ENVIRONMENT_URL }} | |
state: 'failure' | |
deployment-id: ${{ steps.deployment.outputs.deployment_id }} | |
notify: | |
name: Notify Slack with result | |
runs-on: ubuntu-22.04 | |
if: ${{ always() && ( contains( inputs.environment , 'QA' ) || contains( inputs.environment , 'DEV' ) ) }} | |
needs: | |
- setup | |
- notify-start | |
- azure-deploy | |
- artifact-check | |
steps: | |
- uses: bitwarden/gh-actions/report-deployment-status-to-slack@main | |
with: | |
project: Clients | |
environment: ${{ needs.setup.outputs.environment-name }} | |
tag: ${{ inputs.branch-or-tag }} | |
slack-channel: ${{ needs.notify-start.outputs.channel_id }} | |
event: ${{ needs.azure-deploy.result }} | |
url: https://github.com/bitwarden/clients/actions/runs/${{ github.run_id }} | |
commit-sha: ${{ needs.artifact-check.outputs.artifact-build-commit }} | |
update-ts: ${{ needs.notify-start.outputs.ts }} | |
AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} |