Deploy Web Vault to USDEV from main #6106
This file contains hidden or 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: {} | |
| 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_client_key_name: ${{ steps.config.outputs.azure_login_client_key_name }} | |
| azure_login_subscription_id_key_name: ${{ steps.config.outputs.azure_login_subscription_id_key_name }} | |
| retrieve_secrets_keyvault: ${{ steps.config.outputs.retrieve_secrets_keyvault }} | |
| 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 | |
| env: | |
| _ENVIRONMENT: ${{ inputs.environment }} | |
| run: | | |
| ENV_NAME_LOWER=$(echo "$_ENVIRONMENT" | awk '{print tolower($0)}') | |
| echo "configuring the Web deploy for _ENVIRONMENT" | |
| echo "environment=$_ENVIRONMENT" >> "$GITHUB_OUTPUT" | |
| case $_ENVIRONMENT in | |
| "USQA") | |
| echo "azure_login_client_key_name=AZURE_CLIENT_ID_USQA" >> "$GITHUB_OUTPUT" | |
| echo "azure_login_subscription_id_key_name=AZURE_SUBSCRIPTION_ID_USQA" >> "$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_client_key_name=AZURE_CLIENT_ID_EUQA" >> "$GITHUB_OUTPUT" | |
| echo "azure_login_subscription_id_key_name=AZURE_SUBSCRIPTION_ID_EUQA" >> "$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_client_key_name=AZURE_CLIENT_ID_USPROD" >> "$GITHUB_OUTPUT" | |
| echo "azure_login_subscription_id_key_name=AZURE_SUBSCRIPTION_ID_USPROD" >> "$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_client_key_name=AZURE_CLIENT_ID_EUPROD" >> "$GITHUB_OUTPUT" | |
| echo "azure_login_subscription_id_key_name=AZURE_SUBSCRIPTION_ID_EUPROD" >> "$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_client_key_name=AZURE_CLIENT_ID_USDEV" >> "$GITHUB_OUTPUT" | |
| echo "azure_login_subscription_id_key_name=AZURE_SUBSCRIPTION_ID_USDEV" >> "$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 | |
| - name: Environment Protection | |
| env: | |
| BUILD_WEB_RUN_ID: ${{ inputs.build-web-run-id }} | |
| GH_TOKEN: ${{ github.token }} | |
| _ENVIRONMENT: ${{ inputs.environment }} | |
| _BRANCH_OR_TAG: ${{ inputs.branch-or-tag }} | |
| run: | | |
| BRANCH_OR_TAG_LOWER="" | |
| if [[ "$BUILD_WEB_RUN_ID" == "" ]]; then | |
| BRANCH_OR_TAG_LOWER=$(echo "$_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 [[ \ | |
| $_ENVIRONMENT =~ \.*($PROD_ENV_PATTERN)\.* && \ | |
| ! "$BRANCH_OR_TAG_LOWER" =~ ^($PROD_ALLOWED_TAGS_PATTERN).* \ | |
| ]] || [[ \ | |
| $_ENVIRONMENT =~ \.*($QA_ENV_PATTERN)\.* && \ | |
| ! "$BRANCH_OR_TAG_LOWER" =~ ^($QA_ALLOWED_TAGS_PATTERN).* \ | |
| ]] || [[ \ | |
| $_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 $_ENVIRONMENT environment" | |
| echo | |
| echo "Environment: $_ENVIRONMENT" | |
| echo "Tag: $BRANCH_OR_TAG_LOWER" | |
| exit 1 | |
| else | |
| echo "The input Branch/Tag: '$BRANCH_OR_TAG_LOWER' is allowed to deploy on $_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 | |
| permissions: | |
| contents: read | |
| id-token: write | |
| 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: Log in to Azure | |
| if: ${{ steps.download-latest-artifacts.outcome == 'failure' }} | |
| uses: bitwarden/gh-actions/azure-login@main | |
| with: | |
| subscription_id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| tenant_id: ${{ secrets.AZURE_TENANT_ID }} | |
| client_id: ${{ secrets.AZURE_CLIENT_ID }} | |
| - 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: Log out from Azure | |
| if: ${{ steps.download-latest-artifacts.outcome == 'failure' }} | |
| uses: bitwarden/gh-actions/azure-logout@main | |
| - 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 }} | |
| _BUILD_WEB_RUN_ID: ${{ inputs.build-web-run-id }} | |
| _ARTIFACT_BUILD_COMMIT: ${{ steps.download-latest-artifacts-run-id.outputs.artifact-build-commit }} | |
| _DOWNLOAD_LATEST_ARTIFACTS_OUTCOME: ${{ steps.download-latest-artifacts.outcome }} | |
| _WORKFLOW_ID: ${{ steps.trigger-build-web.outputs.workflow_id}} | |
| _ARTIFACT_COMMIT: ${{ steps.download-latest-artifacts.outputs.artifact-build-commit }} | |
| run: | | |
| # If run-id was used, get the commit from the download-latest-artifacts-run-id step | |
| if [ "$_BUILD_WEB_RUN_ID" ]; then | |
| echo "commit=$_ARTIFACT_BUILD_COMMIT" >> "$GITHUB_OUTPUT" | |
| elif [ "$_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/$_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=$_ARTIFACT_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' ) ) }} | |
| permissions: | |
| id-token: write | |
| outputs: | |
| channel_id: ${{ steps.slack-message.outputs.channel_id }} | |
| ts: ${{ steps.slack-message.outputs.ts }} | |
| steps: | |
| - name: Notify Slack with start message | |
| 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_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} | |
| update-summary: | |
| name: Display commit | |
| needs: artifact-check | |
| runs-on: ubuntu-22.04 | |
| env: | |
| _ARTIFACT_BUILD_COMMIT_SHA: ${{ needs.artifact-check.outputs.artifact_build_commit }} | |
| steps: | |
| - name: Display commit SHA | |
| run: | | |
| REPO_URL="https://github.com/bitwarden/clients/commit" | |
| COMMIT_SHA="$_ARTIFACT_BUILD_COMMIT_SHA" | |
| 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 }} | |
| permissions: | |
| id-token: write | |
| deployments: write | |
| 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: '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 "$_ENVIRONMENT_ARTIFACT" | |
| - name: Login to Azure | |
| uses: bitwarden/gh-actions/azure-login@main | |
| env: | |
| # The following 2 values are ignored in Zizmor, because they have to be dynamically mapped from secrets | |
| # The only way around this is to create separate steps per environment with static secret references, which is not maintainable | |
| SUBSCRIPTION_ID: ${{ secrets[ needs.setup.outputs.azure_login_subscription_id_key_name ] }} # zizmor: ignore[overprovisioned-secrets] | |
| CLIENT_ID: ${{ secrets[ needs.setup.outputs.azure_login_client_key_name ] }} # zizmor: ignore[overprovisioned-secrets] | |
| TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| with: | |
| subscription_id: ${{ env.SUBSCRIPTION_ID }} | |
| tenant_id: ${{ env.TENANT_ID }} | |
| client_id: ${{ env.CLIENT_ID }} | |
| - name: Retrieve Storage Account name | |
| 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" | |
| - name: Sync to Azure Storage Account using azcopy | |
| working-directory: apps/web | |
| env: | |
| AZCOPY_AUTO_LOGIN_TYPE: AZCLI | |
| AZCOPY_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| _VAULT_NAME: ${{ steps.retrieve-secrets-azcopy.outputs.sa-bitwarden-web-vault-name }} | |
| run: | | |
| azcopy sync ./build "https://$_VAULT_NAME.blob.core.windows.net/\$web/" \ | |
| --delete-destination="${{ inputs.force-delete-destination }}" --compare-hash="MD5" | |
| - name: Log out from Azure | |
| uses: bitwarden/gh-actions/azure-logout@main | |
| - 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 | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Notify Slack with result | |
| 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_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }} | |
| AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} | |
| AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} |