--- name: Build on: push: branches-ignore: - "l10n_master" - "gh-pages" paths-ignore: - ".github/workflows/**" workflow_dispatch: inputs: {} 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-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 if: github.ref == 'refs/heads/master' runs-on: windows-2022 needs: setup strategy: fail-fast: false matrix: variant: ["prod", "qa"] env: android_folder_path: 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@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0 with: dotnet-version: '8.0.x' - name: Set up MSBuild uses: microsoft/setup-msbuild@1ff57057b5cfdc39105cd07a01d78e9b0ea0c14c # v1.3.1 # 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: 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 ./${{ env.main_app_folder_path }}/app_play-keystore.jks ./.github/secrets/app_play-keystore.jks.gpg gpg --quiet --batch --yes --decrypt --passphrase="$DECRYPT_FILE_PASSWORD" \ --output ./${{ env.main_app_folder_path }}/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 ./${{ env.android_folder_path }}/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\"/" \ ./${{ env.android_folder_path }}/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"; $projToBuild = $($env:GITHUB_WORKSPACE + "/${{ env.main_app_project_path }}"); Write-Output "########################################" Write-Output "##### Build $configuration Configuration" Write-Output "########################################" dotnet build $projToBuild -c $configuration -f ${{ env.target-net-version }}-android shell: pwsh - name: Sign Android Build 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 "########################################" Write-Output "##### Sign Google Play Bundle Release Configuration" Write-Output "########################################" dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidPackageFormats=aab /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_upload-keystore.jks") /p:AndroidSigningKeyAlias=upload /p:AndroidSigningKeyPass="$($env:UPLOAD_KEYSTORE_PASSWORD)" /p:AndroidSigningStorePass="$($env:UPLOAD_KEYSTORE_PASSWORD)" --no-restore Write-Output "########################################" Write-Output "##### Copy Google Play Bundle to project root" Write-Output "########################################" $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 "########################################" Write-Output "##### Sign APK Release Configuration" Write-Output "########################################" dotnet publish $projToBuild -c Release -f ${{ env.target-net-version }}-android /p:AndroidKeyStore=true /p:AndroidSigningKeyStore=$("app_play-keystore.jks") /p:AndroidSigningKeyAlias=bitwarden /p:AndroidSigningKeyPass="$($env:PLAY_KEYSTORE_PASSWORD)" /p:AndroidSigningStorePass="$($env:PLAY_KEYSTORE_PASSWORD)" --no-restore Write-Output "########################################" Write-Output "##### Copy Release APK to project root" Write-Output "########################################" $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 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-13 needs: setup env: ios_folder_path: src/App/Platforms/iOS app_output_name: App steps: - name: Set XCode version uses: maxim-lobanov/setup-xcode@v1 with: xcode-version: 15.0.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@ac593985615ec2ede58e132d2e21d2b1cbd6127c # v3.3.0 with: submodules: 'true' - name: Login to Azure - CI Subscription uses: Azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6 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/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./${{ env.ios_folder_path }}/Info.plist perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Extension/Info.plist perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$BUILD_NUMBER"'<\/string>/' ./src/iOS.Autofill/Info.plist perl -0777 -pi.bak -e 's/CFBundleVersion<\/key>\s*1<\/string>/CFBundleVersion<\/key>\n\t'"$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/aps-environment<\/key>\s*development<\/string>/aps-environment<\/key>\n\tproduction<\/string>/' ./${{ env.ios_folder_path }}/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: Restore packages run: dotnet restore - 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: Archive Build for App Store run: | Write-Output "########################################" Write-Output "##### Archive for Release ios-arm64 Write-Output "########################################" dotnet publish ${{ env.main_app_project_path }} -c Release -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=ios-arm64 /p:ArchiveOnBuild=true Write-Output "########################################" Write-Output "##### Done" Write-Output "########################################" shell: pwsh - name: Archive Build for Mobile Automation run: | Write-Output "########################################" Write-Output "##### Archive Releae for iossimulator-arm64 Write-Output "########################################" dotnet publish ${{ env.main_app_project_path }} -c Release -f ${{ env.target-net-version }}-ios /p:RuntimeIdentifier=iossimulator-arm64 /p:ArchiveOnBuild=true 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: Export .app for Automation CI run: | ARCHIVE_PATH="./${{ env.main_app_folder_path }}/bin/Release/iossimulator-arm64/publish/${{ env.app_output_name }}.app" EXPORT_PATH="./bitwarden-export" zip -r -q ${{ env.app_output_name }}.app.zip $ARCHIVE_PATH mv ${{ env.app_output_name }}.app.zip $EXPORT_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/${{ env.app_output_name }}.ipa ./bitwarden-export/dSYMs/*.* if-no-files-found: error - name: Upload .app file for Automation CI uses: actions/upload-artifact@6673cd052c4cd6fcf4b4e6e60ea986c889389535 # v3.0.0 with: name: ${{ env.app_output_name }}.app.zip path: ./bitwarden-export/${{ env.app_output_name }}.app.zip 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/${{ env.app_output_name }}.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@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6 # 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@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0 # 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@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.6 # 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 }}