---
name: Build

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

env:
  main_app_folder_path: src/App
  main_app_project_path: src/App/App.csproj
  target-net-version: net8.0

jobs:
  cloc:
    name: CLOC
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout repo
        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

      - 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-22.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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
        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


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

      - name: Set up .NET
        uses: actions/setup-dotnet@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.0
        with:
          dotnet-version: '8.0.x'

      - name: Set up MSBuild
        uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3

      # This step might be obsolete at some point as .NET MAUI workloads
      # are starting to come pre-installed on the GH Actions build agents.
      - name: Install MAUI Workload
        run: dotnet workload install maui --ignore-failed-sources

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

      - name: Install Microsoft OpenJDK 11
        run: |
          choco install microsoft-openjdk11 --no-progress
          Write-Output "JAVA_HOME=$(Get-ChildItem -Path 'C:\Program Files\Microsoft\jdk*' | `
            Select -First 1 -ExpandProperty FullName)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
          Write-Output "Java Home: $env:JAVA_HOME"

      - 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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
        with:
          fetch-depth: 0

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

      - name: Download secrets
        env:
          ACCOUNT_NAME: bitwardenci
          CONTAINER_NAME: mobile
        run: |
          mkdir -p $HOME/secrets

          az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
          --name app_play-keystore.jks --file ./${{ env.android_folder_path_bash }}/app_play-keystore.jks --output none
          az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
          --name app_upload-keystore.jks --file ./${{ env.android_folder_path_bash }}/app_upload-keystore.jks --output none
          az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
          --name play_creds.json --file $HOME/secrets/play_creds.json --output none
        shell: bash

      - name: Download secrets - Google Services
        if: ${{ matrix.variant == 'prod' }}
        env:
          ACCOUNT_NAME: bitwardenci
          CONTAINER_NAME: mobile
        run: |
          az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME \
          --name google-services.json --file ./${{ env.android_folder_path_bash }}/google-services.json --output none
        shell: bash

      - name: Increment version
        run: |
          BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
          echo "##### Setting Android Version Code to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY

          sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
            ./${{ env.android_folder_path_bash }}/AndroidManifest.xml
        shell: bash

      - name: Restore packages
        run: nuget restore

      - name: Restore tools
        run: dotnet tool restore

      # - name: Run Core tests
      #   run: |
      #     dotnet test test/Core.Test/Core.Test.csproj --logger "trx;LogFileName=test-results.trx" `
      #       /p:CustomConstants=UT

      # - name: Report test results
      #   uses: dorny/test-reporter@eaa763f6ffc21c7a37837f56cd5f9737f27fc6c8 # v1.8.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 & Sign Android
        env:
          PLAY_KEYSTORE_PASSWORD: ${{ secrets.PLAY_KEYSTORE_PASSWORD }}
          UPLOAD_KEYSTORE_PASSWORD: ${{ secrets.UPLOAD_KEYSTORE_PASSWORD }}
        run: |
          $projToBuild = "$($env:GITHUB_WORKSPACE)/${{ env.main_app_project_path }}";
          $packageName = "com.x8bit.bitwarden";

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

          $signingUploadKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env.android_folder_path }}\app_upload-keystore.jks"
          dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android `
            /p:AndroidPackageFormats=aab `
            /p:AndroidKeyStore=true `
            /p:AndroidSigningKeyStore=$signingUploadKeyStore `
            /p:AndroidSigningKeyAlias=upload `
            /p:AndroidSigningKeyPass="$($env:UPLOAD_KEYSTORE_PASSWORD)" `
            /p:AndroidSigningStorePass="$($env:UPLOAD_KEYSTORE_PASSWORD)" --no-restore

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

          $signedAabPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\publish\$($packageName)-Signed.aab";
          $signedAabDestPath = "$($env:GITHUB_WORKSPACE)\$($packageName).aab";
          Copy-Item $signedAabPath $signedAabDestPath

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

          $signingPlayKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env.android_folder_path }}\app_play-keystore.jks"
          dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android `
            /p:AndroidKeyStore=true `
            /p:AndroidSigningKeyStore=$signingPlayKeyStore `
            /p:AndroidSigningKeyAlias=bitwarden `
            /p:AndroidSigningKeyPass="$($env:PLAY_KEYSTORE_PASSWORD)" `
            /p:AndroidSigningStorePass="$($env:PLAY_KEYSTORE_PASSWORD)" --no-restore

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

          $signedApkPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\publish\$($packageName)-Signed.apk";
          $signedApkDestPath = "$($env:GITHUB_WORKSPACE)\$($packageName).apk";
          Copy-Item $signedApkPath $signedApkDestPath

      - name: Upload Prod .aab artifact
        if: ${{ matrix.variant == 'prod' }}
        uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        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/main'
          && 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: |
          $publisherPath = "$($env:GITHUB_WORKSPACE)\store\google\Publisher\bin\Release\net8.0\Publisher.dll"
          $credsPath = "$($HOME)\secrets\play_creds.json"
          $aabPath = "$($env:GITHUB_WORKSPACE)\com.x8bit.bitwarden.aab"
          $track = "internal"

          dotnet $publisherPath $credsPath $aabPath $track


  f-droid:
    name: F-Droid Build
    runs-on: windows-2022
    env:
      android_folder_path: src\App\Platforms\Android
      android_folder_path_bash: src/App/Platforms/Android
      android_manifest_path: src/App/Platforms/Android/AndroidManifest.xml
    steps:
      - name: Setup NuGet
        uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
        with:
          nuget-version: 6.4.0

      - name: Set up .NET
        uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
        with:
          dotnet-version: '8.0.x'

      - name: Set up MSBuild
        uses: microsoft/setup-msbuild@ede762b26a2de8d110bb5a3db4d7e0e080c0e917 # v1.3.3

      # This step might be obsolete at some point as .NET MAUI workloads
      # are starting to come pre-installed on the GH Actions build agents.
      - name: Install MAUI Workload
        run: dotnet workload install maui --ignore-failed-sources

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

      - name: Install Microsoft OpenJDK 11
        run: |
          choco install microsoft-openjdk11 --no-progress
          Write-Output "JAVA_HOME=$(Get-ChildItem -Path 'C:\Program Files\Microsoft\jdk*' | Select -First 1 -ExpandProperty FullName)" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
          Write-Output "Java Home: $env:JAVA_HOME"

      - 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@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

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

      - name: Download secrets
        env:
          ACCOUNT_NAME: bitwardenci
          CONTAINER_NAME: mobile
          FILE: app_fdroid-keystore.jks
        run: |
          az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
            --file ${{ env.android_folder_path_bash }}/$FILE --output none
        shell: bash

      - name: Increment version
        run: |
          BUILD_NUMBER=$((3000 + $GITHUB_RUN_NUMBER))
          echo "##### Setting F-Droid Version Code to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY

          sed -i "s/android:versionCode=\"1\"/android:versionCode=\"$BUILD_NUMBER\"/" \
            ./${{ env.android_manifest_path }}
        shell: bash

      - name: Clean for F-Droid
        run: |
          $directoryBuildProps = $($env:GITHUB_WORKSPACE + "/Directory.Build.props");

          $androidManifest = $($env:GITHUB_WORKSPACE + "/${{ env.android_manifest_path }}");

          Write-Output "##### Back up project files"

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

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

          $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 "##### Enabling FDROID constant"

          (Get-Content $directoryBuildProps).Replace('<!-- <CustomConstants>FDROID</CustomConstants> -->', '<CustomConstants>FDROID</CustomConstants>') | Set-Content $directoryBuildProps

      - name: Restore packages
        run: dotnet restore

      - name: Build & Sign F-Droid
        env:
          FDROID_KEYSTORE_PASSWORD: ${{ secrets.FDROID_KEYSTORE_PASSWORD }}
        run: |
          $projToBuild = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_project_path }}";
          $packageName = "com.x8bit.bitwarden";

          Write-Output "##### Sign FDroid"

          $signingFdroidKeyStore = "$($env:GITHUB_WORKSPACE)\${{ env.android_folder_path }}\app_fdroid-keystore.jks"
          dotnet build $projToBuild -c Release -f ${{ env.target-net-version }}-android `
            /p:AndroidKeyStore=true `
            /p:AndroidSigningKeyStore=$signingFdroidKeyStore `
            /p:AndroidSigningKeyAlias=bitwarden `
            /p:AndroidSigningKeyPass="$($env:FDROID_KEYSTORE_PASSWORD)" `
            /p:AndroidSigningStorePass="$($env:FDROID_KEYSTORE_PASSWORD)" ` --no-restore

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

          $signedApkPath = "$($env:GITHUB_WORKSPACE)\${{ env.main_app_folder_path }}\bin\Release\${{ env.target-net-version }}-android\$($packageName)-Signed.apk";
          $signedApkDestPath = "$($env:GITHUB_WORKSPACE)\com.x8bit.bitwarden-fdroid.apk";

          Copy-Item $signedApkPath $signedApkDestPath

      - name: Upload F-Droid .apk artifact
        uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        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@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        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-13
    needs: setup
    env:
      ios_folder_path: src/App/Platforms/iOS
      app_output_name: App
      app_ci_output_filename: App_x64_Debug
    steps:
      - name: Set XCode version
        uses: maxim-lobanov/setup-xcode@60606e260d2fc5762a71e64e74b2174e8ea3c8bd # v1.6.0
        with:
          xcode-version: 15.1

      - name: Setup NuGet
        uses: nuget/setup-nuget@296fd3ccf8528660c91106efefe2364482f86d6f # v1.2.0
        with:
          nuget-version: 6.4.0

      - name: Set up .NET
        uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0
        with:
          dotnet-version: '8.0.x'

      # This step might be obsolete at some point as .NET MAUI workloads
      # are starting to come pre-installed on the GH Actions build agents.
      - name: Install MAUI Workload
        run: dotnet workload install maui --ignore-failed-sources

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

      - name: Checkout repo
        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
        with:
          submodules: 'true'

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

      - name: Retrieve secrets
        id: retrieve-secrets
        uses: bitwarden/gh-actions/get-keyvault-secrets@main
        with:
          keyvault: "bitwarden-ci"
          secrets: "appcenter-ios-token"

      - name: Download Provisioning Profiles secrets
        env:
          ACCOUNT_NAME: bitwardenci
          CONTAINER_NAME: profiles
        run: |
          mkdir -p $HOME/secrets
          profiles=(
              "dist_autofill.mobileprovision"
              "dist_bitwarden.mobileprovision"
              "dist_extension.mobileprovision"
              "dist_share_extension.mobileprovision"
              "dist_bitwarden_watch_app.mobileprovision"
              "dist_bitwarden_watch_app_extension.mobileprovision"
          )

          for FILE in "${profiles[@]}"
          do
            az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
              --file $HOME/secrets/$FILE --output none
          done

      - name: Download Google Services secret
        env:
          ACCOUNT_NAME: bitwardenci
          CONTAINER_NAME: mobile
          FILE: GoogleService-Info.plist
        run: |
          mkdir -p $HOME/secrets
          az storage blob download --account-name $ACCOUNT_NAME --container-name $CONTAINER_NAME --name $FILE \
            --file src/watchOS/bitwarden/$FILE --output none

      - name: Increment version
        run: |
          BUILD_NUMBER=$((100 + $GITHUB_RUN_NUMBER))
          echo "##### Setting iOS CFBundleVersion to $BUILD_NUMBER" | tee -a $GITHUB_STEP_SUMMARY

          perl -0777 -pi.bak -e 's/<key>CFBundleVersion<\/key>\s*<string>1<\/string>/<key>CFBundleVersion<\/key>\n\t<string>'"$BUILD_NUMBER"'<\/string>/' ./${{ env.ios_folder_path }}/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

      - name: Update Entitlements
        run: |
          echo "##### Updating Entitlements"
          perl -0777 -pi.bak -e 's/<key>aps-environment<\/key>\s*<string>development<\/string>/<key>aps-environment<\/key>\n\t<string>production<\/string>/' ./${{ env.ios_folder_path }}/Entitlements.plist

      - name: Get certificates
        run: |
          mkdir -p $HOME/certificates
          az keyvault secret show --id https://bitwarden-ci.vault.azure.net/certificates/ios-distribution |
            jq -r .value | base64 -d > $HOME/certificates/ios-distribution.p12

      - name: Set up Keychain
        env:
          KEYCHAIN_PASSWORD: ${{ secrets.IOS_KEYCHAIN_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/certificates/ios-distribution.p12 -k build.keychain -P "" -T /usr/bin/codesign \
            -T /usr/bin/security
          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $KEYCHAIN_PASSWORD build.keychain

      - 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_bitwarden_watch_app.mobileprovision
          WATCH_APP_EXTENSION_PROFILE_PATH=$HOME/secrets/dist_bitwarden_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"

      - name: Restore packages
        run: dotnet restore

      - name: Bulid WatchApp
        run: |
          echo "##### Build WatchApp with Release Configuration"
          xcodebuild archive -workspace ./src/watchOS/bitwarden/bitwarden.xcodeproj/project.xcworkspace -configuration Release -scheme bitwarden\ WatchKit\ App -archivePath ./src/watchOS/bitwarden

      - name: Archive Build for App Store
        run: |
          echo "##### Archive for Release ios-arm64"
          dotnet publish ${{ env.main_app_project_path }} -c Release -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=ios-arm64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false

      - name: Archive Build for Mobile Automation
        run: |
          echo "##### Archive Debug for iossimulator-x64"
          dotnet build ${{ env.main_app_project_path }} -c Debug -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=iossimulator-x64 /p:ArchiveOnBuild=true /p:MtouchUseLlvm=false
          ls $HOME/Library/Developer/Xcode/Archives

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

      - name: Export .app for Automation CI
        env:
          ARCHIVE_PATH: ./${{ env.main_app_folder_path }}/bin/Debug/${{ env.target-net-version }}-ios/iossimulator-x64
          EXPORT_PATH: ./bitwarden-export
        run: |
          zip -r -q ${{ env.app_ci_output_filename }}.app.zip $ARCHIVE_PATH
          mv ${{ env.app_ci_output_filename }}.app.zip $EXPORT_PATH

      - name: Copy all dSYMs files to upload
        env:
          EXPORT_PATH: ./bitwarden-export
          WATCH_ARCHIVE_DSYMS_PATH: ./src/watchOS/bitwarden.xcarchive/dSYMs/
          WATCH_DSYMS_EXPORT_PATH: ./bitwarden-export/Watch_dSYMs
        run: |
          ARCHIVE_DSYMS_PATH="$HOME/Library/Developer/Xcode/Archives/*/*.xcarchive/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

      - name: Upload App Store .ipa & dSYMs artifacts
        uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        with:
          name: Bitwarden iOS
          path: |
            ./bitwarden-export/Bitwarden.ipa
            ./bitwarden-export/dSYMs/*.*
          if-no-files-found: error

      - name: Upload .app file for Automation CI
        uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
        with:
          name: ${{ env.app_ci_output_filename }}.app.zip
          path: ./bitwarden-export/${{ env.app_ci_output_filename }}.app.zip
          if-no-files-found: error

      - name: Install AppCenter CLI
        if: |
          (github.ref == 'refs/heads/main'
            && 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/main'
            && 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

      - name: Upload Watch dSYMs to Firebase Crashlytics
        if: |
          (github.ref == 'refs/heads/main'
            && 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 "##### Uploading Watch dSYMs to Firebase"
          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" \;

      - name: Validate app in 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 --validate-app --type ios --file "./bitwarden-export/Bitwarden.ipa" \
              --username "$APPLE_ID_USERNAME" --password "$APPLE_ID_PASSWORD"

      - name: Deploy to App Store
        if: |
          (github.ref == 'refs/heads/main'
            && 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"


  crowdin-push:
    name: Crowdin Push
    if: github.ref == 'refs/heads/main'
    needs:
      - android
      - f-droid
      - ios
    runs-on: ubuntu-22.04
    env:
      _CROWDIN_PROJECT_ID: "269690"
    steps:
      - name: Checkout repo
        uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

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

      - name: Retrieve secrets
        id: retrieve-secrets
        uses: bitwarden/gh-actions/get-keyvault-secrets@main
        with:
          keyvault: "bitwarden-ci"
          secrets: "crowdin-api-token"

      - name: Upload Sources
        uses: crowdin/github-action@c953b17499daa6be3e5afbf7a63616fb02d8b18d # v1.19.0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}
        with:
          config: crowdin.yml
          crowdin_branch_name: main
          upload_sources: true
          upload_translations: false


  check-failures:
    name: Check for failures
    if: always()
    runs-on: ubuntu-22.04
    needs:
      - cloc
      - android
      - f-droid
      - ios
      - crowdin-push
    steps:
      - name: Check if any job failed
        if: |
          (github.ref == 'refs/heads/main'
          || github.ref == 'refs/heads/rc'
          || github.ref == 'refs/heads/hotfix-rc')
          && contains(needs.*.result, 'failure')
        run: exit 1

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

      - name: Retrieve secrets
        id: retrieve-secrets
        uses: bitwarden/gh-actions/get-keyvault-secrets@main
        if: failure()
        with:
          keyvault: "bitwarden-ci"
          secrets: "devops-alerts-slack-webhook-url"

      - 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 }}
