--- 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: 'QA' 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 invert-default-sync-delete-destination-files-value: description: "Invert the default sync-delete-destination-files value" type: boolean default: false debug: description: "Debug mode" type: boolean default: 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 invert-default-sync-delete-destination-files-value: description: "Invert the default sync-delete-destination-files value" type: boolean default: false debug: description: "Debug mode" type: boolean default: true 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 }} 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 # Invert the default value for sync-delete-destination-files if [ ${{ inputs.invert-default-sync-delete-destination-files-value }} ]; then echo "sync-delete-destination-files=true" >> $GITHUB_OUTPUT else # This is the default value for USQA, EUQA, USPROD, and EUPROD echo "sync-delete-destination-files=false" >> $GITHUB_OUTPUT fi 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 ;; "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 ;; "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 ;; "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 ;; "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 if [ ${{ inputs.invert-default-sync-delete-destination-files-value }} ]; then echo "sync-delete-destination-files=false" >> $GITHUB_OUTPUT else # This is the default value for USDEV echo "sync-delete-destination-files=true" >> $GITHUB_OUTPUT fi ;; esac # Set the sync utility to use for deployment to the environment (az-sync or azcopy) echo "sync-utility=azcopy" >> $GITHUB_OUTPUT 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 get-branch-or-tag-sha: name: Get Branch or Tag SHA runs-on: ubuntu-22.04 outputs: branch-or-tag-sha: ${{ steps.get-branch-or-tag-sha.outputs.sha }} steps: - name: Checkout Branch uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: ref: ${{ inputs.branch-or-tag }} fetch-depth: 0 - name: Get Branch or Tag SHA id: get-branch-or-tag-sha run: | echo "sha=$(git rev-parse origin/${{ inputs.branch-or-tag }})" >> $GITHUB_OUTPUT notify-start: name: Notify Slack with start message needs: - approval - setup - get-branch-or-tag-sha runs-on: ubuntu-22.04 if: ${{ always() && contains( inputs.environment , 'QA' ) }} 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: team-eng-qa-devops event: 'start' commit-sha: ${{ needs.get-branch-or-tag-sha.outputs.branch-or-tag-sha }} url: https://github.com/bitwarden/clients/actions/runs/${{ github.run_id }} AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }} artifact-check: name: Check if Web artifact is present runs-on: ubuntu-22.04 needs: setup env: _ENVIRONMENT_ARTIFACT: ${{ needs.setup.outputs.environment-artifact }} steps: - name: 'Download latest cloud asset from branch/tag: ${{ inputs.branch-or-tag }}' uses: bitwarden/gh-actions/download-artifacts@main id: download-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-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-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-artifacts.outcome == 'failure' }} uses: convictional/trigger-workflow-and-wait@f69fa9eedd3c62a599220f4d5745230e237904be # v1.6.5 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 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 }}' - 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 cloud asset from branch/tag: ${{ inputs.branch-or-tag }}' 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=${{ needs.setup.outputs.sync-delete-destination-files }} - 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=${{ needs.setup.outputs.sync-delete-destination-files }} --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@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 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@2afb7d27101260f4a764219439564d954d10b5b0 # v2.0.1 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' ) }} needs: - notify-start - azure-deploy - setup - get-branch-or-tag-sha 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.get-branch-or-tag-sha.outputs.branch-or-tag-sha }} update-ts: ${{ needs.notify-start.outputs.ts }} AZURE_KV_CI_SERVICE_PRINCIPAL: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}