---
name: Build

on:
  push:
    branches-ignore:
      - "l10n_master"
      - "gh-pages"
    paths-ignore:
      - ".github/workflows/**"
  workflow_dispatch:
    inputs: {}

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

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

      - name: Print lines of code
        run: cloc --vcs git --exclude-dir Resources,store,test,Properties --include-lang C#,XAML


  setup:
    name: Setup
    runs-on: ubuntu-20.04
    outputs:
      rc_branch_exists: ${{ steps.branch-check.outputs.rc_branch_exists }}
      hotfix_branch_exists: ${{ steps.branch-check.outputs.hotfix_branch_exists }}
    steps:
      - name: Checkout repo
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
        with:
          submodules: 'true'

      - name: Check if special branches exist
        id: branch-check
        run: |
          if [[ $(git ls-remote --heads origin rc) ]]; then
            echo "rc_branch_exists=1" >> $GITHUB_OUTPUT
          else
            echo "rc_branch_exists=0" >> $GITHUB_OUTPUT
          fi

          if [[ $(git ls-remote --heads origin hotfix-rc) ]]; then
            echo "hotfix_branch_exists=1" >> $GITHUB_OUTPUT
          else
            echo "hotfix_branch_exists=0" >> $GITHUB_OUTPUT
          fi
        shell: bash


  android:
    name: Android
    runs-on: windows-2022
    needs: setup
    strategy:
      fail-fast: false
      matrix:
        variant: ["prod", "qa"]
    steps:
      - name: Setup NuGet
        uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
        with:
          nuget-version: 5.9.0

      - name: Set up MSBuild
        uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1

      - name: Setup Windows builder
        run: choco install checksum --no-progress

      - name: Work Around for broken Windows 2022 Runner Image
        run: |
          Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
          $InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
          $componentsToAdd = @(
            "Component.Xamarin"
          )
          [string]$workloadArgs = $componentsToAdd | ForEach-Object {" --add " +  $_}
          $Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache')
          $process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
          if ($process.ExitCode -eq 0)
          {
              Write-Host "components have been successfully added"
          }
          else
          {
              Write-Host "components were not installed"
              exit 1
          }
      - name: Print environment
        run: |
          nuget help | grep Version
          msbuild -version
          dotnet --info
          echo "GitHub ref: $GITHUB_REF"
          echo "GitHub event: $GITHUB_EVENT"

      - name: Checkout repo
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
        with:
          fetch-depth: 0
      - name: Decrypt secrets
        env:
          DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
        run: |
          mkdir -p ~/secrets

          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output ./src/Android/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output ./src/Android/app_upload-keystore.jks ./.github/secrets/app_upload-keystore.jks.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output $HOME/secrets/play_creds.json ./.github/secrets/play_creds.json.gpg
        shell: bash
      - name: Decrypt secrets - Google Services
        if: ${{ matrix.variant == 'prod' }}
        env:
          DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
        run: |
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output ./src/Android/google-services.json ./.github/secrets/google-services.json.gpg
        shell: bash
      - name: Increment version
        run: |
          BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))

          echo "########################################"
          echo "##### Setting Version Code $BUILD_NUMBER"
          echo "########################################"

          sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
            ./src/Android/Properties/AndroidManifest.xml
        shell: bash

      - name: Restore packages
        run: nuget restore

      - name: Restore tools
        run: dotnet tool restore
        shell: pwsh

      - name: Verify Format
        run: dotnet tool run dotnet-format --check
        shell: pwsh

      - name: Run Core tests
        run: dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx"
        shell: pwsh

      - name: Report test results
        uses: dorny/test-reporter@c9b3d0e2bd2a4e96aaf424dbaa31c46b42318226 # v1.6.0
        if: always()
        with:
          name: Test Results
          path: "**/test-results.trx"
          reporter: dotnet-trx
          fail-on-error: true

      - name: Build Play Store publisher
        if: ${{ matrix.variant == 'prod' }}
        run: dotnet build ./store/google/Publisher/Publisher.csproj -p:Configuration=Release

      - name: Setup Android build  (${{ matrix.variant }})
        run: dotnet cake build.cake --target Android --variant ${{ matrix.variant }}

      - name: Build Android
        run: |
          $configuration = "Release";

          Write-Output "########################################"
          Write-Output "##### Build $configuration Configuration"
          Write-Output "########################################"
          msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"

        shell: pwsh

      - name: Sign Android Build
        env:
          PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }}
          UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
        run: |
          $androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
          $packageName = "com.x8bit.bitwarden";

          if ("${{ matrix.variant }}" -ne "prod")
          {
            $packageName = "com.x8bit.bitwarden.${{ matrix.variant }}";
          }
          Write-Output "########################################"
          Write-Output "##### Sign Google Play Bundle Release Configuration"
          Write-Output "########################################"

          msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
              "/p:AndroidSigningKeyAlias=upload" "/p:AndroidSigningKeyPass=$($env:UPLOAD_KEYSTORE_PASSWORD)" `
              "/p:AndroidSigningKeyStore=$("app_upload-keystore.jks")" `
              "/p:AndroidSigningStorePass=$($env:UPLOAD_KEYSTORE_PASSWORD)" "/p:AndroidPackageFormat=aab" "/v:quiet"

          Write-Output "########################################"
          Write-Output "##### Copy Google Play Bundle to project root"
          Write-Output "########################################"

          $signedAabPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/$($packageName)-Signed.aab");
          $signedAabDestPath = $($env:GITHUB_WORKSPACE + "/$($packageName).aab");
          Copy-Item $signedAabPath $signedAabDestPath

          Write-Output "########################################"
          Write-Output "##### Sign APK Release Configuration"
          Write-Output "########################################"

          msbuild "$($androidPath)" "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" `
              "/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:PLAY_KEYSTORE_PASSWORD)" `
              "/p:AndroidSigningKeyStore=$("app_play-keystore.jks")" `
              "/p:AndroidSigningStorePass=$($env:PLAY_KEYSTORE_PASSWORD)" "/v:quiet"

          Write-Output "########################################"
          Write-Output "##### Copy Release APK to project root"
          Write-Output "########################################"

          $signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/Release/$($packageName)-Signed.apk");
          $signedApkDestPath = $($env:GITHUB_WORKSPACE + "/$($packageName).apk");

          Copy-Item $signedApkPath $signedApkDestPath
        shell: pwsh
      - name: Upload Prod .aab artifact
        if: ${{ matrix.variant == 'prod' }}
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
        with:
          name: com.x8bit.bitwarden.aab
          path: ./com.x8bit.bitwarden.aab
          if-no-files-found: error

      - name: Upload Prod .apk artifact
        if: ${{ matrix.variant == 'prod' }}
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
        with:
          name: com.x8bit.bitwarden.apk
          path: ./com.x8bit.bitwarden.apk
          if-no-files-found: error

      - name: Upload Other .apk artifact
        if: ${{ matrix.variant != 'prod' }}
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
        with:
          name: com.x8bit.bitwarden.${{ matrix.variant }}.apk
          path: ./com.x8bit.bitwarden.${{ matrix.variant }}.apk
          if-no-files-found: error

      - name: Create checksum for Prod .apk artifact
        if: ${{ matrix.variant == 'prod' }}
        run: |
          checksum -f="./com.x8bit.bitwarden.apk" `
            -t sha256 | Out-File -Encoding ASCII ./bw-android-apk-sha256.txt

      - name: Create checksum for Other .apk artifact
        if: ${{ matrix.variant != 'prod' }}
        run: |
          checksum -f="./com.x8bit.bitwarden.${{ matrix.variant }}.apk" `
            -t sha256 | Out-File -Encoding ASCII ./bw-android-${{ matrix.variant }}-apk-sha256.txt

      - name: Upload .apk sha file for prod
        if: ${{ matrix.variant == 'prod' }}
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
        with:
          name: bw-android-apk-sha256.txt
          path: ./bw-android-apk-sha256.txt
          if-no-files-found: error

      - name: Upload .apk sha file for other
        if: ${{ matrix.variant != 'prod' }}
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
        with:
          name: bw-android-${{ matrix.variant }}-apk-sha256.txt
          path: ./bw-android-${{ matrix.variant }}-apk-sha256.txt
          if-no-files-found: error

      - name: Deploy to Play Store
        if: ${{ matrix.variant == 'prod' && (( github.ref == 'refs/heads/master'
          && needs.setup.outputs.rc_branch_exists == 0
          && needs.setup.outputs.hotfix_branch_exists == 0)
          || (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
          || github.ref == 'refs/heads/hotfix-rc' ) }}
        run: |
          PUBLISHER_PATH="$GITHUB_WORKSPACE/store/google/Publisher/bin/Release/netcoreapp3.1/Publisher.dll"
          CREDS_PATH="$HOME/secrets/play_creds.json"
          AAB_PATH="$GITHUB_WORKSPACE/com.x8bit.bitwarden.aab"
          TRACK="internal"

          dotnet $PUBLISHER_PATH $CREDS_PATH $AAB_PATH $TRACK
        shell: bash


  f-droid:
    name: F-Droid Build
    runs-on: windows-2022
    steps:
      - name: Setup NuGet
        uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
        with:
          nuget-version: 5.9.0

      - name: Set up MSBuild
        uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1

      - name: Setup Windows builder
        run: choco install checksum --no-progress

      - name: Work Around for broken Windows 2022 Runner Image
        run: |
          Set-Location "C:\Program Files (x86)\Microsoft Visual Studio\Installer\"
          $InstallPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise"
          $componentsToAdd = @(
            "Component.Xamarin"
          )
          [string]$workloadArgs = $componentsToAdd | ForEach-Object {" --add " +  $_}
          $Arguments = ('/c', "vs_installer.exe", 'modify', '--installPath', "`"$InstallPath`"",$workloadArgs, '--quiet', '--norestart', '--nocache')
          $process = Start-Process -FilePath cmd.exe -ArgumentList $Arguments -Wait -PassThru -WindowStyle Hidden
          if ($process.ExitCode -eq 0)
          {
              Write-Host "components have been successfully added"
          }
          else
          {
              Write-Host "components were not installed"
              exit 1
          }

      - name: Print environment
        run: |
          nuget help | grep Version
          msbuild -version
          dotnet --info
          echo "GitHub ref: $GITHUB_REF"
          echo "GitHub event: $GITHUB_EVENT"

      - name: Checkout repo
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0

      - name: Decrypt secrets
        env:
          DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
        run: |
          mkdir -p ~/secrets

          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output ./src/Android/app_fdroid-keystore.jks ./.github/secrets/app_fdroid-keystore.jks.gpg
        shell: bash

      - name: Increment version
        run: |
          BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))

          echo "########################################"
          echo "##### Setting Version Code $BUILD_NUMBER"
          echo "########################################"

          sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
            ./src/Android/Properties/AndroidManifest.xml
        shell: bash

      - name: Clean for F-Droid
        run: |
          $androidPath = $($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj");
          $appPath = $($env:GITHUB_WORKSPACE + "/src/App/App.csproj");
          $corePath = $($env:GITHUB_WORKSPACE + "/src/Core/Core.csproj");

          $androidManifest = $($env:GITHUB_WORKSPACE + "/src/Android/Properties/AndroidManifest.xml");

          Write-Output "########################################"
          Write-Output "##### Clean Android and App"
          Write-Output "########################################"

          msbuild "$($androidPath)" "/t:Clean" "/p:Configuration=FDroid"
          msbuild "$($appPath)" "/t:Clean" "/p:Configuration=FDroid"

          Write-Output "########################################"
          Write-Output "##### Backup project files"
          Write-Output "########################################"

          Copy-Item $androidManifest $($androidManifest + ".original");
          Copy-Item $androidPath $($androidPath + ".original");
          Copy-Item $appPath $($appPath + ".original");

          Write-Output "########################################"
          Write-Output "##### Cleanup Android Manifest"
          Write-Output "########################################"

          $xml=New-Object XML;
          $xml.Load($androidManifest);

          $nsAndroid=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
          $nsAndroid.AddNamespace("android", "http://schemas.android.com/apk/res/android");

          $xml.Save($androidManifest);

          Write-Output "########################################"
          Write-Output "##### Uninstall from Android.csproj"
          Write-Output "########################################"

          $xml=New-Object XML;
          $xml.Load($androidPath);

          $ns=New-Object System.Xml.XmlNamespaceManager($xml.NameTable);
          $ns.AddNamespace("ns", $xml.DocumentElement.NamespaceURI);

          $firebaseNode=$xml.SelectSingleNode(`
              "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Firebase.Messaging']", $ns);
          $firebaseNode.ParentNode.RemoveChild($firebaseNode);

          $daggerNode=$xml.SelectSingleNode(`
              "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.Google.Dagger']", $ns);
          $daggerNode.ParentNode.RemoveChild($daggerNode);

          $safetyNetNode=$xml.SelectSingleNode(`
              "/ns:Project/ns:ItemGroup/ns:PackageReference[@Include='Xamarin.GooglePlayServices.SafetyNet']", $ns);
          $safetyNetNode.ParentNode.RemoveChild($safetyNetNode);

          $xml.Save($androidPath);

          Write-Output "########################################"
          Write-Output "##### Uninstall from Core.csproj"
          Write-Output "########################################"

          $xml=New-Object XML;
          $xml.Load($corePath);

          $appCenterNode=$xml.SelectSingleNode("/Project/ItemGroup/PackageReference[@Include='Microsoft.AppCenter.Crashes']");
          $appCenterNode.ParentNode.RemoveChild($appCenterNode);

          $xml.Save($corePath);
        shell: pwsh

      - name: Restore packages
        run: nuget restore

      - name: Build for F-Droid
        run: |
          $configuration = "FDroid";

          Write-Output "########################################"
          Write-Output "##### Build $configuration Configuration"
          Write-Output "########################################"

          msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" "/p:Configuration=$configuration"
        shell: pwsh

      - name: Sign for F-Droid
        env:
          FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
        run: |
          Write-Output "########################################"
          Write-Output "##### Sign FDroid Configuration"
          Write-Output "########################################"

          msbuild "$($env:GITHUB_WORKSPACE + "/src/Android/Android.csproj")" `
            "/t:SignAndroidPackage" "/p:Configuration=FDroid" "/p:AndroidKeyStore=true" `
            "/p:AndroidSigningKeyAlias=bitwarden" "/p:AndroidSigningKeyPass=$($env:FDROID_KEYSTORE_PASSWORD)" `
            "/p:AndroidSigningKeyStore=$("app_fdroid-keystore.jks")" `
            "/p:AndroidSigningStorePass=$($env:FDROID_KEYSTORE_PASSWORD)" "/v:quiet"

          Write-Output "########################################"
          Write-Output "##### Copy FDroid apk to project root"
          Write-Output "########################################"

          $signedApkPath = $($env:GITHUB_WORKSPACE + "/src/Android/bin/FDroid/com.x8bit.bitwarden-Signed.apk");
          $signedApkDestPath = $($env:GITHUB_WORKSPACE + "/com.x8bit.bitwarden-fdroid.apk");

          Copy-Item $signedApkPath $signedApkDestPath
        shell: pwsh

      - name: Upload F-Droid .apk artifact
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
        with:
          name: com.x8bit.bitwarden-fdroid.apk
          path: ./com.x8bit.bitwarden-fdroid.apk
          if-no-files-found: error

      - name: Create checksum for F-Droid artifact
        run: |
          checksum -f="./com.x8bit.bitwarden-fdroid.apk" `
            -t sha256 | Out-File -Encoding ASCII ./bw-fdroid-apk-sha256.txt

      - name: Upload F-Droid sha file
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
        with:
          name: bw-fdroid-apk-sha256.txt
          path: ./bw-fdroid-apk-sha256.txt
          if-no-files-found: error


  ios:
    name: Apple iOS
    runs-on: macos-12
    needs: setup
    steps:
      - name: Setup NuGet
        uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
        with:
          nuget-version: 5.9.0

      - name: Print environment
        run: |
          nuget help | grep Version
          msbuild -version
          dotnet --info
          echo "GitHub ref: $GITHUB_REF"
          echo "GitHub event: $GITHUB_EVENT"

      - name: Checkout repo
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0
        with:
          submodules: 'true'

      - name: Login to Azure - CI Subscription
        uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
        with:
          creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}

      - name: Retrieve secrets
        id: retrieve-secrets
        env:
          KEYVAULT: bitwarden-ci
          SECRETS: |
            appcenter-ios-token
        run: |
          for i in ${SECRETS//,/ }
          do
            VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv)
            echo "::add-mask::$VALUE"
            echo "$i=$VALUE" >> $GITHUB_OUTPUT
          done

      - name: Decrypt secrets
        env:
          DECRYPT_FILE_PASSWORD: ${{ secrets.DECRYPT_FILE_PASSWORD }}
        run: |
          mkdir -p ~/secrets

          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output $HOME/secrets/bitwarden-mobile-key.p12 ./.github/secrets/bitwarden-mobile-key.p12.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output $HOME/secrets/iphone-distribution-cert.p12 ./.github/secrets/iphone-distribution-cert.p12.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output $HOME/secrets/dist_autofill.mobileprovision ./.github/secrets/dist_autofill.mobileprovision.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output $HOME/secrets/dist_bitwarden.mobileprovision ./.github/secrets/dist_bitwarden.mobileprovision.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output $HOME/secrets/dist_extension.mobileprovision ./.github/secrets/dist_extension.mobileprovision.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output $HOME/secrets/dist_share_extension.mobileprovision \
            ./.github/secrets/dist_share_extension.mobileprovision.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output $HOME/secrets/dist_watch_app.mobileprovision \
            ./.github/secrets/dist_watch_app.mobileprovision.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output $HOME/secrets/dist_watch_app_extension.mobileprovision \
            ./.github/secrets/dist_watch_app_extension.mobileprovision.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \
            --output ./src/watchOS/bitwarden/GoogleService-Info.plist ./.github/secrets/GoogleService-Info.plist.gpg
        shell: bash

      - name: Increment version
        run: |
          BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))

          echo "########################################"
          echo "##### Setting CFBundleVersion $BUILD_NUMBER"
          echo "########################################"

          perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS/Info.plist
          perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist
          perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist
          perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.ShareExtension/Info.plist
          cd src/watchOS/bitwarden
          agvtool new-version -all  $BUILD_NUMBER
          cd ../../..
        shell: bash

      - name: Update Entitlements
        run: |
          echo "########################################"
          echo "##### Updating Entitlements"
          echo "########################################"

          perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>production<\/string>/' ./src/iOS/Entitlements.plist
        shell: bash

      - name: Set up Keychain
        env:
          KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_PASSWORD }}
          MOBILE_KEY_PASSWORD: ${{ secrets.IOS_KEY_PASSWORD }}
          DIST_CERT_PASSWORD: ${{ secrets.IOS_DIST_CERT_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 ~/secrets/bitwarden-mobile-key.p12 -k build.keychain -P $MOBILE_KEY_PASSWORD \
            -T /usr/bin/codesign -T /usr/bin/security
          security import ~/secrets/iphone-distribution-cert.p12 -k build.keychain -P $DIST_CERT_PASSWORD \
            -T /usr/bin/codesign -T /usr/bin/security
          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain
        shell: bash

      - name: Set up provisioning profiles
        run: |
          AUTOFILL_PROFILE_PATH=$HOME/secrets/dist_autofill.mobileprovision
          BITWARDEN_PROFILE_PATH=$HOME/secrets/dist_bitwarden.mobileprovision
          EXTENSION_PROFILE_PATH=$HOME/secrets/dist_extension.mobileprovision
          SHARE_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_share_extension.mobileprovision
          WATCH_APP_PROFILE_PATH=$HOME/secrets/dist_watch_app.mobileprovision
          WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_watch_app_extension.mobileprovision
          PROFILES_DIR_PATH=$HOME/Library/MobileDevice/Provisioning\ Profiles

          mkdir -p "$PROFILES_DIR_PATH"

          AUTOFILL_UUID=$(grep UUID -A1 -a $AUTOFILL_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
          cp $AUTOFILL_PROFILE_PATH "$PROFILES_DIR_PATH/$AUTOFILL_UUID.mobileprovision"

          BITWARDEN_UUID=$(grep UUID -A1 -a $BITWARDEN_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
          cp $BITWARDEN_PROFILE_PATH "$PROFILES_DIR_PATH/$BITWARDEN_UUID.mobileprovision"

          EXTENSION_UUID=$(grep UUID -A1 -a $EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
          cp $EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$EXTENSION_UUID.mobileprovision"

          SHARE_EXTENSION_UUID=$(grep UUID -A1 -a $SHARE_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
          cp $SHARE_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$SHARE_EXTENSION_UUID.mobileprovision"

          WATCH_APP_UUID=$(grep UUID -A1 -a $WATCH_APP_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
          cp $WATCH_APP_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_UUID.mobileprovision"

          WATCH_APP_EXTENSION_UUID=$(grep UUID -A1 -a $WATCH_APP_EXTENSION_PROFILE_PATH | grep -io "[-A-F0-9]\{36\}")
          cp $WATCH_APP_EXTENSION_PROFILE_PATH "$PROFILES_DIR_PATH/$WATCH_APP_EXTENSION_UUID.mobileprovision"
        shell: bash

      - name: Bulid WatchApp
        run: |
          echo "########################################"
          echo "##### Build WatchApp with Release Configuration"
          echo "########################################"

          xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden

          echo "########################################"
          echo "##### Done"
          echo "########################################"
        shell: bash

      - name: Restore packages
        run: nuget restore

      - name: Archive Build for App Store
        run: |
          $configuration = "AppStore";
          $platform = "iPhone";

          Write-Output "########################################"
          Write-Output "##### Archive $configuration Configuration for $platform Platform"
          Write-Output "########################################"
          msbuild "$($env:GITHUB_WORKSPACE + "/src/iOS/iOS.csproj")" "/p:Platform=$platform" `
            "/p:Configuration=$configuration" "/p:ArchiveOnBuild=true" "/t:`"Build`""

          Write-Output "########################################"
          Write-Output "##### Done"
          Write-Output "########################################"
          ls ~/Library/Developer/Xcode/Archives
        shell: pwsh

      - name: Export .ipa for App Store
        run: |
          EXPORT_OPTIONS_PATH="./.github/resources/export-options-app-store.plist"
          ARCHIVE_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive"
          EXPORT_PATH="./bitwarden-export"

          xcodebuild -exportArchive -archivePath $ARCHIVE_PATH -exportPath $EXPORT_PATH \
            -exportOptionsPlist $EXPORT_OPTIONS_PATH
        shell: bash

      - name: Copy all dSYMs files to upload
        run: |
          ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/dSYMs"
          EXPORT_PATH="./bitwarden-export"

          WATCH_ARCHIVE_DSYMS_PATH="./src/watchOS/bitwarden.xcarchive/dSYMs/"
          WATCH_DSYMS_EXPORT_PATH="$EXPORT_PATH/Watch_dSYMs"

          cp -r -v $ARCHIVE_DSYMS_PATH $EXPORT_PATH
          mkdir $WATCH_DSYMS_EXPORT_PATH
          cp -r -v $WATCH_ARCHIVE_DSYMS_PATH $WATCH_DSYMS_EXPORT_PATH
        shell: bash

      - name: Upload App Store .ipa & dSYMs artifacts
        uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0
        with:
          name: Bitwarden iOS
          path: |
            ./bitwarden-export/Bitwarden.ipa
            ./bitwarden-export/dSYMs/*.*
          if-no-files-found: error

      - name: Install AppCenter CLI
        if: |
          (github.ref == 'refs/heads/master'
            && needs.setup.outputs.rc_branch_exists == 0
            && needs.setup.outputs.hotfix_branch_exists == 0)
          || (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
          || github.ref == 'refs/heads/hotfix-rc'
        run: npm install -g appcenter-cli

      - name: Upload dSYMs to App Center
        if: |
          (github.ref == 'refs/heads/master'
            && needs.setup.outputs.rc_branch_exists == 0
            && needs.setup.outputs.hotfix_branch_exists == 0)
          || (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
          || github.ref == 'refs/heads/hotfix-rc'
        env:
          APPCENTER_IOS_TOKEN: ${{ steps.retrieve-secrets.outputs.appcenter-ios-token }}
        run: appcenter crashes upload-symbols -a bitwarden/bitwarden -s "./bitwarden-export/dSYMs" --token $APPCENTER_IOS_TOKEN
        shell: bash

      - name: Upload Watch dSYMs to Firebase Crashlytics
        if: |
          (github.ref == 'refs/heads/master'
            && needs.setup.outputs.rc_branch_exists == 0
            && needs.setup.outputs.hotfix_branch_exists == 0)
          || (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
          || github.ref == 'refs/heads/hotfix-rc'
        run: |

          echo "########################################"
          echo "##### Uploading Watch dSYMs to Firebase"
          echo "########################################"

          find "$HOME/Library/Developer/XCode/DerivedData" -name "upload-symbols" -exec chmod +x {} \; -exec {} -gsp "./src/watchOS/bitwarden/GoogleService-Info.plist" -p ios "./bitwarden-export/Watch_dSYMs" \;
        shell: bash

      - name: Deploy to App Store
        if: |
          (github.ref == 'refs/heads/master'
            && needs.setup.outputs.rc_branch_exists == 0
            && needs.setup.outputs.hotfix_branch_exists == 0)
          || (github.ref == 'refs/heads/rc' && needs.setup.outputs.hotfix_branch_exists == 0)
          || github.ref == 'refs/heads/hotfix-rc'
        env:
          APPLE_ID_USERNAME: ${{ secrets.APPLE_ID_USERNAME }}
          APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
        run: |
          xcrun altool --upload-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
              --username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"
        shell: bash


  crowdin-push:
    name: Crowdin Push
    if: github.ref == 'refs/heads/master'
    needs:
      - android
      - f-droid
      - ios
    runs-on: ubuntu-20.04
    env:
      _CROWDIN_PROJECT_ID: "269690"
    steps:
      - name: Checkout repo
        uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0

      - name: Login to Azure - CI Subscription
        uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
        with:
          creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}

      - name: Retrieve secrets
        id: retrieve-secrets
        env:
          KEYVAULT: bitwarden-ci
          SECRETS: |
            crowdin-api-token
        run: |
          for i in ${SECRETS//,/ }
          do
            VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv)
            echo "::add-mask::$VALUE"
            echo "$i=$VALUE" >> $GITHUB_OUTPUT
          done

      - 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 }}
        with:
          config: crowdin.yml
          crowdin_branch_name: master
          upload_sources: true
          upload_translations: false


  check-failures:
    name: Check for failures
    if: always()
    runs-on: ubuntu-20.04
    needs:
      - cloc
      - android
      - f-droid
      - ios
      - crowdin-push
    steps:
      - name: Check if any job failed
        if: |
          (github.ref == 'refs/heads/master')
          || (github.ref == 'refs/heads/rc')
          || (github.ref == 'refs/heads/hotfix-rc')
        env:
          CLOC_STATUS: ${{ needs.cloc.result }}
          ANDROID_STATUS: ${{ needs.android.result }}
          F_DROID_STATUS: ${{ needs.f-droid.result }}
          IOS_STATUS: ${{ needs.ios.result }}
          CROWDIN_PUSH_STATUS: ${{ needs.crowdin-push.result }}
        run: |
          if [ "$CLOC_STATUS" = "failure" ]; then
              exit 1
          elif [ "$ANDROID_STATUS" = "failure" ]; then
              exit 1
          elif [ "$F_DROID_STATUS" = "failure" ]; then
              exit 1
          elif [ "$IOS_STATUS" = "failure" ]; then
              exit 1
          elif [ "$CROWDIN_PUSH_STATUS" = "failure" ]; then
              exit 1
          fi

      - name: Login to Azure - CI Subscription
        uses: Azure/login@1f63701bf3e6892515f1b7ce2d2bf1708b46beaf # v1.4.3
        if: failure()
        with:
          creds: ${{ secrets.AZURE_KV_CI_SERVICE_PRINCIPAL }}

      - name: Retrieve secrets
        id: retrieve-secrets
        if: failure()
        env:
          KEYVAULT: bitwarden-ci
          SECRETS: |
            devops-alerts-slack-webhook-url
        run: |
          for i in ${SECRETS//,/ }
          do
            VALUE=$(az keyvault secret show --vault-name $KEYVAULT --name $i --query value --output tsv)
            echo "::add-mask::$VALUE"
            echo "$i=$VALUE" >> $GITHUB_OUTPUT
          done

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