--- 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@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 - 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('', 'FDROID') | 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@4d6c8fcf3c8f7a60068d26b594648e99df24cee3 # v4.0.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/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 - name: Update Entitlements run: | echo "##### Updating Entitlements" perl -0777 -pi.bak -e 's/aps-environment<\/key>\s*development<\/string>/aps-environment<\/key>\n\tproduction<\/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@67705afb6985401459cd143d5f5f00c9dc212f23 # v1.20.2 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@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 if: failure() env: SLACK_WEBHOOK_URL: ${{ steps.retrieve-secrets.outputs.devops-alerts-slack-webhook-url }} with: status: ${{ job.status }}