diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 57f0ad9f1..524e81301 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,8 +16,8 @@ env: RUBY_VERSION: 3.2.2 jobs: - build: - name: Build + check: + name: Check runs-on: ubuntu-22.04 steps: - name: Checkout @@ -62,3 +62,131 @@ jobs: - name: Check run: bundle exec fastlane check + + build: + name: Build + needs: + - check + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + with: + fetch-depth: 0 + # Allow subsequent steps to trigger GitHub Actions via git push + # https://github.community/t/push-from-action-even-with-pat-does-not-trigger-action/17622 + persist-credentials: false + + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@b5418f5a58f5fd2eb486dd7efb368fe7be7eae45 # v2.1.3 + + - name: Cache Gradle Files + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ${{ github.workspace }}/build-cache + key: ${{ runner.os }}-gradle-v2-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/libs.versions.toml') }} + restore-keys: | + ${{ runner.os }}-gradle-v2- + + - name: Configure JDK + uses: actions/setup-java@99b8673ff64fbf99d8d325f52d9a5bdedb8483e9 # v4.2.1 + with: + distribution: 'temurin' + java-version: ${{ env.JAVA_VERSION }} + + - name: Configure Ruby + uses: ruby/setup-ruby@5f19ec79cedfadb78ab837f95b87734d0003c899 # v1.173.0 + with: + bundler-cache: true + ruby-version: ${{ env.RUBY_VERSION }} + + - name: Install Fastlane + run: | + gem install bundler:2.2.27 + bundle config path vendor/bundle + bundle install --jobs 4 --retry 3 + + - 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 ${{ github.workspace }}/secrets + mkdir -p ${{ github.workspace }}/keystores + + az storage blob download \ + --account-name $ACCOUNT_NAME \ + --container-name $CONTAINER_NAME \ + --name authenticator_apk-keystore.jks \ + --file ${{ github.workspace }}/keystores/authenticator_apk-keystore.jks \ + --output none + + az storage blob download \ + --account-name $ACCOUNT_NAME \ + --container-name $CONTAINER_NAME \ + --name authenticator_aab-keystore.jks \ + --file ${{ github.workspace }}/keystores/authenticator_aab-keystore.jks \ + --output none + + az storage blob download \ + --account-name $ACCOUNT_NAME \ + --container-name $CONTAINER_NAME \ + --name authenticator_play_firebase-creds.json \ + --file ${{ github.workspace }}/secrets/authenticator_play_firebase-creds.json \ + --output none + shell: bash + + - name: Set build version + env: + FIREBASE_CREDS_PATH: ${{ github.workspace }}/secrets/authenticator_play_firebase-creds.json + run: | + bundle exec fastlane setBuildVersionInfo \ + serviceCredentialsFile:${{ env.FIREBASE_CREDS_PATH }} + shell: bash + + - name: Assemble Release APK + run: | + bundle exec fastlane buildRelease \ + storeFile:${{ github.workspace }}/keystores/authenticator_apk-keystore.jks \ + storePassword:'${{ secrets.APK_KEYSTORE_STORE_PASSWORD }}' \ + keyAlias:bitwardenauthenticator \ + keyPassword:'${{ secrets.APK_KEYSTORE_KEY_PASSWORD }}' + shell: bash + + - name: Create checksum file for Release APK + run: | + sha256sum "app/build/outputs/apk/release/com.bitwarden.authenticator-release.apk" \ + > ./authenticator-android-apk-sha256.txt + + - name: Upload release APK to GitHub + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + with: + name: com.bitwarden.authenticator.apk + path: app/build/outputs/apk/release/com.bitwarden.authenticator-release.apk + if-no-files-found: error + + - name: Upload checksum file for Release .apk + uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + with: + name: authenticator-android-apk-sha256.txt + path: ./authenticator-android-apk-sha256.txt + if-no-files-found: error + + - name: Install Firebase App Distribution plugin + run: bundle exec fastlane add_plugin firebase_app_distribution + + - name: Publish release APK to Firebase + env: + FIREBASE_CREDS_PATH: ${{ github.workspace }}/secrets/authenticator_play_firebase-creds.json + run: | + bundle exec fastlane distributeReleaseToFirebase \ + serviceCredentialFile:${{ env.FIREBASE_CREDS_PATH }} + shell: bash diff --git a/Gemfile b/Gemfile index 60beb163c..3a6536094 100644 --- a/Gemfile +++ b/Gemfile @@ -2,3 +2,6 @@ source "https://rubygems.org" ruby '3.2.2' gem "fastlane", "2.219.0" + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock index 0a3d459bd..a019bf070 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -109,6 +109,9 @@ GEM xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) + fastlane-plugin-firebase_app_distribution (0.9.0) + google-apis-firebaseappdistribution_v1 (~> 0.3.0) + google-apis-firebaseappdistribution_v1alpha (~> 0.2.0) gh_inspector (1.1.3) google-apis-androidpublisher_v3 (0.54.0) google-apis-core (>= 0.11.0, < 2.a) @@ -120,6 +123,10 @@ GEM representable (~> 3.0) retriable (>= 2.0, < 4.a) rexml + google-apis-firebaseappdistribution_v1 (0.3.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-firebaseappdistribution_v1alpha (0.2.0) + google-apis-core (>= 0.11.0, < 2.a) google-apis-iamcredentials_v1 (0.17.0) google-apis-core (>= 0.11.0, < 2.a) google-apis-playcustomapp_v1 (0.13.0) @@ -213,6 +220,7 @@ PLATFORMS DEPENDENCIES fastlane (= 2.219.0) + fastlane-plugin-firebase_app_distribution RUBY VERSION ruby 3.2.2p53 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index be96b560d..5b24d747d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,6 @@ plugins { alias(libs.plugins.android.application) - alias(libs.plugins.crashlytics) +// alias(libs.plugins.crashlytics) alias(libs.plugins.hilt) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.parcelize) @@ -23,6 +23,8 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + setProperty("archivesBaseName", "com.bitwarden.authenticator") + ksp { // The location in which the generated Room Database Schemas will be stored in the repo. arg("room.schemaLocation", "$projectDir/schemas") @@ -36,7 +38,7 @@ android { buildTypes { release { - isMinifyEnabled = false + isMinifyEnabled = true proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" diff --git a/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt b/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt index 71943ac61..a68fc29f0 100644 --- a/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt +++ b/app/src/main/kotlin/com/bitwarden/authenticator/ui/authenticator/feature/itemlisting/ItemListingScreen.kt @@ -277,7 +277,9 @@ fun ItemListingScreen( ItemListingState.ViewState.Loading, -> { EmptyItemListingContent( - onAddCodeClick = onNavigateToQrCodeScanner, + onAddCodeClick = remember(viewModel) { + { launcher.launch(Manifest.permission.CAMERA) } + }, onSyncWithBitwardenClick = onNavigateToSyncWithBitwardenScreen, onImportItemsClick = onNavigateToImportScreen, ) diff --git a/fastlane/Appfile b/fastlane/Appfile index e66cfbaf9..92fef5f92 100644 --- a/fastlane/Appfile +++ b/fastlane/Appfile @@ -1,2 +1,2 @@ -json_key_file("") # Path to the json secret file - Follow https://docs.fastlane.tools/actions/supply/#setup to get one -package_name("com.bitwarden.authenticator") # e.g. com.krausefx.app +json_key_file("secrets/authenticator_play_firebase-creds.json") # Path to the json secret file +package_name("com.bitwarden.authenticator") diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 5a6e18e0c..7d91d46a9 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -16,8 +16,92 @@ default_platform(:android) platform :android do - desc "Runs all the tests" + + desc "Runs tests" lane :check do gradle(task: "check") end + + desc "Apply build version information" + fastlane_require "time" + lane :setBuildVersionInfo do |options| + + latestRelease = firebase_app_distribution_get_latest_release( + app: "1:867301491091:android:50b626dba42a361651e866", + service_credentials_file:options[:serviceCredentialsFile] + ) + + currentVersionName = latestRelease[:displayVersion] + + # Gather current version information + versionParts = currentVersionName.split(".") + currentMajor = versionParts[0] + currentMinor = versionParts[1] + currentRevision = versionParts[2] + + # Current date used to derive next version name. + currentDate = Time.new + major = currentDate.year.to_s + minor = currentDate.strftime "%m" + + # Determine the next revision number to apply. + revision = 0 + if currentMajor == major and currentMinor == minor + revision = currentRevision.to_i + 1 + end + + # Cache next version information + nextVersionName = major.to_s + "." + minor.to_s + "." + revision.to_s + nextVersionCode = latestRelease[:buildVersion].to_i + 1 + + # Read in app build config file. + buildConfigPath = "../app/build.gradle.kts" + buildConfigFile = File.open(buildConfigPath) + buildConfigText = buildConfigFile.read + buildConfigFile.close + + # Replace version information. + puts "Setting version code to #{nextVersionCode}." + buildConfigText.gsub!("versionCode = 1", "versionCode = #{nextVersionCode}") + puts "Setting version name to #{nextVersionName}." + buildConfigText.gsub!("versionName = \"#{currentVersionName}\"", "versionName = \"#{nextVersionName}\"") + + # Save changes + File.open(buildConfigPath, "w") { |buildConfigFile| buildConfigFile << buildConfigText } + end + + desc "Assemble debug variants" + lane :buildDebug do + gradle( + task: "assemble", + build_type: "Debug", + print_command: false, + ) + end + + desc "Assemble and sign release APK" + lane :buildRelease do |options| + gradle( + task: "assemble", + build_type: "Release", + properties: { + "android.injected.signing.store.file" => options[:storeFile], + "android.injected.signing.store.password" => options[:storePassword], + "android.injected.signing.key.alias" => options[:keyAlias], + "android.injected.signing.key.password" => options[:keyPassword] + }, + print_command: false, + ) + end + + desc "Publish release to Firebase" + lane :distributeReleasePlayStoreToFirebase do |options| + firebase_app_distribution( + app: "1:867301491091:android:50b626dba42a361651e866", + android_artifact_type: "APK", + android_artifact_path: "app/build/outputs/apk/release/com.bitwarden.authenticator-release.apk", + service_credentials_file: options[:serviceCredentialsFile], + groups: "internal-prod-group", + ) + end end diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile new file mode 100644 index 000000000..b18539bc9 --- /dev/null +++ b/fastlane/Pluginfile @@ -0,0 +1,5 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-firebase_app_distribution'