From 8999f93501cdffa1f72ac408708f3a004c29bbed Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Wed, 18 May 2022 16:41:34 +0200 Subject: [PATCH 01/10] Support universal APK as input as well --- .../android/android_send_app_size_metrics.rb | 65 +++++-- spec/android_send_app_size_metrics_spec.rb | 170 ++++++++++-------- 2 files changed, 141 insertions(+), 94 deletions(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb index 3dbec910f..3b5da0c0a 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb @@ -10,6 +10,9 @@ def self.run(params) if (api_token.nil? || api_token.empty?) && !api_url.is_a?(URI::File) UI.user_error!('An API token is required when using an `api_url` with a scheme other than `file://`') end + if params[:aab_path].nil? && params[:universal_apk_path].nil? + UI.user_error!('You must provide at least an `aab_path` or an `universal_apk_path`, or both') + end # Build the payload base metrics_helper = Fastlane::Helper::AppSizeMetricsHelper.new( @@ -21,28 +24,32 @@ def self.run(params) 'Build Type': params[:build_type], Source: params[:source] ) - metrics_helper.add_metric(name: 'AAB File Size', value: File.size(params[:aab_path])) + # Add AAB file size + metrics_helper.add_metric(name: 'AAB File Size', value: File.size(params[:aab_path])) unless params[:aab_path].nil? + # Add Universal APK file size + metrics_helper.add_metric(name: 'Universal APK File Size', value: File.size(params[:universal_apk_path])) unless params[:universal_apk_path].nil? - # Add device-specific 'splits' metrics to the payload if a `:include_split_sizes` is enabled + # Add optimized file and download sizes for each split `.apk` metrics to the payload if a `:include_split_sizes` is enabled if params[:include_split_sizes] - check_bundletool_installed! apkanalyzer_bin = params[:apkanalyzer_binary] || find_apkanalyzer_binary! - UI.message("[App Size Metrics] Generating the various APK splits from #{params[:aab_path]}...") - Dir.mktmpdir('release-toolkit-android-app-size-metrics') do |tmp_dir| - Action.sh('bundletool', 'build-apks', '--bundle', params[:aab_path], '--output-format', 'DIRECTORY', '--output', tmp_dir) - apks = Dir.glob('splits/*.apk', base: tmp_dir).map { |f| File.join(tmp_dir, f) } - UI.message("[App Size Metrics] Generated #{apks.length} APKs.") - - apks.each do |apk| - UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...") - split_name = File.basename(apk, '.apk') - file_size = Action.sh(apkanalyzer_bin, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i - download_size = Action.sh(apkanalyzer_bin, 'apk', 'download-size', apk, print_command: false, print_command_output: false).chomp.to_i - metrics_helper.add_metric(name: 'APK File Size', value: file_size, metadata: { split: split_name }) - metrics_helper.add_metric(name: 'Download Size', value: download_size, metadata: { split: split_name }) + unless params[:aab_path].nil? + check_bundletool_installed! + UI.message("[App Size Metrics] Generating the various APK splits from #{params[:aab_path]}...") + Dir.mktmpdir('release-toolkit-android-app-size-metrics') do |tmp_dir| + Action.sh('bundletool', 'build-apks', '--bundle', params[:aab_path], '--output-format', 'DIRECTORY', '--output', tmp_dir) + apks = Dir.glob('splits/*.apk', base: tmp_dir).map { |f| File.join(tmp_dir, f) } + UI.message("[App Size Metrics] Generated #{apks.length} APKs.") + + apks.each do |apk| + split_name = File.basename(apk, '.apk') + add_apk_size_metrics(helper: metrics_helper, apkanalyzer_bin: apkanalyzer_bin, apk: apk, split_name: split_name) + end + + UI.message('[App Size Metrics] Done computing splits sizes.') end - - UI.message('[App Size Metrics] Done computing splits sizes.') + end + unless params[:universal_apk_path].nil? + add_apk_size_metrics(helper: metrics_helper, apkanalyzer_bin: apkanalyzer_bin, apk: params[:universal_apk_path], split_name: 'Universal') end end @@ -76,6 +83,14 @@ def self.find_apkanalyzer_binary! apkanalyzer_bin end + def self.add_apk_size_metrics(helper:, apkanalyzer_bin:, apk:, split_name:) + UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...") + file_size = Action.sh(apkanalyzer_bin, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i + download_size = Action.sh(apkanalyzer_bin, 'apk', 'download-size', apk, print_command: false, print_command_output: false).chomp.to_i + helper.add_metric(name: 'APK File Size', value: file_size, metadata: { split: split_name }) + helper.add_metric(name: 'Download Size', value: download_size, metadata: { split: split_name }) + end + ##################################################### # @!group Documentation ##################################################### @@ -95,6 +110,7 @@ def self.details DETAILS end + # rubocop:disable Metrics/MethodLength def self.available_options [ FastlaneCore::ConfigItem.new( @@ -165,7 +181,7 @@ def self.available_options env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_AAB_PATH', description: 'The path to the .aab to extract size information from', type: String, - optional: false, + optional: true, # We can have `aab_path` only, or `universal_apk_path` only, or both (but not none) verify_block: proc do |value| UI.user_error!('You must provide an path to an existing `.aab` file') unless File.exist?(value) end @@ -178,6 +194,16 @@ def self.available_options type: FastlaneCore::Boolean, default_value: true ), + FastlaneCore::ConfigItem.new( + key: :universal_apk_path, + env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_UNIVERSAL_APK_PATH', + description: 'The path to the Universal .apk to extract size information from', + type: String, + optional: true, # We can have `aab_path` only, or `universal_apk_path` only, or both (but not none) + verify_block: proc do |value| + UI.user_error!('You must provide an path to an existing `.apk` file') unless File.exist?(value) + end + ), FastlaneCore::ConfigItem.new( key: :apkanalyzer_binary, env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_APKANALYZER_BINARY', @@ -191,6 +217,7 @@ def self.available_options ), ] end + # rubocop:enable Metrics/MethodLength def self.return_type :integer diff --git a/spec/android_send_app_size_metrics_spec.rb b/spec/android_send_app_size_metrics_spec.rb index 9a51b8a58..efadb404e 100644 --- a/spec/android_send_app_size_metrics_spec.rb +++ b/spec/android_send_app_size_metrics_spec.rb @@ -51,87 +51,107 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_ end context 'when `include_split_sizes` is turned off' do - it 'generates the expected payload compressed by default' do - expected = { - meta: [ - { name: 'Platform', value: 'Android' }, - { name: 'App Name', value: 'my-app' }, - { name: 'App Version', value: '10.2-rc-3' }, - { name: 'Product Flavor', value: 'Vanilla' }, - { name: 'Build Type', value: 'Release' }, - { name: 'Source', value: 'unit-test' }, - ], - metrics: [ - { name: 'AAB File Size', value: 123_456 }, - ] - } - - test_app_size_action( - fake_aab_size: 123_456, - fake_apks: {}, - expected_payload: expected, - app_name: 'my-app', - app_version_name: '10.2-rc-3', - product_flavor: 'Vanilla', - build_type: 'Release', - source: 'unit-test', - include_split_sizes: false - ) + context 'when only providing an `aab_path`' do + it 'generates the expected payload compressed by default' do + expected = { + meta: [ + { name: 'Platform', value: 'Android' }, + { name: 'App Name', value: 'my-app' }, + { name: 'App Version', value: '10.2-rc-3' }, + { name: 'Product Flavor', value: 'Vanilla' }, + { name: 'Build Type', value: 'Release' }, + { name: 'Source', value: 'unit-test' }, + ], + metrics: [ + { name: 'AAB File Size', value: 123_456 }, + ] + } + + test_app_size_action( + fake_aab_size: 123_456, + fake_apks: {}, + expected_payload: expected, + app_name: 'my-app', + app_version_name: '10.2-rc-3', + product_flavor: 'Vanilla', + build_type: 'Release', + source: 'unit-test', + include_split_sizes: false + ) + end + + it 'generates the expected payload uncompressed when disabling gzip' do + expected = { + meta: [ + { name: 'Platform', value: 'Android' }, + { name: 'App Name', value: 'my-app' }, + { name: 'App Version', value: '10.2-rc-3' }, + { name: 'Product Flavor', value: 'Vanilla' }, + { name: 'Build Type', value: 'Release' }, + { name: 'Source', value: 'unit-test' }, + ], + metrics: [ + { name: 'AAB File Size', value: 123_456 }, + ] + } + + test_app_size_action( + fake_aab_size: 123_456, + fake_apks: {}, + expected_payload: expected, + app_name: 'my-app', + app_version_name: '10.2-rc-3', + product_flavor: 'Vanilla', + build_type: 'Release', + source: 'unit-test', + include_split_sizes: false, + use_gzip_content_encoding: false + ) + end end - it 'generates the expected payload uncompressed when disabling gzip' do - expected = { - meta: [ - { name: 'Platform', value: 'Android' }, - { name: 'App Name', value: 'my-app' }, - { name: 'App Version', value: '10.2-rc-3' }, - { name: 'Product Flavor', value: 'Vanilla' }, - { name: 'Build Type', value: 'Release' }, - { name: 'Source', value: 'unit-test' }, - ], - metrics: [ - { name: 'AAB File Size', value: 123_456 }, - ] - } - - test_app_size_action( - fake_aab_size: 123_456, - fake_apks: {}, - expected_payload: expected, - app_name: 'my-app', - app_version_name: '10.2-rc-3', - product_flavor: 'Vanilla', - build_type: 'Release', - source: 'unit-test', - include_split_sizes: false, - use_gzip_content_encoding: false - ) + context 'when only providing an `universal_apk_path`' do + it 'generates the expected payload containing the apk file size' + end + + context 'when providing both an `aab_path` and an `universal_apk_path`' do + it 'generates the expected payload containing the aab and universal apk file size' end end context 'when keeping the default value of `include_split_sizes` turned on' do - it 'generates the expected payload containing the aab file size and optimized split sizes' do - expected_fixture = File.join(test_data_dir, 'android-metrics-payload.json') - expected = JSON.parse(File.read(expected_fixture)) - - test_app_size_action( - fake_aab_size: 987_654_321, - fake_apks: { - 'base-arm64_v8a.apk': [164_080, 64_080], - 'base-arm64_v8a_2.apk': [164_082, 64_082], - 'base-armeabi.apk': [150_000, 50_000], - 'base-armeabi_2.apk': [150_002, 50_002], - 'base-armeabi_v7a.apk': [150_070, 50_070], - 'base-armeabi_v7a_2.apk': [150_072, 50_072] - }, - expected_payload: expected, - app_name: 'wordpress', - app_version_name: '19.8-rc-3', - app_version_code: 1214, - product_flavor: 'Vanilla', - build_type: 'Release', - source: 'unit-test' - ) + context 'when only providing an `aab_path`' do + it 'generates the expected payload containing the aab file size and optimized split sizes' do + expected_fixture = File.join(test_data_dir, 'android-metrics-payload.json') + expected = JSON.parse(File.read(expected_fixture)) + + test_app_size_action( + fake_aab_size: 987_654_321, + fake_apks: { + 'base-arm64_v8a.apk': [164_080, 64_080], + 'base-arm64_v8a_2.apk': [164_082, 64_082], + 'base-armeabi.apk': [150_000, 50_000], + 'base-armeabi_2.apk': [150_002, 50_002], + 'base-armeabi_v7a.apk': [150_070, 50_070], + 'base-armeabi_v7a_2.apk': [150_072, 50_072] + }, + expected_payload: expected, + app_name: 'wordpress', + app_version_name: '19.8-rc-3', + app_version_code: 1214, + product_flavor: 'Vanilla', + build_type: 'Release', + source: 'unit-test' + ) + end + end + + context 'when only providing an `universal_apk_path`' do + it 'generates the expected payload containing the apk file size and optimized file and download sizes' + end + + context 'when providing both an `aab_path` and an `universal_apk_path`' do + it 'generates the expected payload containing the aab and universal apk file size and optimized file and download sizes for all splits' end end end From 4e7a304b6652612df4a8b6b2e27d1881693ecb55 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 19 May 2022 19:16:38 +0200 Subject: [PATCH 02/10] Android: Extract payload key names as constants --- .../android/android_send_app_size_metrics.rb | 17 ++++++++++++----- .../android-metrics-payload.json | 12 ++++++------ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb index 3b5da0c0a..91b5a67f0 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb @@ -3,6 +3,13 @@ module Fastlane module Actions class AndroidSendAppSizeMetricsAction < Action + # Keys used by the metrics payload + AAB_FILE_SIZE_KEY = 'AAB File Size'.freeze # value from `File.size` of the `.aab` + UNIVERSAL_APK_FILE_SIZE_KEY = 'Universal APK File Size'.freeze # value from `File.size` of the Universal `.apk` + UNIVERSAL_APK_SPLIT_NAME = 'Universal'.freeze # pseudo-name of the split representing the Universal `.apk` + APK_OPTIMIZED_FILE_SIZE_KEY = 'Optimized APK File Size'.freeze # value from `apkanalyzer apk file-size` + APK_OPTIMIZED_DOWNLOAD_SIZE_KEY = 'Download Size'.freeze # value from `apkanalyzer apk download-size` + def self.run(params) # Check input parameters api_url = URI(params[:api_url]) @@ -25,9 +32,9 @@ def self.run(params) Source: params[:source] ) # Add AAB file size - metrics_helper.add_metric(name: 'AAB File Size', value: File.size(params[:aab_path])) unless params[:aab_path].nil? + metrics_helper.add_metric(name: AAB_FILE_SIZE_KEY, value: File.size(params[:aab_path])) unless params[:aab_path].nil? # Add Universal APK file size - metrics_helper.add_metric(name: 'Universal APK File Size', value: File.size(params[:universal_apk_path])) unless params[:universal_apk_path].nil? + metrics_helper.add_metric(name: UNIVERSAL_APK_FILE_SIZE_KEY, value: File.size(params[:universal_apk_path])) unless params[:universal_apk_path].nil? # Add optimized file and download sizes for each split `.apk` metrics to the payload if a `:include_split_sizes` is enabled if params[:include_split_sizes] @@ -49,7 +56,7 @@ def self.run(params) end end unless params[:universal_apk_path].nil? - add_apk_size_metrics(helper: metrics_helper, apkanalyzer_bin: apkanalyzer_bin, apk: params[:universal_apk_path], split_name: 'Universal') + add_apk_size_metrics(helper: metrics_helper, apkanalyzer_bin: apkanalyzer_bin, apk: params[:universal_apk_path], split_name: UNIVERSAL_APK_SPLIT_NAME) end end @@ -87,8 +94,8 @@ def self.add_apk_size_metrics(helper:, apkanalyzer_bin:, apk:, split_name:) UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...") file_size = Action.sh(apkanalyzer_bin, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i download_size = Action.sh(apkanalyzer_bin, 'apk', 'download-size', apk, print_command: false, print_command_output: false).chomp.to_i - helper.add_metric(name: 'APK File Size', value: file_size, metadata: { split: split_name }) - helper.add_metric(name: 'Download Size', value: download_size, metadata: { split: split_name }) + helper.add_metric(name: APK_OPTIMIZED_FILE_SIZE_KEY, value: file_size, metadata: { split: split_name }) + helper.add_metric(name: APK_OPTIMIZED_DOWNLOAD_SIZE_KEY, value: download_size, metadata: { split: split_name }) end ##################################################### diff --git a/spec/test-data/app_size_metrics/android-metrics-payload.json b/spec/test-data/app_size_metrics/android-metrics-payload.json index f77014b5f..e7f366942 100644 --- a/spec/test-data/app_size_metrics/android-metrics-payload.json +++ b/spec/test-data/app_size_metrics/android-metrics-payload.json @@ -10,17 +10,17 @@ ], "metrics": [ { "name": "AAB File Size", "value": 987654321 }, - { "name": "APK File Size", "value": 164082, "meta": [{ "name": "split", "value": "base-arm64_v8a_2" }] }, + { "name": "Optimized APK File Size", "value": 164082, "meta": [{ "name": "split", "value": "base-arm64_v8a_2" }] }, { "name": "Download Size", "value": 64082, "meta": [{ "name": "split", "value": "base-arm64_v8a_2" }] }, - { "name": "APK File Size", "value": 150072, "meta": [{ "name": "split", "value": "base-armeabi_v7a_2" }] }, + { "name": "Optimized APK File Size", "value": 150072, "meta": [{ "name": "split", "value": "base-armeabi_v7a_2" }] }, { "name": "Download Size", "value": 50072, "meta": [{ "name": "split", "value": "base-armeabi_v7a_2" }] }, - { "name": "APK File Size", "value": 150002, "meta": [{ "name": "split", "value": "base-armeabi_2" }] }, + { "name": "Optimized APK File Size", "value": 150002, "meta": [{ "name": "split", "value": "base-armeabi_2" }] }, { "name": "Download Size", "value": 50002, "meta": [{ "name": "split", "value": "base-armeabi_2" }] }, - { "name": "APK File Size", "value": 164080, "meta": [{ "name": "split", "value": "base-arm64_v8a" }] }, + { "name": "Optimized APK File Size", "value": 164080, "meta": [{ "name": "split", "value": "base-arm64_v8a" }] }, { "name": "Download Size", "value": 64080, "meta": [{ "name": "split", "value": "base-arm64_v8a" }] }, - { "name": "APK File Size", "value": 150000, "meta": [{ "name": "split", "value": "base-armeabi" }] }, + { "name": "Optimized APK File Size", "value": 150000, "meta": [{ "name": "split", "value": "base-armeabi" }] }, { "name": "Download Size", "value": 50000, "meta": [{ "name": "split", "value": "base-armeabi" }] }, - { "name": "APK File Size", "value": 150070, "meta": [{ "name": "split", "value": "base-armeabi_v7a" }] }, + { "name": "Optimized APK File Size", "value": 150070, "meta": [{ "name": "split", "value": "base-armeabi_v7a" }] }, { "name": "Download Size", "value": 50070, "meta": [{ "name": "split", "value": "base-armeabi_v7a" }] } ] } From 0d592981893e1b0bd648dbe4fecb0106bbf14c0a Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 19 May 2022 19:34:58 +0200 Subject: [PATCH 03/10] Android: Extract some code in helper method to make it more readable --- .../android/android_send_app_size_metrics.rb | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb index 91b5a67f0..f6f552599 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb @@ -40,19 +40,9 @@ def self.run(params) if params[:include_split_sizes] apkanalyzer_bin = params[:apkanalyzer_binary] || find_apkanalyzer_binary! unless params[:aab_path].nil? - check_bundletool_installed! - UI.message("[App Size Metrics] Generating the various APK splits from #{params[:aab_path]}...") - Dir.mktmpdir('release-toolkit-android-app-size-metrics') do |tmp_dir| - Action.sh('bundletool', 'build-apks', '--bundle', params[:aab_path], '--output-format', 'DIRECTORY', '--output', tmp_dir) - apks = Dir.glob('splits/*.apk', base: tmp_dir).map { |f| File.join(tmp_dir, f) } - UI.message("[App Size Metrics] Generated #{apks.length} APKs.") - - apks.each do |apk| - split_name = File.basename(apk, '.apk') - add_apk_size_metrics(helper: metrics_helper, apkanalyzer_bin: apkanalyzer_bin, apk: apk, split_name: split_name) - end - - UI.message('[App Size Metrics] Done computing splits sizes.') + generate_split_apks(aab_path: params[:aab_path]) do |apk| + split_name = File.basename(apk, '.apk') + add_apk_size_metrics(helper: metrics_helper, apkanalyzer_bin: apkanalyzer_bin, apk: apk, split_name: split_name) end end unless params[:universal_apk_path].nil? @@ -68,34 +58,49 @@ def self.run(params) ) end - def self.check_bundletool_installed! - Action.sh('command', '-v', 'bundletool', print_command: false, print_command_output: false) - rescue StandardError - UI.user_error!('bundletool is required to build the split APKs. Install it with `brew install bundletool`') - raise - end + # @!group Small helper methods + class << self + def check_bundletool_installed! + Action.sh('command', '-v', 'bundletool', print_command: false, print_command_output: false) + rescue StandardError + UI.user_error!('bundletool is required to build the split APKs. Install it with `brew install bundletool`') + raise + end - def self.find_apkanalyzer_binary - sdk_root = ENV['ANDROID_SDK_ROOT'] || ENV['ANDROID_HOME'] - if sdk_root - pattern = File.join(sdk_root, 'cmdline-tools', '{latest,tools}', 'bin', 'apkanalyzer') - apkanalyzer_bin = Dir.glob(pattern).find { |path| File.executable?(path) } + def find_apkanalyzer_binary + sdk_root = ENV['ANDROID_SDK_ROOT'] || ENV['ANDROID_HOME'] + if sdk_root + pattern = File.join(sdk_root, 'cmdline-tools', '{latest,tools}', 'bin', 'apkanalyzer') + apkanalyzer_bin = Dir.glob(pattern).find { |path| File.executable?(path) } + end + apkanalyzer_bin || Action.sh('command', '-v', 'apkanalyzer', print_command_output: false) { |_| nil } end - apkanalyzer_bin || Action.sh('command', '-v', 'apkanalyzer', print_command_output: false) { |_| nil } - end - def self.find_apkanalyzer_binary! - apkanalyzer_bin = find_apkanalyzer_binary - UI.user_error!('Unable to find `apkanalyzer` executable in `$PATH` nor `$ANDROID_SDK_ROOT`. Make sure you installed the Android SDK Command-line Tools') if apkanalyzer_bin.nil? - apkanalyzer_bin - end + def find_apkanalyzer_binary! + apkanalyzer_bin = find_apkanalyzer_binary + UI.user_error!('Unable to find `apkanalyzer` executable in `$PATH` nor `$ANDROID_SDK_ROOT`. Make sure you installed the Android SDK Command-line Tools') if apkanalyzer_bin.nil? + apkanalyzer_bin + end + + def add_apk_size_metrics(helper:, apkanalyzer_bin:, apk:, split_name:) + UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...") + file_size = Action.sh(apkanalyzer_bin, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i + download_size = Action.sh(apkanalyzer_bin, 'apk', 'download-size', apk, print_command: false, print_command_output: false).chomp.to_i + helper.add_metric(name: APK_OPTIMIZED_FILE_SIZE_KEY, value: file_size, metadata: { split: split_name }) + helper.add_metric(name: APK_OPTIMIZED_DOWNLOAD_SIZE_KEY, value: download_size, metadata: { split: split_name }) + end - def self.add_apk_size_metrics(helper:, apkanalyzer_bin:, apk:, split_name:) - UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...") - file_size = Action.sh(apkanalyzer_bin, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i - download_size = Action.sh(apkanalyzer_bin, 'apk', 'download-size', apk, print_command: false, print_command_output: false).chomp.to_i - helper.add_metric(name: APK_OPTIMIZED_FILE_SIZE_KEY, value: file_size, metadata: { split: split_name }) - helper.add_metric(name: APK_OPTIMIZED_DOWNLOAD_SIZE_KEY, value: download_size, metadata: { split: split_name }) + def generate_split_apks(aab_path:, &block) + check_bundletool_installed! + UI.message("[App Size Metrics] Generating the various APK splits from #{aab_path}...") + Dir.mktmpdir('release-toolkit-android-app-size-metrics') do |tmp_dir| + Action.sh('bundletool', 'build-apks', '--bundle', aab_path, '--output-format', 'DIRECTORY', '--output', tmp_dir) + apks = Dir.glob('splits/*.apk', base: tmp_dir).map { |f| File.join(tmp_dir, f) } + UI.message("[App Size Metrics] Generated #{apks.length} APKs.") + apks.each(&block) + UI.message('[App Size Metrics] Done computing splits sizes.') + end + end end ##################################################### From 5a4063615cf584aab3790ef9f14b5d275cf15de2 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 19 May 2022 19:23:09 +0200 Subject: [PATCH 04/10] iOS: Extract payload key names as constants --- .../actions/ios/ios_send_app_size_metrics.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb index b01f54908..fa3ca3bc6 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/ios/ios_send_app_size_metrics.rb @@ -4,6 +4,11 @@ module Fastlane module Actions class IosSendAppSizeMetricsAction < Action + # Keys used by the metrics payload + IPA_FILE_SIZE_KEY = 'File Size'.freeze # value from `File.size` of the Universal `.ipa` + IPA_DOWNLOAD_SIZE_KEY = 'Download Size'.freeze # value from `app-thinning.plist` + IPA_INSTALL_SIZE_KEY = 'Install Size'.freeze # value from `app-thinning.plist` + def self.run(params) # Check input parameters api_url = URI(params[:api_url]) @@ -20,7 +25,7 @@ def self.run(params) 'Build Type': params[:build_type], Source: params[:source] ) - metrics_helper.add_metric(name: 'File Size', value: File.size(params[:ipa_path])) + metrics_helper.add_metric(name: IPA_FILE_SIZE_KEY, value: File.size(params[:ipa_path])) # Add app-thinning metrics to the payload if a `.plist` is provided app_thinning_plist_path = params[:app_thinning_plist_path] || File.join(File.dirname(params[:ipa_path]), 'app-thinning.plist') @@ -30,8 +35,8 @@ def self.run(params) variant_descriptors = variant['variantDescriptors'] || [{ 'device' => 'Universal' }] variant_descriptors.each do |desc| variant_metadata = { device: desc['device'], 'OS Version': desc['os-version'] } - metrics_helper.add_metric(name: 'Download Size', value: variant['sizeCompressedApp'], metadata: variant_metadata) - metrics_helper.add_metric(name: 'Install Size', value: variant['sizeUncompressedApp'], metadata: variant_metadata) + metrics_helper.add_metric(name: IPA_DOWNLOAD_SIZE_KEY, value: variant['sizeCompressedApp'], metadata: variant_metadata) + metrics_helper.add_metric(name: IPA_INSTALL_SIZE_KEY, value: variant['sizeUncompressedApp'], metadata: variant_metadata) end end end From 071f9855a370c8b802fc3acfb306edd24530d9f2 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 19 May 2022 20:08:06 +0200 Subject: [PATCH 05/10] Android tests: add cases with/out Universal APK --- spec/android_send_app_size_metrics_spec.rb | 175 ++++++++++++++---- .../android-metrics-payload-aab+apk.json | 29 +++ ....json => android-metrics-payload-aab.json} | 0 .../android-metrics-payload-apk.json | 16 ++ 4 files changed, 183 insertions(+), 37 deletions(-) create mode 100644 spec/test-data/app_size_metrics/android-metrics-payload-aab+apk.json rename spec/test-data/app_size_metrics/{android-metrics-payload.json => android-metrics-payload-aab.json} (100%) create mode 100644 spec/test-data/app_size_metrics/android-metrics-payload-apk.json diff --git a/spec/android_send_app_size_metrics_spec.rb b/spec/android_send_app_size_metrics_spec.rb index efadb404e..eec6cb845 100644 --- a/spec/android_send_app_size_metrics_spec.rb +++ b/spec/android_send_app_size_metrics_spec.rb @@ -3,13 +3,22 @@ describe Fastlane::Actions::AndroidSendAppSizeMetricsAction do let(:test_data_dir) { File.join(File.dirname(__FILE__), 'test-data', 'app_size_metrics') } - def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_action_args) + def test_app_size_action(fake_aab_size:, fake_apks:, fake_universal_apk_sizes:, expected_payload:, **other_action_args) in_tmp_dir do |tmp_dir| # Arrange output_file = File.join(tmp_dir, 'output-payload') - aab_path = File.join(tmp_dir, 'fake.aab') - File.write(aab_path, '-fake-aab-file-') - allow(File).to receive(:size).with(aab_path).and_return(fake_aab_size) + aab_path = nil + unless fake_aab_size.nil? + aab_path = File.join(tmp_dir, 'fake.aab') + File.write(aab_path, '-fake-aab-file-') + allow(File).to receive(:size).with(aab_path).and_return(fake_aab_size) + end + universal_apk_path = nil + unless fake_universal_apk_sizes.empty? + universal_apk_path = File.join(tmp_dir, 'fake.apk') + File.write(universal_apk_path, '-fake-universal-apk-file-') + allow(File).to receive(:size).with(universal_apk_path).and_return(fake_universal_apk_sizes[0]) + end if other_action_args[:include_split_sizes] != false # Arrange: fake that apkanalyzer exists @@ -17,24 +26,31 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_ allow(described_class).to receive(:find_apkanalyzer_binary).and_return(apkanalyzer_bin) allow(File).to receive(:executable?).with(apkanalyzer_bin).and_return(true) - # Arrange: fake that bundletool exists and mock its call to create fake apks with corresponding apkanalyzer calls mocks - allow(Fastlane::Action).to receive(:sh).with('command', '-v', 'bundletool', anything) - allow(Fastlane::Action).to receive(:sh).with('bundletool', 'build-apks', '--bundle', aab_path, '--output-format', 'DIRECTORY', '--output', anything) do |*args| - bundletool_tmpdir = args.last - FileUtils.mkdir(File.join(bundletool_tmpdir, 'splits')) - fake_apks.each do |apk_name, sizes| - apk_path = File.join(bundletool_tmpdir, 'splits', apk_name.to_s) - File.write(apk_path, "Fake APK file (#{sizes})") - allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'file-size', apk_path, anything).and_return(sizes[0].to_s) - allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'download-size', apk_path, anything).and_return(sizes[1].to_s) + unless fake_apks.empty? + # Arrange: fake that `bundletool` exists and mock its call to create fake apks with corresponding apkanalyzer calls mocks + allow(Fastlane::Action).to receive(:sh).with('command', '-v', 'bundletool', anything) + allow(Fastlane::Action).to receive(:sh).with('bundletool', 'build-apks', '--bundle', aab_path, '--output-format', 'DIRECTORY', '--output', anything) do |*args| + bundletool_tmpdir = args.last + FileUtils.mkdir(File.join(bundletool_tmpdir, 'splits')) + fake_apks.each do |apk_name, sizes| + apk_path = File.join(bundletool_tmpdir, 'splits', apk_name.to_s) + File.write(apk_path, "Fake APK file (#{sizes})") + allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'file-size', apk_path, anything).and_return(sizes[0].to_s) + allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'download-size', apk_path, anything).and_return(sizes[1].to_s) + end end end + unless fake_universal_apk_sizes.empty? + allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'file-size', universal_apk_path, anything).and_return(fake_universal_apk_sizes[1].to_s) + allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'download-size', universal_apk_path, anything).and_return(fake_universal_apk_sizes[2].to_s) + end end # Act code = run_described_fastlane_action( api_url: File.join('file://localhost/', output_file), aab_path: aab_path, + universal_apk_path: universal_apk_path, **other_action_args ) @@ -51,6 +67,17 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_ end context 'when `include_split_sizes` is turned off' do + let(:common_action_args) do + { + app_name: 'my-app', + app_version_name: '10.2-rc-3', + product_flavor: 'Vanilla', + build_type: 'Release', + source: 'unit-test', + include_split_sizes: false + } + end + context 'when only providing an `aab_path`' do it 'generates the expected payload compressed by default' do expected = { @@ -70,13 +97,9 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_ test_app_size_action( fake_aab_size: 123_456, fake_apks: {}, + fake_universal_apk_sizes: [], expected_payload: expected, - app_name: 'my-app', - app_version_name: '10.2-rc-3', - product_flavor: 'Vanilla', - build_type: 'Release', - source: 'unit-test', - include_split_sizes: false + **common_action_args ) end @@ -92,37 +115,90 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_ ], metrics: [ { name: 'AAB File Size', value: 123_456 }, + { name: 'Universal APK File Size', value: 56_789 }, ] } test_app_size_action( fake_aab_size: 123_456, fake_apks: {}, + fake_universal_apk_sizes: [56_789], expected_payload: expected, - app_name: 'my-app', - app_version_name: '10.2-rc-3', - product_flavor: 'Vanilla', - build_type: 'Release', - source: 'unit-test', - include_split_sizes: false, + **common_action_args, use_gzip_content_encoding: false ) end end context 'when only providing an `universal_apk_path`' do - it 'generates the expected payload containing the apk file size' + it 'generates the expected payload containing the apk file size' do + expected = { + meta: [ + { name: 'Platform', value: 'Android' }, + { name: 'App Name', value: 'my-app' }, + { name: 'App Version', value: '10.2-rc-3' }, + { name: 'Product Flavor', value: 'Vanilla' }, + { name: 'Build Type', value: 'Release' }, + { name: 'Source', value: 'unit-test' }, + ], + metrics: [ + { name: 'Universal APK File Size', value: 567_654_321 }, + ] + } + + test_app_size_action( + fake_aab_size: nil, + fake_apks: {}, + fake_universal_apk_sizes: [567_654_321], + expected_payload: expected, + **common_action_args + ) + end end context 'when providing both an `aab_path` and an `universal_apk_path`' do - it 'generates the expected payload containing the aab and universal apk file size' + it 'generates the expected payload containing the aab and universal apk file size' do + expected = { + meta: [ + { name: 'Platform', value: 'Android' }, + { name: 'App Name', value: 'my-app' }, + { name: 'App Version', value: '10.2-rc-3' }, + { name: 'Product Flavor', value: 'Vanilla' }, + { name: 'Build Type', value: 'Release' }, + { name: 'Source', value: 'unit-test' }, + ], + metrics: [ + { name: 'AAB File Size', value: 123_456 }, + { name: 'Universal APK File Size', value: 567_654_321 }, + ] + } + + test_app_size_action( + fake_aab_size: 123_456, + fake_apks: {}, + fake_universal_apk_sizes: [567_654_321], + expected_payload: expected, + **common_action_args + ) + end end end context 'when keeping the default value of `include_split_sizes` turned on' do + let(:common_action_args) do + { + app_name: 'wordpress', + app_version_name: '19.8-rc-3', + app_version_code: 1214, + product_flavor: 'Vanilla', + build_type: 'Release', + source: 'unit-test' + } + end + context 'when only providing an `aab_path`' do it 'generates the expected payload containing the aab file size and optimized split sizes' do - expected_fixture = File.join(test_data_dir, 'android-metrics-payload.json') + expected_fixture = File.join(test_data_dir, 'android-metrics-payload-aab.json') expected = JSON.parse(File.read(expected_fixture)) test_app_size_action( @@ -135,23 +211,48 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_ 'base-armeabi_v7a.apk': [150_070, 50_070], 'base-armeabi_v7a_2.apk': [150_072, 50_072] }, + fake_universal_apk_sizes: [], expected_payload: expected, - app_name: 'wordpress', - app_version_name: '19.8-rc-3', - app_version_code: 1214, - product_flavor: 'Vanilla', - build_type: 'Release', - source: 'unit-test' + **common_action_args ) end end context 'when only providing an `universal_apk_path`' do - it 'generates the expected payload containing the apk file size and optimized file and download sizes' + it 'generates the expected payload containing the apk file size and optimized file and download sizes' do + expected_fixture = File.join(test_data_dir, 'android-metrics-payload-apk.json') + expected = JSON.parse(File.read(expected_fixture)) + + test_app_size_action( + fake_aab_size: nil, + fake_apks: {}, + fake_universal_apk_sizes: [567_654_321, 555_000, 533_000], + expected_payload: expected, + **common_action_args + ) + end end context 'when providing both an `aab_path` and an `universal_apk_path`' do - it 'generates the expected payload containing the aab and universal apk file size and optimized file and download sizes for all splits' + it 'generates the expected payload containing the aab and universal apk file size and optimized file and download sizes for all splits' do + expected_fixture = File.join(test_data_dir, 'android-metrics-payload-aab+apk.json') + expected = JSON.parse(File.read(expected_fixture)) + + test_app_size_action( + fake_aab_size: 987_654_321, + fake_apks: { + 'base-arm64_v8a.apk': [164_080, 64_080], + 'base-arm64_v8a_2.apk': [164_082, 64_082], + 'base-armeabi.apk': [150_000, 50_000], + 'base-armeabi_2.apk': [150_002, 50_002], + 'base-armeabi_v7a.apk': [150_070, 50_070], + 'base-armeabi_v7a_2.apk': [150_072, 50_072] + }, + fake_universal_apk_sizes: [567_654_321, 555_000, 533_000], + expected_payload: expected, + **common_action_args + ) + end end end end diff --git a/spec/test-data/app_size_metrics/android-metrics-payload-aab+apk.json b/spec/test-data/app_size_metrics/android-metrics-payload-aab+apk.json new file mode 100644 index 000000000..4fba63922 --- /dev/null +++ b/spec/test-data/app_size_metrics/android-metrics-payload-aab+apk.json @@ -0,0 +1,29 @@ +{ + "meta": [ + { "name": "Platform", "value": "Android" }, + { "name": "App Name", "value": "wordpress" }, + { "name": "App Version", "value": "19.8-rc-3" }, + { "name": "Version Code", "value": 1214 }, + { "name": "Product Flavor", "value": "Vanilla" }, + { "name": "Build Type", "value": "Release" }, + { "name": "Source", "value": "unit-test" } + ], + "metrics": [ + { "name": "AAB File Size", "value": 987654321 }, + { "name": "Universal APK File Size", "value": 567654321 }, + { "name": "Optimized APK File Size", "value": 164082, "meta": [{ "name": "split", "value": "base-arm64_v8a_2" }] }, + { "name": "Download Size", "value": 64082, "meta": [{ "name": "split", "value": "base-arm64_v8a_2" }] }, + { "name": "Optimized APK File Size", "value": 150072, "meta": [{ "name": "split", "value": "base-armeabi_v7a_2" }] }, + { "name": "Download Size", "value": 50072, "meta": [{ "name": "split", "value": "base-armeabi_v7a_2" }] }, + { "name": "Optimized APK File Size", "value": 150002, "meta": [{ "name": "split", "value": "base-armeabi_2" }] }, + { "name": "Download Size", "value": 50002, "meta": [{ "name": "split", "value": "base-armeabi_2" }] }, + { "name": "Optimized APK File Size", "value": 164080, "meta": [{ "name": "split", "value": "base-arm64_v8a" }] }, + { "name": "Download Size", "value": 64080, "meta": [{ "name": "split", "value": "base-arm64_v8a" }] }, + { "name": "Optimized APK File Size", "value": 150000, "meta": [{ "name": "split", "value": "base-armeabi" }] }, + { "name": "Download Size", "value": 50000, "meta": [{ "name": "split", "value": "base-armeabi" }] }, + { "name": "Optimized APK File Size", "value": 150070, "meta": [{ "name": "split", "value": "base-armeabi_v7a" }] }, + { "name": "Download Size", "value": 50070, "meta": [{ "name": "split", "value": "base-armeabi_v7a" }] }, + { "name": "Optimized APK File Size", "value": 555000, "meta": [{ "name": "split", "value": "Universal" }] }, + { "name": "Download Size", "value": 533000, "meta": [{ "name": "split", "value": "Universal" }] } + ] +} diff --git a/spec/test-data/app_size_metrics/android-metrics-payload.json b/spec/test-data/app_size_metrics/android-metrics-payload-aab.json similarity index 100% rename from spec/test-data/app_size_metrics/android-metrics-payload.json rename to spec/test-data/app_size_metrics/android-metrics-payload-aab.json diff --git a/spec/test-data/app_size_metrics/android-metrics-payload-apk.json b/spec/test-data/app_size_metrics/android-metrics-payload-apk.json new file mode 100644 index 000000000..1543ccb4a --- /dev/null +++ b/spec/test-data/app_size_metrics/android-metrics-payload-apk.json @@ -0,0 +1,16 @@ +{ + "meta": [ + { "name": "Platform", "value": "Android" }, + { "name": "App Name", "value": "wordpress" }, + { "name": "App Version", "value": "19.8-rc-3" }, + { "name": "Version Code", "value": 1214 }, + { "name": "Product Flavor", "value": "Vanilla" }, + { "name": "Build Type", "value": "Release" }, + { "name": "Source", "value": "unit-test" } + ], + "metrics": [ + { "name": "Universal APK File Size", "value": 567654321 }, + { "name": "Optimized APK File Size", "value": 555000, "meta": [{ "name": "split", "value": "Universal" }] }, + { "name": "Download Size", "value": 533000, "meta": [{ "name": "split", "value": "Universal" }] } + ] +} From ae152435fe8890be8c96b42a99ce8042cb4b4b76 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 19 May 2022 20:23:56 +0200 Subject: [PATCH 06/10] Remove nil values from the action call To use defaults instead of actually passing `nil` as a value --- spec/android_send_app_size_metrics_spec.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/spec/android_send_app_size_metrics_spec.rb b/spec/android_send_app_size_metrics_spec.rb index eec6cb845..49cd34c13 100644 --- a/spec/android_send_app_size_metrics_spec.rb +++ b/spec/android_send_app_size_metrics_spec.rb @@ -47,12 +47,13 @@ def test_app_size_action(fake_aab_size:, fake_apks:, fake_universal_apk_sizes:, end # Act - code = run_described_fastlane_action( + action_params = { api_url: File.join('file://localhost/', output_file), aab_path: aab_path, universal_apk_path: universal_apk_path, **other_action_args - ) + }.compact + code = run_described_fastlane_action(action_params) # Asserts expect(code).to eq(201) From 7b8888985fa13c06e33c9b2bbf320adfeb7c2a8a Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 19 May 2022 21:03:46 +0200 Subject: [PATCH 07/10] Add CHANGELOG entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 502dd22cc..ff6150b3f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ _None_ ### New Features -* Introduce new `ios_send_app_size_metrics` and `android_send_app_size_metrics` actions. [#364] +* Introduce new `ios_send_app_size_metrics` and `android_send_app_size_metrics` actions. [#364] [#365] ### Bug Fixes From 0c923e32791a7da91fb2ace352bc2424bdeeea05 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Tue, 24 May 2022 20:36:13 +0200 Subject: [PATCH 08/10] Add some YARD documentation to helper methods --- .../android/android_send_app_size_metrics.rb | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb index f6f552599..a383ffeaa 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb @@ -58,15 +58,24 @@ def self.run(params) ) end + ##################################################### # @!group Small helper methods + ##################################################### class << self + # @raise if `bundletool` can not be found in `$PATH` def check_bundletool_installed! Action.sh('command', '-v', 'bundletool', print_command: false, print_command_output: false) rescue StandardError - UI.user_error!('bundletool is required to build the split APKs. Install it with `brew install bundletool`') + UI.user_error!('`bundletool` is required to build the split APKs. Install it with `brew install bundletool`') raise end + # The path where the `apkanalyzer` binary was found, after searching it: + # - in priority in `$ANDROID_SDK_ROOT` (or `$ANDROID_HOME` for legacy setups), under `cmdline-tools/latest/bin/` or `cmdline-tools/tools/bin` + # - and falling back by trying to find it in `$PATH` + # + # @return [String,Nil] The path to `apkanalyzer`, or `nil` if it wasn't found in any of the above tested paths. + # def find_apkanalyzer_binary sdk_root = ENV['ANDROID_SDK_ROOT'] || ENV['ANDROID_HOME'] if sdk_root @@ -76,12 +85,26 @@ def find_apkanalyzer_binary apkanalyzer_bin || Action.sh('command', '-v', 'apkanalyzer', print_command_output: false) { |_| nil } end + # The path where the `apkanalyzer` binary was found, after searching it: + # - in priority in `$ANDROID_SDK_ROOT` (or `$ANDROID_HOME` for legacy setups), under `cmdline-tools/latest/bin/` or `cmdline-tools/tools/bin` + # - and falling back by trying to find it in `$PATH` + # + # @return [String] The path to `apkanalyzer` + # @raise [FastlaneCore::Interface::FastlaneError] if it wasn't found in any of the above tested paths. + # def find_apkanalyzer_binary! apkanalyzer_bin = find_apkanalyzer_binary UI.user_error!('Unable to find `apkanalyzer` executable in `$PATH` nor `$ANDROID_SDK_ROOT`. Make sure you installed the Android SDK Command-line Tools') if apkanalyzer_bin.nil? apkanalyzer_bin end + # Add the `file-size` and `download-size` values of an APK to the helper, as reported by the corresponding `apkanalyzer apk …` commands + # + # @param [Fastlane::Helper::AppSizeMetricsHelper] helper The helper to add the metrics to + # @param [String] apkanalyzer_bin The path to the `apkanalyzer` binary to use to extract those file and download sizes from the `.apk` + # @param [String] apk The path to the `.apk` file to extract the sizes from + # @param [String] split_name The name to use for the value of the `split` metadata key in the metrics being added + # def add_apk_size_metrics(helper:, apkanalyzer_bin:, apk:, split_name:) UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...") file_size = Action.sh(apkanalyzer_bin, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i @@ -90,6 +113,13 @@ def add_apk_size_metrics(helper:, apkanalyzer_bin:, apk:, split_name:) helper.add_metric(name: APK_OPTIMIZED_DOWNLOAD_SIZE_KEY, value: download_size, metadata: { split: split_name }) end + # Generates all the split `.apk` files (typically one per device architecture) from a given `.aab` file, then yield for each apk produced. + # + # @note The split `.apk` files are generated in a temporary directory and are thus all deleted after each of them has been `yield`ed to the provided block. + # @param [String] aab_path The path to the `.aab` file to generate split `.apk` files for + # @yield [apk] Calls the provided block once for each split `.apk` that was generated from the `.aab` + # @yieldparam apk [String] The path to one of the split `.apk` temporary file generated from the `.aab` + # def generate_split_apks(aab_path:, &block) check_bundletool_installed! UI.message("[App Size Metrics] Generating the various APK splits from #{aab_path}...") From 3ce195993d9832535dd3ece8ab0cc29ec369e983 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 26 May 2022 09:13:07 +0200 Subject: [PATCH 09/10] Apply suggestions from code review Co-authored-by: Gio Lodi --- .../actions/android/android_send_app_size_metrics.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb index a383ffeaa..84c7a2793 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb @@ -239,11 +239,11 @@ def self.available_options FastlaneCore::ConfigItem.new( key: :universal_apk_path, env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_UNIVERSAL_APK_PATH', - description: 'The path to the Universal .apk to extract size information from', + description: 'The path to the Universal `.apk` to extract size information from', type: String, optional: true, # We can have `aab_path` only, or `universal_apk_path` only, or both (but not none) verify_block: proc do |value| - UI.user_error!('You must provide an path to an existing `.apk` file') unless File.exist?(value) + UI.user_error!('You must provide a path to an existing `.apk` file') unless File.exist?(value) end ), FastlaneCore::ConfigItem.new( From 4a15e071a39236f58228ab395aea71d76e7bc7b5 Mon Sep 17 00:00:00 2001 From: Olivier Halligon Date: Thu, 26 May 2022 13:17:40 +0200 Subject: [PATCH 10/10] Update lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb Co-authored-by: Gio Lodi --- .../actions/android/android_send_app_size_metrics.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb index 84c7a2793..d5925c67a 100644 --- a/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb +++ b/lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb @@ -94,7 +94,7 @@ def find_apkanalyzer_binary # def find_apkanalyzer_binary! apkanalyzer_bin = find_apkanalyzer_binary - UI.user_error!('Unable to find `apkanalyzer` executable in `$PATH` nor `$ANDROID_SDK_ROOT`. Make sure you installed the Android SDK Command-line Tools') if apkanalyzer_bin.nil? + UI.user_error!('Unable to find `apkanalyzer` executable in either `$PATH` or `$ANDROID_SDK_ROOT`. Make sure you installed the Android SDK Command-line Tools') if apkanalyzer_bin.nil? apkanalyzer_bin end