Skip to content

Instantly share code, notes, and snippets.

@bastionkid
Created June 18, 2023 06:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bastionkid/44c34a670915b0a8ab6e269066220fd5 to your computer and use it in GitHub Desktop.
Save bastionkid/44c34a670915b0a8ab6e269066220fd5 to your computer and use it in GitHub Desktop.
Python script to parse the json report generated by Ruler plugin
import sys
import json
# read json file
def read_report_file(file_path):
with open(file_path, 'r') as file:
json_data = json.load(file)
return json_data
# generate dictionary of the grouped contents of an apk file
def get_apk_components(json_data):
components = {}
for component in json_data['components']:
for file in component['files']:
if file['name'].startswith('/lib/'):
update_if_present(components, 'Native libraries (arm64-v8a)', file['downloadSize'])
elif file['name'].startswith('/resources.arsc') or file['name'].startswith('/res/'):
update_if_present(components, 'Resources', file['downloadSize'])
elif file['name'].startswith('/assets/'):
update_if_present(components, 'Assets', file['downloadSize'])
elif file['name'].startswith('/'):
update_if_present(components, 'Others', file['downloadSize'])
else:
update_if_present(components, 'Classes', file['downloadSize'])
return components
# add/update the value (i.e. size) based on the presence of provided key
def update_if_present(components, key, value):
if key in components:
components[key] = components[key] + value
else:
components[key] = value
# generate html file containing size diff in HRF
def generate_size_diff_html():
html = "<html>"
html += "<body><h1>Ruler Size Diff Report</h1><h3>Affected Products</h3>"
html += "<ul><li><h4><code>release</code></h4><table>"
html += f"<tr><th>Component</th><th>Base ({apk_1_sha})</th><th>Merge ({apk_2_sha})</th><th>Diff</th></tr>"
# print diff of each components of both of the apk files
for component in set(components_1.keys()) | set(components_2.keys()):
size_1 = components_1.get(component, 0)
size_2 = components_2.get(component, 0)
html += f"<tr><td>{component}</td><td>{format_size(size_1)}</td><td>{format_size(size_2)}</td><td>{format_size_with_indicator(size_2 - size_1)}</td></tr>"
# calculate size of the apk files
apk_1_download_size = apk_1_json['downloadSize']
apk_2_download_size = apk_2_json['downloadSize']
html += f"<tr><td>apk (Download Size)</td><td>{format_size(apk_1_download_size)}</td><td>{format_size(apk_2_download_size)}</td><td>{format_size_with_indicator(apk_2_download_size - apk_1_download_size)}</td></tr>"
html += "</li></ul></table></body></html>"
with open("ruler_diff_report.html", "w") as file:
file.write(html)
# format bytes to KB or MB. Any size less than a KB is treated as 0KB
def format_size(size):
if abs(size) > mb_in_bytes:
return f"{round(size / mb_in_bytes, 2)} MB"
elif abs(size) > kb_in_bytes:
return f"{round(size / kb_in_bytes, 2)} KB"
else:
return "0 KB"
# add an indicator to highlight the size diff
def format_size_with_indicator(size):
size_indicator = "🔴" if size > kb_in_bytes else "🟢"
return f"{format_size(size)} {size_indicator}"
# read arguments passed to this script
apk_1_sha = sys.argv[1]
apk_2_sha = sys.argv[2]
kb_in_bytes = 1024
mb_in_bytes = 1024 * 1024
apk_1_json = read_report_file(f"{apk_1_sha}.json")
apk_2_json = read_report_file(f"{apk_2_sha}.json")
# generate dictionaries for the apk components size
components_1 = get_apk_components(apk_1_json)
components_2 = get_apk_components(apk_2_json)
generate_size_diff_html()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment