Skip to content

Commit 4649c7a

Browse files
committed
Implement android_send_app_size_metrics
1 parent 9907614 commit 4649c7a

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
require_relative '../../models/app_size_metrics_payload'
2+
3+
module Fastlane
4+
module Actions
5+
class AndroidSendAppSizeMetricsAction < Action
6+
def self.run(params)
7+
# Check input parameters
8+
base_url = URI(params[:api_base_url])
9+
api_token = params[:api_token]
10+
if (api_token.nil? || api_token.empty?) && !base_url.is_a?(URI::File)
11+
UI.user_error!('An API token is required when using an `api_base_url` with a scheme other than `file://`')
12+
end
13+
14+
# Build the payload base
15+
payload = Fastlane::WPMRT::AppSizeMetricsPayload.new(
16+
'Platform': 'Android',
17+
'App Name': params[:app_name],
18+
'App Version': params[:app_version_name],
19+
'Version Code': params[:app_version_code],
20+
'Build Type': params[:build_type],
21+
'Product Flavor': params[:product_flavor],
22+
'Source': params[:source],
23+
)
24+
payload.add_metric(name: 'File Size', value: File.size(params[:aab_path]), meta: { split: 'AAB' })
25+
26+
if params[:include_split_sizes]
27+
check_bundletool_installed!
28+
apkanalyzer_bin = find_apkanalyzer_binary!
29+
Dir.mktmpdir('release-toolkit-android-app-size-metrics') do |tmp_dir|
30+
UI.message("[App Size Metrics] Generating the various APK splits from #{params[:aab_path]}...")
31+
Action.sh('bundletool', 'build-apks', '--bundle', params[:aab_path], '--output-format', 'DIRECTORY', '--output', tmp_dir)
32+
apks = Dir.glob('splits/*.apk', base: tmp_dir).map { |f| File.join(tmp_dir, f) }
33+
UI.message("[App Size Metrics] Generated #{apks.length} APKs.")
34+
apks.each do |apk|
35+
split_name = File.basename(apk, '.apk').delete_prefix('base-')
36+
UI.message("[App Size Metrics] Computing file and download size of #{File.basename(apk)}...")
37+
file_size = Action.sh(apkanalyzer_bin, 'apk', 'file-size', apk, print_command: false, print_command_output: false).chomp.to_i
38+
download_size = Action.sh(apkanalyzer_bin, 'apk', 'download-size', apk, print_command: false, print_command_output: false).chomp.to_i
39+
payload.add_metric(name: 'File Size', value: file_size, meta: { split: split_name })
40+
payload.add_metric(name: 'Download Size', value: download_size, meta: { split: split_name })
41+
end
42+
UI.message('[App Size Metrics] Done computing splits sizes.')
43+
end
44+
end
45+
46+
# Send the payload
47+
payload.send_metrics(
48+
base_url: base_url,
49+
api_token: api_token,
50+
use_gzip: params[:use_gzip_content_encoding]
51+
)
52+
end
53+
54+
def self.check_bundletool_installed!
55+
begin
56+
Action.sh('command', '-v', 'bundletool', print_command: false, print_command_output: false)
57+
rescue
58+
UI.user_error!('bundletool is required to build the split APKs. Install it with `brew install bundletool`')
59+
raise
60+
end
61+
end
62+
63+
def self.find_apkanalyzer_binary!
64+
sdk_root = ENV['ANDROID_SDK_ROOT'] || ENV['ANDROID_HOME']
65+
apkanalyzer_bin = sdk_root.nil? ? Action.sh('command', '-v', 'apkanalyzer') : File.join(sdk_root, 'cmdline-tools', 'latest', 'bin', 'apkanalyzer')
66+
UI.user_error('Unable to find apkanalyzer executable. Make sure you installed the Android SDK Command-line Tools') unless File.executable?(apkanalyzer_bin)
67+
apkanalyzer_bin
68+
end
69+
70+
#####################################################
71+
# @!group Documentation
72+
#####################################################
73+
74+
def self.description
75+
'Send Android app size metrics to our metrics server'
76+
end
77+
78+
def self.details
79+
<<~DETAILS
80+
Send Android app size metrics to our metrics server.
81+
82+
See https://github.com/Automattic/apps-metrics for the API contract expected by the Metrics server you will send those metrics to.
83+
84+
Tip: If you provide a `file://` URL for the `api_base_url`, the action will write the payload on disk at the specified path
85+
instead of sending the data to a endpoint over network. This can be very useful to inspect the data sent and debug without requiring a
86+
DETAILS
87+
end
88+
89+
def self.available_options
90+
[
91+
FastlaneCore::ConfigItem.new(
92+
key: :api_base_url,
93+
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_API_BASE_URL',
94+
description: 'The endpoint API URL to publish metrics to. (Note: you can also point to a `file://` URL to write the payload to a file instead)',
95+
type: String,
96+
optional: false
97+
),
98+
FastlaneCore::ConfigItem.new(
99+
key: :api_token,
100+
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_API_TOKEN',
101+
description: 'The bearer token to call the API. Required unless `api_base_url` is a `file://` URL',
102+
type: String,
103+
optional: true
104+
),
105+
FastlaneCore::ConfigItem.new(
106+
key: :use_gzip_content_encoding,
107+
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_USE_GZIP_CONTENT_ENCODING',
108+
description: 'Specify that we should use `Content-Encoding: gzip` and gzipped body when sending the request',
109+
type: FastlaneCore::Boolean,
110+
default_value: true
111+
),
112+
FastlaneCore::ConfigItem.new(
113+
key: :app_name,
114+
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_APP_NAME',
115+
description: 'The name of the app for which we are publishing metrics, to help filter by app in the dashboard',
116+
type: String,
117+
optional: false
118+
),
119+
FastlaneCore::ConfigItem.new(
120+
key: :app_version_name,
121+
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_APP_VERSION_NAME',
122+
description: 'The version name of the app for which we are publishing metrics, to help filter by app in the dashboard',
123+
type: String,
124+
optional: false
125+
),
126+
FastlaneCore::ConfigItem.new(
127+
key: :app_version_code,
128+
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_APP_VERSION_CODE',
129+
description: 'The version code of the app for which we are publishing metrics, to help filter by app in the dashboard',
130+
type: Integer,
131+
optional: true
132+
),
133+
FastlaneCore::ConfigItem.new(
134+
key: :build_type,
135+
env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_BUILD_TYPE',
136+
description: 'The build configuration for which we are publishing metrics, to help filter by app in the dashboard. E.g. `Debug`, `Release`',
137+
type: String,
138+
optional: true
139+
),
140+
FastlaneCore::ConfigItem.new(
141+
key: :product_flavor,
142+
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_PRODUCT_FLAVOR',
143+
description: 'The build flavor for which we are publishing metrics, to help filter by app in the dashboard. E.g. `Vanilla`, `beta`, `release`',
144+
type: String,
145+
optional: true
146+
),
147+
FastlaneCore::ConfigItem.new(
148+
key: :source,
149+
env_name: 'FL_IOS_SEND_APP_SIZE_METRICS_SOURCE',
150+
description: 'The type of build at the origin of that build, to help filter by app in the dashboard. E.g. `pr`, `beta`, `final-release`',
151+
type: String,
152+
optional: true
153+
),
154+
FastlaneCore::ConfigItem.new(
155+
key: :aab_path,
156+
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_AAB_PATH',
157+
description: 'The path to the .ipa to extract size information from',
158+
type: String,
159+
optional: false,
160+
#default_value: Actions.lane_context[SharedValues::IPA_OUTPUT_PATH],
161+
verify_block: proc do |value|
162+
UI.user_error!('You must provide an path to an existing `.aab` file') unless File.exist?(value)
163+
end
164+
),
165+
FastlaneCore::ConfigItem.new(
166+
key: :include_split_sizes,
167+
env_name: 'FL_ANDROID_SEND_APP_SIZE_METRICS_COMPUTE_SPLIT_SIZES',
168+
description: 'Indicate if we should use `bundletool` and `apkanalyzer` to also compute and send split apk sizes per architecture. ' \
169+
+ 'Setting this to `true` adds a bit of extra time to generate the data, but provides more detailed metrics',
170+
type: FastlaneCore::Boolean,
171+
default_value: true
172+
),
173+
]
174+
end
175+
176+
def self.return_type
177+
:integer
178+
end
179+
180+
def self.return_value
181+
'The HTTP return code from the call'
182+
end
183+
184+
def self.authors
185+
['automattic']
186+
end
187+
188+
def self.is_supported?(platform)
189+
platform == :android
190+
end
191+
end
192+
end
193+
end

0 commit comments

Comments
 (0)