---
name: Build Browser

on:
  pull_request:
    branches-ignore:
      - 'l10n_master'
      - 'cf-pages'
    paths:
      - 'apps/browser/**'
      - 'libs/**'
      - '*'
      - '!*.md'
      - '!*.txt'
  push:
    branches:
      - 'master'
      - 'rc'
      - 'hotfix-rc/**'
    paths:
      - 'apps/browser/**'
      - 'libs/**'
      - '*'
      - '!*.md'
      - '!*.txt'
      - '.github/workflows/build-browser.yml'
  workflow_dispatch:
    inputs: {}

defaults:
  run:
    shell: bash

jobs:
  cloc:
    name: CLOC
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout repo
        uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846  # v3.0.0

      - name: Set up cloc
        run: |
          sudo apt update
          sudo apt -y install cloc

      - name: Print lines of code
        run: cloc --include-lang TypeScript,JavaScript,HTML,Sass,CSS --vcs git


  setup:
    name: Setup
    runs-on: ubuntu-20.04
    outputs:
      repo_url: ${{ steps.gen_vars.outputs.repo_url }}
      adj_build_number: ${{ steps.gen_vars.outputs.adj_build_number }}
    steps:
      - name: Get Package Version
        id: gen_vars
        run: |
          repo_url=https://github.com/$GITHUB_REPOSITORY.git
          adj_build_num=${GITHUB_SHA:0:7}

          echo "::set-output name=repo_url::$repo_url"
          echo "::set-output name=adj_build_number::$adj_build_num"


  locales-test:
    name: Locales Test
    runs-on: ubuntu-20.04
    needs:
      - setup
    defaults:
      run:
        working-directory: apps/browser
    steps:
      - name: Checkout repo
        uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846  # v3.0.0

      - name: Testing locales - extName length
        run: |
         found_error=false

         echo "Locales Test"
         echo "============"
         echo "extName string must be 40 characters or less"
         echo
         for locale in $(ls src/_locales/); do
           string_length=$(jq '.extName.message | length' src/_locales/$locale/messages.json)
           if [[ $string_length -gt 40 ]]; then
             echo "$locale: $string_length"
             found_error=true
           fi
         done

         if $found_error; then
           echo
           echo "Please fix 'extName' for the locales listed above."
           exit 1
         else
           echo "Test passed!"
         fi


  build:
    name: Build
    runs-on: windows-2019
    needs:
      - setup
      - locales-test
    env:
      _BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }}
    defaults:
      run:
        working-directory: apps/browser
    steps:
      - name: Checkout repo
        uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846  # v3.0.0

      - name: Set up Node
        uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd  # v3.3.0
        with:
          cache: 'npm'
          cache-dependency-path: '**/package-lock.json'
          node-version: '16'

      - name: Install node-gyp
        run: |
          npm install -g node-gyp
          node-gyp install $(node -v)

      - name: Print environment
        run: |
          node --version
          npm --version

      - name: NPM setup
        run: npm ci
        working-directory: ./

      - name: Build & Test
        run: |
          npm run dist
          npm run test

      - name: Gulp
        run: gulp ci

      - name: Build sources for reviewers
        shell: cmd
        run: |
          REM Remove ".git" directory
          rmdir /S /Q ".git"

          REM Copy root level files to source directory
          mkdir browser-source
          copy * browser-source

          REM Copy apps\browser to Browser source directory
          mkdir browser-source\apps\browser
          xcopy apps\browser\* browser-source\apps\browser /E

          REM Copy libs to Browser source directory
          mkdir browser-source\libs
          xcopy libs\* browser-source\libs /E

          call 7z a browser-source.zip "browser-source\*"
        working-directory: ./

      - name: Upload Opera artifact
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535  # v3.0.0
        with:
          name: dist-opera-${{ env._BUILD_NUMBER }}.zip
          path: apps/browser/dist/dist-opera.zip
          if-no-files-found: error

      - name: Upload Chrome artifact
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535  # v3.0.0
        with:
          name: dist-chrome-${{ env._BUILD_NUMBER }}.zip
          path: apps/browser/dist/dist-chrome.zip
          if-no-files-found: error

      - name: Upload Firefox artifact
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535  # v3.0.0
        with:
          name: dist-firefox-${{ env._BUILD_NUMBER }}.zip
          path: apps/browser/dist/dist-firefox.zip
          if-no-files-found: error

      - name: Upload Edge artifact
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535  # v3.0.0
        with:
          name: dist-edge-${{ env._BUILD_NUMBER }}.zip
          path: apps/browser/dist/dist-edge.zip
          if-no-files-found: error

      - name: Upload browser source
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535  # v3.0.0
        with:
          name: browser-source-${{ env._BUILD_NUMBER }}.zip
          path: browser-source.zip
          if-no-files-found: error

      - name: Upload coverage artifact
        if: false
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535  # v3.0.0
        with:
          name: coverage-${{ env._BUILD_NUMBER }}.zip
          path: apps/browser/coverage/coverage-${{ env._BUILD_NUMBER }}.zip
          if-no-files-found: error

  build-safari:
    name: Build Safari
    runs-on: macos-11
    needs:
      - setup
      - locales-test
    env:
      _BUILD_NUMBER: ${{ needs.setup.outputs.adj_build_number }}
    steps:
      - name: Checkout repo
        uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846  # v3.0.0

      - name: Set up Node
        uses: actions/setup-node@eeb10cff27034e7acf239c5d29f62154018672fd  # v3.3.0
        with:
          cache: 'npm'
          cache-dependency-path: '**/package-lock.json'
          node-version: '16'

      - name: Print environment
        run: |
          node --version
          npm --version

      - name: Decrypt secrets
        env:
          DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
        run: |
          mkdir -p $HOME/secrets
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output "$HOME/secrets/bitwarden-desktop-key.p12" \
            "$GITHUB_WORKSPACE/.github/secrets/bitwarden-desktop-key.p12.gpg"
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output "$HOME/secrets/appstore-app-cert.p12" \
            "$GITHUB_WORKSPACE/.github/secrets/appstore-app-cert.p12.gpg"
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output "$HOME/secrets/appstore-installer-cert.p12" \
            "$GITHUB_WORKSPACE/.github/secrets/appstore-installer-cert.p12.gpg"
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output "$HOME/secrets/devid-app-cert.p12" \
            "$GITHUB_WORKSPACE/.github/secrets/devid-app-cert.p12.gpg"
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output "$HOME/secrets/devid-installer-cert.p12" \
            "$GITHUB_WORKSPACE/.github/secrets/devid-installer-cert.p12.gpg"
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output "$HOME/secrets/macdev-cert.p12" \
            "$GITHUB_WORKSPACE/.github/secrets/macdev-cert.p12.gpg"
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output "$HOME/secrets/bitwarden_desktop_appstore.provisionprofile" \
            "$GITHUB_WORKSPACE/.github/secrets/bitwarden_desktop_appstore.provisionprofile.gpg"

      - name: Set up keychain
        env:
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
          DESKTOP_KEY_PASSWORD: ${{ secrets.DESKTOP_KEY_PASSWORD }}
          DEVID_CERT_PASSWORD: ${{ secrets.DEVID_CERT_PASSWORD }}
          APPSTORE_CERT_PASSWORD: ${{ secrets.APPSTORE_CERT_PASSWORD }}
          MACDEV_CERT_PASSWORD: ${{ secrets.MACDEV_CERT_PASSWORD }}
          APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
        run: |
          security create-keychain -p $KEYCHAIN_PASSWORD build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p $KEYCHAIN_PASSWORD build.keychain
          security set-keychain-settings -lut 1200 build.keychain
          security import "$HOME/secrets/bitwarden-desktop-key.p12" -k build.keychain -P $DESKTOP_KEY_PASSWORD \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
          security import "$HOME/secrets/devid-app-cert.p12" -k build.keychain -P $DEVID_CERT_PASSWORD \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
          security import "$HOME/secrets/devid-installer-cert.p12" -k build.keychain -P $DEVID_CERT_PASSWORD \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
          security import "$HOME/secrets/appstore-app-cert.p12" -k build.keychain -P $APPSTORE_CERT_PASSWORD \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
          security import "$HOME/secrets/appstore-installer-cert.p12" -k build.keychain -P $APPSTORE_CERT_PASSWORD \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
          security import "$HOME/secrets/macdev-cert.p12" -k build.keychain -P $MACDEV_CERT_PASSWORD \
            -T /usr/bin/codesign -T /usr/bin/security -T /usr/bin/productbuild
          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain

      - name: NPM setup
        run: npm ci
        working-directory: ./

      - name: Build Safari extension
        run: npm run dist:safari
        working-directory: apps/browser

      - name: Zip Safari build artifact
        run: |
          cd apps/browser/dist
          zip dist-safari.zip ./Safari/**/build/Release/safari.appex -r
          pwd
          ls -la

      - name: Upload Safari artifact
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535  # v3.0.0
        with:
          name: dist-safari-${{ env._BUILD_NUMBER }}.zip
          path: apps/browser/dist/dist-safari.zip
          if-no-files-found: error

  crowdin-push:
    name: Crowdin Push
    if: github.ref == 'refs/heads/master'
    runs-on: ubuntu-20.04
    needs:
      - build
      - build-safari
    steps:
      - name: Checkout repo
        uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846  # v3.0.0

      - name: Login to Azure
        uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
        with:
          creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}

      - name: Retrieve secrets
        id: retrieve-secrets
        uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
        with:
          keyvault: "bitwarden-prod-kv"
          secrets: "crowdin-api-token"

      - name: Upload Sources
        uses: crowdin/github-action@ecd7eb0ef6f3cfa16293c79e9cbc4bc5b5fd9c49  # v1.4.9
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
          CROWDIN_PROJECT_ID: "268134"
        with:
          config: apps/browser/crowdin.yml
          crowdin_branch_name: master
          upload_sources: true
          upload_translations: false

  trigger-desktop-build:
    name: Trigger desktop build
    if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') || contains(github.ref, 'hotfix-rc') }}
    runs-on: ubuntu-20.04
    needs:
      - build
      - build-safari
    steps:

      - name: Login to Azure
        uses: Azure/login@ec3c14589bd3e9312b3cc8c41e6860e258df9010
        with:
          creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}

      - name: Retrieve secrets
        id: retrieve-secrets
        uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
        with:
          keyvault: "bitwarden-prod-kv"
          secrets: "github-pat-bitwarden-devops-bot-repo-scope"

      - name: Extract branch name
        id: extract_branch
        shell: bash
        run: echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})"

      - name: Call GitHub API to trigger desktop build workflow
        env:
          TOKEN: ${{ steps.retrieve-secrets.outputs.github-pat-bitwarden-devops-bot-repo-scope }}
          BRANCH_NAME: ${{ steps.extract_branch.outputs.branch }}
        run: |

          JSON_STRING=$(printf '{"ref":"%s"}' "$BRANCH_NAME")

          curl \
            -X POST \
            -i -u bitwarden-devops-bot:$TOKEN \
            -H "Accept: application/vnd.github.v3+json" \
            https://api.github.com/repos/bitwarden/clients/actions/workflows/build-desktop.yml/dispatches \
            -d $JSON_STRING

  check-failures:
    name: Check for failures
    if: always()
    runs-on: ubuntu-20.04
    needs:
      - cloc
      - setup
      - locales-test
      - build
      - build-safari
      - crowdin-push
      - trigger-desktop-build
    steps:
      - name: Check if any job failed
        if: ${{ (github.ref == 'refs/heads/master') || (github.ref == 'refs/heads/rc') }}
        env:
          CLOC_STATUS: ${{ needs.cloc.result }}
          SETUP_STATUS: ${{ needs.setup.result }}
          LOCALES_TEST_STATUS: ${{ needs.locales-test.result }}
          BUILD_STATUS: ${{ needs.build.result }}
          SAFARI_BUILD_STATUS: ${{ needs.build-safari.result }}
          CROWDIN_PUSH_STATUS: ${{ needs.crowdin-push.result }}
          TRIGGER_DESKTOP_BUILD_STATUS: ${{ needs.trigger-desktop-build.result }}
        run: |
          if [ "$CLOC_STATUS" = "failure" ]; then
              exit 1
          elif [ "$SETUP_STATUS" = "failure" ]; then
              exit 1
          elif [ "$LOCALES_TEST_STATUS" = "failure" ]; then
              exit 1
          elif [ "$BUILD_STATUS" = "failure" ]; then
              exit 1
          elif [ "$SAFARI_BUILD_STATUS" = "failure" ]; then
              exit 1
          elif [ "$CROWDIN_PUSH_STATUS" = "failure" ]; then
              exit 1
          elif [ "$TRIGGER_DESKTOP_BUILD_STATUS" = "failure" ]; then
              exit 1
          fi

      - name: Login to Azure - Prod Subscription
        uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf
        if: failure()
        with:
          creds: ${{ secrets.AZURE_PROD_KV_CREDENTIALS }}

      - name: Retrieve secrets
        id: retrieve-secrets
        uses: Azure/get-keyvault-secrets@b5c723b9ac7870c022b8c35befe620b7009b336f
        if: failure()
        with:
          keyvault: "bitwarden-prod-kv"
          secrets: "devops-alerts-slack-webhook-url"

      - name: Notify Slack on failure
        uses: act10ns/slack@da3191ebe2e67f49b46880b4633f5591a96d1d33  # v1.5.0
        if: failure()
        env:
          SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }}
        with:
          status: ${{ job.status }}
