Skip to content

Commit a36b67b

Browse files
committed
Support universal APK as input as well
1 parent e8acd9d commit a36b67b

File tree

2 files changed

+149
-103
lines changed

2 files changed

+149
-103
lines changed

lib/fastlane/plugin/wpmreleasetoolkit/actions/android/android_send_app_size_metrics.rb

Lines changed: 50 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ def self.run(params)
1010
if (api_token.nil? || api_token.empty?) && !api_url.is_a?(URI::File)
1111
UI.user_error!('An API token is required when using an `api_url` with a scheme other than `file://`')
1212
end
13+
if params[:aab_path].nil? && params[:universal_apk_path].nil?
14+
UI.user_error!('You must provide at least an `aab_path` or an `universal_apk_path`, or both')
15+
end
1316

1417
# Build the payload base
1518
metrics_helper = Fastlane::Helper::AppSizeMetricsHelper.new(
@@ -21,28 +24,31 @@ def self.run(params)
2124
'Build Type': params[:build_type],
2225
Source: params[:source]
2326
)
24-
metrics_helper.add_metric(name: 'AAB File Size', value: File.size(params[:aab_path]))
27+
# Add AAB file size
28+
metrics_helper.add_metric(name: 'AAB File Size', value: File.size(params[:aab_path])) unless params[:aab_path].nil?
29+
# Add Universal APK file size
30+
metrics_helper.add_metric(name: 'Universal APK File Size', value: File.size(params[:universal_apk_path])) unless params[:universal_apk_path].nil?
2531

26-
# Add device-specific 'splits' metrics to the payload if a `:include_split_sizes` is enabled
32+
# Add optimized file and download sizes for each split `.apk` metrics to the payload if a `:include_split_sizes` is enabled
2733
if params[:include_split_sizes]
28-
check_bundletool_installed!
29-
apkanalyzer_bin = find_apkanalyzer_binary!
30-
UI.message("[App Size Metrics] Generating the various APK splits from #{params[:aab_path]}...")
31-
Dir.mktmpdir('release-toolkit-android-app-size-metrics') do |tmp_dir|
32-
Action.sh('bundletool', 'build-apks', '--bundle', params[:aab_path], '--output-format', 'DIRECTORY', '--output', tmp_dir)
33-
apks = Dir.glob('splits/*.apk', base: tmp_dir).map { |f| File.join(tmp_dir, f) }
34-
UI.message("[App Size Metrics] Generated #{apks.length} APKs.")
35-
36-
apks.each do |apk|
37-
UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...")
38-
split_name = File.basename(apk, '.apk')
39-
file_size = Action.sh(apkanalyzer_bin, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i
40-
download_size = Action.sh(apkanalyzer_bin, 'apk', 'download-size', apk, print_command: false, print_command_output: false).chomp.to_i
41-
metrics_helper.add_metric(name: 'APK File Size', value: file_size, metadata: { split: split_name })
42-
metrics_helper.add_metric(name: 'Download Size', value: download_size, metadata: { split: split_name })
34+
unless params[:aab_path].nil?
35+
check_bundletool_installed!
36+
UI.message("[App Size Metrics] Generating the various APK splits from #{params[:aab_path]}...")
37+
Dir.mktmpdir('release-toolkit-android-app-size-metrics') do |tmp_dir|
38+
Action.sh('bundletool', 'build-apks', '--bundle', params[:aab_path], '--output-format', 'DIRECTORY', '--output', tmp_dir)
39+
apks = Dir.glob('splits/*.apk', base: tmp_dir).map { |f| File.join(tmp_dir, f) }
40+
UI.message("[App Size Metrics] Generated #{apks.length} APKs.")
41+
42+
apks.each do |apk|
43+
split_name = File.basename(apk, '.apk')
44+
add_apk_size_metrics(helper: metrics_helper, apk: apk, split_name: split_name)
45+
end
46+
47+
UI.message('[App Size Metrics] Done computing splits sizes.')
4348
end
44-
45-
UI.message('[App Size Metrics] Done computing splits sizes.')
49+
end
50+
unless params[:universal_apk_path].nil?
51+
add_apk_size_metrics(helper: metrics_helper, apk: params[:universal_apk_path], split_name: 'Universal')
4652
end
4753
end
4854

@@ -61,13 +67,24 @@ def self.check_bundletool_installed!
6167
raise
6268
end
6369

64-
def self.find_apkanalyzer_binary!
70+
def self.apkanalyzer_binary!
71+
Action.sh('command', '-v', 'apkanalyzer')
72+
rescue StandardError
6573
sdk_root = ENV['ANDROID_SDK_ROOT'] || ENV['ANDROID_HOME']
66-
apkanalyzer_bin = sdk_root.nil? ? Action.sh('command', '-v', 'apkanalyzer') : File.join(sdk_root, 'cmdline-tools', 'latest', 'bin', 'apkanalyzer')
67-
UI.user_error!('Unable to find apkanalyzer executable. Make sure you installed the Android SDK Command-line Tools') unless File.executable?(apkanalyzer_bin)
74+
UI.user_error!('Unable to find ANDROID_SDK_ROOT / ANDROID_HOME') if sdk_root.nil?
75+
apkanalyzer_bin = File.join(sdk_root, 'cmdline-tools', 'latest', 'bin', 'apkanalyzer')
76+
UI.user_error!('Unable to find `apkanalyzer` executable. Make sure you installed the Android SDK Command-line Tools') unless File.executable?(apkanalyzer_bin)
6877
apkanalyzer_bin
6978
end
7079

80+
def self.add_apk_size_metrics(helper:, apk:, split_name:)
81+
UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...")
82+
file_size = Action.sh(apkanalyzer_binary!, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i
83+
download_size = Action.sh(apkanalyzer_binary!, 'apk', 'download-size', apk, print_command: false, print_command_output: false).chomp.to_i
84+
helper.add_metric(name: 'APK File Size', value: file_size, metadata: { split: split_name })
85+
helper.add_metric(name: 'Download Size', value: download_size, metadata: { split: split_name })
86+
end
87+
7188
#####################################################
7289
# @!group Documentation
7390
#####################################################
@@ -157,7 +174,7 @@ def self.available_options
157174
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_AAB_PATH',
158175
description: 'The path to the .aab to extract size information from',
159176
type: String,
160-
optional: false,
177+
optional: true, # We can have `aab_path` only, or `universal_apk_path` only, or both (but not none)
161178
verify_block: proc do |value|
162179
UI.user_error!('You must provide an path to an existing `.aab` file') unless File.exist?(value)
163180
end
@@ -170,6 +187,16 @@ def self.available_options
170187
type: FastlaneCore::Boolean,
171188
default_value: true
172189
),
190+
FastlaneCore::ConfigItem.new(
191+
key: :universal_apk_path,
192+
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_UNIVERSAL_APK_PATH',
193+
description: 'The path to the Universal .apk to extract size information from',
194+
type: String,
195+
optional: true, # We can have `aab_path` only, or `universal_apk_path` only, or both (but not none)
196+
verify_block: proc do |value|
197+
UI.user_error!('You must provide an path to an existing `.apk` file') unless File.exist?(value)
198+
end
199+
),
173200
]
174201
end
175202

spec/android_send_app_size_metrics_spec.rb

Lines changed: 99 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_
1313

1414
if other_action_args[:include_split_sizes] != false
1515
# Arrange: fake that apkanalyzer exists
16-
ENV['ANDROID_SDK_ROOT'] = '__ANDROID_SDK_ROOT__FOR_TESTS__'
17-
apkanalyzer_bin = File.join('__ANDROID_SDK_ROOT__FOR_TESTS__', 'cmdline-tools', 'latest', 'bin', 'apkanalyzer')
18-
allow(File).to receive(:executable?).with(apkanalyzer_bin).and_return(true)
16+
fake_apkanalyzer_path = './fake_apkanalyzer_bin'
17+
allow(described_class).to receive(:apkanalyzer_binary!).and_return(fake_apkanalyzer_path)
1918

2019
# Arrange: fake that bundletool exists and mock its call to create fake apks with corresponding apkanalyzer calls mocks
2120
allow(Fastlane::Action).to receive(:sh).with('command', '-v', 'bundletool', anything)
@@ -25,8 +24,8 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_
2524
fake_apks.each do |apk_name, sizes|
2625
apk_path = File.join(bundletool_tmpdir, 'splits', apk_name.to_s)
2726
File.write(apk_path, "Fake APK file (#{sizes})")
28-
allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'file-size', apk_path, anything).and_return(sizes[0].to_s)
29-
allow(Fastlane::Action).to receive(:sh).with(apkanalyzer_bin, 'apk', 'download-size', apk_path, anything).and_return(sizes[1].to_s)
27+
allow(Fastlane::Action).to receive(:sh).with(fake_apkanalyzer_path, 'apk', 'file-size', apk_path, anything).and_return(sizes[0].to_s)
28+
allow(Fastlane::Action).to receive(:sh).with(fake_apkanalyzer_path, 'apk', 'download-size', apk_path, anything).and_return(sizes[1].to_s)
3029
end
3130
end
3231
end
@@ -51,87 +50,107 @@ def test_app_size_action(fake_aab_size:, fake_apks:, expected_payload:, **other_
5150
end
5251

5352
context 'when `include_split_sizes` is turned off' do
54-
it 'generates the expected payload compressed by default' do
55-
expected = {
56-
meta: [
57-
{ name: 'Platform', value: 'Android' },
58-
{ name: 'App Name', value: 'my-app' },
59-
{ name: 'App Version', value: '10.2-rc-3' },
60-
{ name: 'Product Flavor', value: 'Vanilla' },
61-
{ name: 'Build Type', value: 'Release' },
62-
{ name: 'Source', value: 'unit-test' },
63-
],
64-
metrics: [
65-
{ name: 'AAB File Size', value: 123_456 },
66-
]
67-
}
68-
69-
test_app_size_action(
70-
fake_aab_size: 123_456,
71-
fake_apks: {},
72-
expected_payload: expected,
73-
app_name: 'my-app',
74-
app_version_name: '10.2-rc-3',
75-
product_flavor: 'Vanilla',
76-
build_type: 'Release',
77-
source: 'unit-test',
78-
include_split_sizes: false
79-
)
53+
context 'when only providing an `aab_path`' do
54+
it 'generates the expected payload compressed by default' do
55+
expected = {
56+
meta: [
57+
{ name: 'Platform', value: 'Android' },
58+
{ name: 'App Name', value: 'my-app' },
59+
{ name: 'App Version', value: '10.2-rc-3' },
60+
{ name: 'Product Flavor', value: 'Vanilla' },
61+
{ name: 'Build Type', value: 'Release' },
62+
{ name: 'Source', value: 'unit-test' },
63+
],
64+
metrics: [
65+
{ name: 'AAB File Size', value: 123_456 },
66+
]
67+
}
68+
69+
test_app_size_action(
70+
fake_aab_size: 123_456,
71+
fake_apks: {},
72+
expected_payload: expected,
73+
app_name: 'my-app',
74+
app_version_name: '10.2-rc-3',
75+
product_flavor: 'Vanilla',
76+
build_type: 'Release',
77+
source: 'unit-test',
78+
include_split_sizes: false
79+
)
80+
end
81+
82+
it 'generates the expected payload uncompressed when disabling gzip' do
83+
expected = {
84+
meta: [
85+
{ name: 'Platform', value: 'Android' },
86+
{ name: 'App Name', value: 'my-app' },
87+
{ name: 'App Version', value: '10.2-rc-3' },
88+
{ name: 'Product Flavor', value: 'Vanilla' },
89+
{ name: 'Build Type', value: 'Release' },
90+
{ name: 'Source', value: 'unit-test' },
91+
],
92+
metrics: [
93+
{ name: 'AAB File Size', value: 123_456 },
94+
]
95+
}
96+
97+
test_app_size_action(
98+
fake_aab_size: 123_456,
99+
fake_apks: {},
100+
expected_payload: expected,
101+
app_name: 'my-app',
102+
app_version_name: '10.2-rc-3',
103+
product_flavor: 'Vanilla',
104+
build_type: 'Release',
105+
source: 'unit-test',
106+
include_split_sizes: false,
107+
use_gzip_content_encoding: false
108+
)
109+
end
80110
end
81111

82-
it 'generates the expected payload uncompressed when disabling gzip' do
83-
expected = {
84-
meta: [
85-
{ name: 'Platform', value: 'Android' },
86-
{ name: 'App Name', value: 'my-app' },
87-
{ name: 'App Version', value: '10.2-rc-3' },
88-
{ name: 'Product Flavor', value: 'Vanilla' },
89-
{ name: 'Build Type', value: 'Release' },
90-
{ name: 'Source', value: 'unit-test' },
91-
],
92-
metrics: [
93-
{ name: 'AAB File Size', value: 123_456 },
94-
]
95-
}
96-
97-
test_app_size_action(
98-
fake_aab_size: 123_456,
99-
fake_apks: {},
100-
expected_payload: expected,
101-
app_name: 'my-app',
102-
app_version_name: '10.2-rc-3',
103-
product_flavor: 'Vanilla',
104-
build_type: 'Release',
105-
source: 'unit-test',
106-
include_split_sizes: false,
107-
use_gzip_content_encoding: false
108-
)
112+
context 'when only providing an `universal_apk_path`' do
113+
it 'generates the expected payload containing the apk file size'
114+
end
115+
116+
context 'when providing both an `aab_path` and an `universal_apk_path`' do
117+
it 'generates the expected payload containing the aab and universal apk file size'
109118
end
110119
end
111120

112121
context 'when keeping the default value of `include_split_sizes` turned on' do
113-
it 'generates the expected payload containing the aab file size and optimized split sizes' do
114-
expected_fixture = File.join(test_data_dir, 'android-metrics-payload.json')
115-
expected = JSON.parse(File.read(expected_fixture))
116-
117-
test_app_size_action(
118-
fake_aab_size: 987_654_321,
119-
fake_apks: {
120-
'base-arm64_v8a.apk': [164_080, 64_080],
121-
'base-arm64_v8a_2.apk': [164_082, 64_082],
122-
'base-armeabi.apk': [150_000, 50_000],
123-
'base-armeabi_2.apk': [150_002, 50_002],
124-
'base-armeabi_v7a.apk': [150_070, 50_070],
125-
'base-armeabi_v7a_2.apk': [150_072, 50_072]
126-
},
127-
expected_payload: expected,
128-
app_name: 'wordpress',
129-
app_version_name: '19.8-rc-3',
130-
app_version_code: 1214,
131-
product_flavor: 'Vanilla',
132-
build_type: 'Release',
133-
source: 'unit-test'
134-
)
122+
context 'when only providing an `aab_path`' do
123+
it 'generates the expected payload containing the aab file size and optimized split sizes' do
124+
expected_fixture = File.join(test_data_dir, 'android-metrics-payload.json')
125+
expected = JSON.parse(File.read(expected_fixture))
126+
127+
test_app_size_action(
128+
fake_aab_size: 987_654_321,
129+
fake_apks: {
130+
'base-arm64_v8a.apk': [164_080, 64_080],
131+
'base-arm64_v8a_2.apk': [164_082, 64_082],
132+
'base-armeabi.apk': [150_000, 50_000],
133+
'base-armeabi_2.apk': [150_002, 50_002],
134+
'base-armeabi_v7a.apk': [150_070, 50_070],
135+
'base-armeabi_v7a_2.apk': [150_072, 50_072]
136+
},
137+
expected_payload: expected,
138+
app_name: 'wordpress',
139+
app_version_name: '19.8-rc-3',
140+
app_version_code: 1214,
141+
product_flavor: 'Vanilla',
142+
build_type: 'Release',
143+
source: 'unit-test'
144+
)
145+
end
146+
end
147+
148+
context 'when only providing an `universal_apk_path`' do
149+
it 'generates the expected payload containing the apk file size and optimized file and download sizes'
150+
end
151+
152+
context 'when providing both an `aab_path` and an `universal_apk_path`' do
153+
it 'generates the expected payload containing the aab and universal apk file size and optimized file and download sizes for all splits'
135154
end
136155
end
137156
end

0 commit comments

Comments
 (0)