Skip to content

adding size comparison #82

adding size comparison

adding size comparison #82

Workflow file for this run

name: Size Comparison
on:
push:
branches: ["main","sizes"]
paths-ignore:
- "**.md"
- "LICENSE"
pull_request:
branches: ["main"]
jobs:
windows:
name: windows-size-comparison
runs-on: windows-2019
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: "stable"
# Create and build base project
- name: Create base project
run: |
mkdir size_comparison
cd size_comparison
flutter create base_project
cd base_project
flutter build windows
$baseSize = (Get-ChildItem -Recurse "build\windows\x64\runner\Release\" | Measure-Object -Property Length -Sum).Sum / 1KB
echo "BASE_SIZE=$baseSize" | Out-File -FilePath $env:GITHUB_ENV -Append
# Build and measure opencv_core
- name: opencv_core size
run: |
cd packages/opencv_core/example
flutter build windows
$coreSize = (Get-ChildItem -Recurse "build\windows\x64\runner\Release\" | Measure-Object -Property Length -Sum).Sum / 1KB
echo "CORE_SIZE=$coreSize" | Out-File -FilePath $env:GITHUB_ENV -Append
# Build and measure opencv_dart
- name: opencv_dart size
run: |
cd packages/opencv_dart/example
flutter build windows
$dartSize = (Get-ChildItem -Recurse "build\windows\x64\runner\Release\" | Measure-Object -Property Length -Sum).Sum / 1KB
echo "DART_SIZE=$dartSize" | Out-File -FilePath $env:GITHUB_ENV -Append
# Generate size comparison JSON
- name: Generate size report
run: |
$report = @{
'platform' = 'windows'
'base_size' = $env:BASE_SIZE
'opencv_core' = @{
'total_size' = $env:CORE_SIZE
'package_size' = [int]$env:CORE_SIZE - [int]$env:BASE_SIZE
}
'opencv_dart' = @{
'total_size' = $env:DART_SIZE
'package_size' = [int]$env:DART_SIZE - [int]$env:BASE_SIZE
}
}
$report | ConvertTo-Json | Out-File windows_size_report.json
- name: Upload size report
uses: actions/upload-artifact@v4
with:
name: windows-size-report
path: windows_size_report.json
android:
name: android-size-comparison
runs-on: ubuntu-latest
steps:
- uses: subosito/flutter-action@v2
with:
channel: "stable"
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: "17"
# Create and build base project with split-per-abi
- name: Create base project (split-per-abi)
run: |
mkdir size_comparison
cd size_comparison
flutter create base_project
cd base_project
flutter build apk --release --target-platform android-arm64,android-arm,android-x64 --split-per-abi
BASE_SIZE_ARM64=$(($(stat -c%s "build/app/outputs/apk/release/app-arm64-v8a-release.apk" || echo "0") / 1024))
BASE_SIZE_ARMV7=$(($(stat -c%s "build/app/outputs/apk/release/app-armeabi-v7a-release.apk" || echo "0") / 1024))
BASE_SIZE_X64=$(($(stat -c%s "build/app/outputs/apk/release/app-x86_64-release.apk" || echo "0") / 1024))
echo "BASE_SIZE_ARM64=$BASE_SIZE_ARM64" >> $GITHUB_ENV
echo "BASE_SIZE_ARMV7=$BASE_SIZE_ARMV7" >> $GITHUB_ENV
echo "BASE_SIZE_X64=$BASE_SIZE_X64" >> $GITHUB_ENV
# Build full APK for base project (universal)
- name: Create base project (universal APK)
run: |
cd size_comparison/base_project
flutter build apk --release
BASE_FULL_SIZE=$(($(stat -c%s "build/app/outputs/apk/release/app-release.apk" || echo "0") / 1024))
echo "BASE_FULL_SIZE=$BASE_FULL_SIZE" >> $GITHUB_ENV
# Build and measure opencv_core with split-per-abi
- name: opencv_core size (split-per-abi)
run: |
cd packages/opencv_core/example
flutter build apk --release --target-platform android-arm64,android-arm,android-x64 --split-per-abi
CORE_SIZE_ARM64=$(($(stat -c%s "build/app/outputs/apk/release/app-arm64-v8a-release.apk" || echo "0") / 1024))
CORE_SIZE_ARMV7=$(($(stat -c%s "build/app/outputs/apk/release/app-armeabi-v7a-release.apk" || echo "0") / 1024))
CORE_SIZE_X64=$(($(stat -c%s "build/app/outputs/apk/release/app-x86_64-release.apk" || echo "0") / 1024))
echo "CORE_SIZE_ARM64=$CORE_SIZE_ARM64" >> $GITHUB_ENV
echo "CORE_SIZE_ARMV7=$CORE_SIZE_ARMV7" >> $GITHUB_ENV
echo "CORE_SIZE_X64=$CORE_SIZE_X64" >> $GITHUB_ENV
# Build full APK for opencv_core (universal)
- name: opencv_core full apk size
run: |
cd packages/opencv_core/example
flutter build apk --release
CORE_FULL_SIZE=$(($(stat -c%s "build/app/outputs/apk/release/app-release.apk" || echo "0") / 1024))
echo "CORE_FULL_SIZE=$CORE_FULL_SIZE" >> $GITHUB_ENV
# Build and measure opencv_dart with split-per-abi
- name: opencv_dart size (split-per-abi)
run: |
cd packages/opencv_dart/example
flutter build apk --release --target-platform android-arm64,android-arm,android-x64 --split-per-abi
DART_SIZE_ARM64=$(($(stat -c%s "build/app/outputs/apk/release/app-arm64-v8a-release.apk" || echo "0") / 1024))
DART_SIZE_ARMV7=$(($(stat -c%s "build/app/outputs/apk/release/app-armeabi-v7a-release.apk" || echo "0") / 1024))
DART_SIZE_X64=$(($(stat -c%s "build/app/outputs/apk/release/app-x86_64-release.apk" || echo "0") / 1024))
echo "DART_SIZE_ARM64=$DART_SIZE_ARM64" >> $GITHUB_ENV
echo "DART_SIZE_ARMV7=$DART_SIZE_ARMV7" >> $GITHUB_ENV
echo "DART_SIZE_X64=$DART_SIZE_X64" >> $GITHUB_ENV
# Build full APK for opencv_dart (universal)
- name: opencv_dart full apk size
run: |
cd packages/opencv_dart/example
flutter build apk --release
DART_FULL_SIZE=$(($(stat -c%s "build/app/outputs/apk/release/app-release.apk" || echo "0") / 1024))
echo "DART_FULL_SIZE=$DART_FULL_SIZE" >> $GITHUB_ENV
# Generate size comparison JSON
- name: Generate size report
run: |
cat > android_size_report.json << EOL
{
"platform": "android",
"base_size": {
"arm64-v8a": ${BASE_SIZE_ARM64:-0},
"armeabi-v7a": ${BASE_SIZE_ARMV7:-0},
"x86_64": ${BASE_SIZE_X64:-0},
"full_apk_size": ${BASE_FULL_SIZE:-0}
},
"opencv_core": {
"arm64-v8a": {
"total_size": ${CORE_SIZE_ARM64:-0},
"package_size": $(( ${CORE_SIZE_ARM64:-0} - ${BASE_SIZE_ARM64:-0} ))
},
"armeabi-v7a": {
"total_size": ${CORE_SIZE_ARMV7:-0},
"package_size": $(( ${CORE_SIZE_ARMV7:-0} - ${BASE_SIZE_ARMV7:-0} ))
},
"x86_64": {
"total_size": ${CORE_SIZE_X64:-0},
"package_size": $(( ${CORE_SIZE_X64:-0} - ${BASE_SIZE_X64:-0} ))
},
"full_apk_size": {
"total_size": ${CORE_FULL_SIZE:-0},
"package_size": $(( ${CORE_FULL_SIZE:-0} - ${BASE_FULL_SIZE:-0} ))
}
},
"opencv_dart": {
"arm64-v8a": {
"total_size": ${DART_SIZE_ARM64:-0},
"package_size": $(( ${DART_SIZE_ARM64:-0} - ${BASE_SIZE_ARM64:-0} ))
},
"armeabi-v7a": {
"total_size": ${DART_SIZE_ARMV7:-0},
"package_size": $(( ${DART_SIZE_ARMV7:-0} - ${BASE_SIZE_ARMV7:-0} ))
},
"x86_64": {
"total_size": ${DART_SIZE_X64:-0},
"package_size": $(( ${DART_SIZE_X64:-0} - ${BASE_SIZE_X64:-0} ))
},
"full_apk_size": {
"total_size": ${DART_FULL_SIZE:-0},
"package_size": $(( ${DART_FULL_SIZE:-0} - ${BASE_FULL_SIZE:-0} ))
}
}
}
EOL
- name: Upload size report
uses: actions/upload-artifact@v4
with:
name: android-size-report
path: android_size_report.json
linux:
name: linux-size-comparison
runs-on: ubuntu-latest
steps:
- name: Setup dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y curl git unzip xz-utils zip libglu1-mesa
sudo apt-get install clang cmake git \
ninja-build pkg-config \
libgtk-3-dev liblzma-dev \
libstdc++-12-dev
- uses: subosito/flutter-action@v2
with:
channel: "stable"
- uses: actions/checkout@v4
# Create and build base project
- name: Create base project
run: |
mkdir size_comparison
cd size_comparison
flutter create base_project
cd base_project
flutter build linux
BASE_SIZE=$(du -sk "build/linux/x64/release/bundle" | cut -f1)
echo "BASE_SIZE=$BASE_SIZE" >> $GITHUB_ENV
# Build and measure opencv_core
- name: opencv_core size
run: |
cd packages/opencv_core/example
flutter build linux
CORE_SIZE=$(du -sk "build/linux/x64/release/bundle" | cut -f1)
echo "CORE_SIZE=$CORE_SIZE" >> $GITHUB_ENV
# Build and measure opencv_dart
- name: opencv_dart size
run: |
cd packages/opencv_dart/example
flutter build linux
DART_SIZE=$(du -sk "build/linux/x64/release/bundle" | cut -f1)
echo "DART_SIZE=$DART_SIZE" >> $GITHUB_ENV
# Generate size comparison JSON
- name: Generate size report
run: |
base_size=${BASE_SIZE:-0}
core_size=${CORE_SIZE:-0}
dart_size=${DART_SIZE:-0}
core_diff=$((core_size - base_size))
dart_diff=$((dart_size - base_size))
cat > linux_size_report.json << EOL
{
"platform": "linux",
"base_size": $base_size,
"opencv_core": {
"total_size": $core_size,
"package_size": $core_diff
},
"opencv_dart": {
"total_size": $dart_size,
"package_size": $dart_diff
}
}
EOL
- name: Upload size report
uses: actions/upload-artifact@v4
with:
name: linux-size-report
path: linux_size_report.json
ios:
name: ios-size-comparison
runs-on: macos-14
steps:
- uses: subosito/flutter-action@v2
with:
channel: "stable"
- uses: actions/checkout@v4
# Create and build base project
- name: Create base project
run: |
mkdir size_comparison
cd size_comparison
flutter create base_project
cd base_project
flutter build ios --release --no-codesign
BASE_SIZE=$(du -sk "build/ios/iphoneos/Runner.app" | cut -f1)
echo "BASE_SIZE=$BASE_SIZE" >> $GITHUB_ENV
# Build and measure opencv_core
- name: opencv_core size
run: |
cd packages/opencv_core/example
flutter build ios --release --no-codesign
CORE_SIZE=$(du -sk "build/ios/iphoneos/Runner.app" | cut -f1)
echo "CORE_SIZE=$CORE_SIZE" >> $GITHUB_ENV
# Build and measure opencv_dart
- name: opencv_dart size
run: |
cd packages/opencv_dart/example
flutter build ios --release --no-codesign
DART_SIZE=$(du -sk "build/ios/iphoneos/Runner.app" | cut -f1)
echo "DART_SIZE=$DART_SIZE" >> $GITHUB_ENV
# Generate size comparison JSON
- name: Generate size report
run: |
base_size=${BASE_SIZE:-0}
core_size=${CORE_SIZE:-0}
dart_size=${DART_SIZE:-0}
core_diff=$((core_size - base_size))
dart_diff=$((dart_size - base_size))
cat > ios_size_report.json << EOL
{
"platform": "ios",
"base_size": $base_size,
"opencv_core": {
"total_size": $core_size,
"package_size": $core_diff
},
"opencv_dart": {
"total_size": $dart_size,
"package_size": $dart_diff
}
}
EOL
- name: Upload size report
uses: actions/upload-artifact@v4
with:
name: ios-size-report
path: ios_size_report.json
macos:
name: macos-size-comparison
runs-on: macos-13
steps:
- uses: subosito/flutter-action@v2
with:
channel: "stable"
- uses: actions/checkout@v4
# Create and build base project
- name: Create base project
run: |
mkdir size_comparison
cd size_comparison
flutter create base_project
cd base_project
flutter build macos
BASE_SIZE=$(du -sk "build/macos/Build/Products/Release/base_project.app" | cut -f1)
echo "BASE_SIZE=$BASE_SIZE" >> $GITHUB_ENV
# Build and measure opencv_core
- name: opencv_core size
run: |
cd packages/opencv_core/example
flutter build macos
CORE_SIZE=$(du -sk "build/macos/Build/Products/Release/opencv_core_example.app" | cut -f1)
echo "CORE_SIZE=$CORE_SIZE" >> $GITHUB_ENV
# Build and measure opencv_dart
- name: opencv_dart size
run: |
cd packages/opencv_dart/example
flutter build macos
DART_SIZE=$(du -sk "build/macos/Build/Products/Release/opencv_dart_example.app" | cut -f1)
echo "DART_SIZE=$DART_SIZE" >> $GITHUB_ENV
# Generate size comparison JSON
- name: Generate size report
run: |
base_size=${BASE_SIZE:-0}
core_size=${CORE_SIZE:-0}
dart_size=${DART_SIZE:-0}
core_diff=$((core_size - base_size))
dart_diff=$((dart_size - base_size))
cat > macos_size_report.json << EOL
{
"platform": "macos",
"base_size": $base_size,
"opencv_core": {
"total_size": $core_size,
"package_size": $core_diff
},
"opencv_dart": {
"total_size": $dart_size,
"package_size": $dart_diff
}
}
EOL
- name: Upload size report
uses: actions/upload-artifact@v4
with:
name: macos-size-report
path: macos_size_report.json
macos-arm:
name: macos-arm-size-comparison
runs-on: macos-14
steps:
- uses: subosito/flutter-action@v2
with:
channel: "stable"
- uses: actions/checkout@v4
# Create and build base project
- name: Create base project
run: |
mkdir size_comparison
cd size_comparison
flutter create base_project
cd base_project
flutter build macos
BASE_SIZE=$(du -sk "build/macos/Build/Products/Release/base_project.app" | cut -f1)
echo "BASE_SIZE=$BASE_SIZE" >> $GITHUB_ENV
# Build and measure opencv_core
- name: opencv_core size
run: |
cd packages/opencv_core/example
flutter build macos
CORE_SIZE=$(du -sk "build/macos/Build/Products/Release/opencv_core_example.app" | cut -f1)
echo "CORE_SIZE=$CORE_SIZE" >> $GITHUB_ENV
# Build and measure opencv_dart
- name: opencv_dart size
run: |
cd packages/opencv_dart/example
flutter build macos
DART_SIZE=$(du -sk "build/macos/Build/Products/Release/opencv_dart_example.app" | cut -f1)
echo "DART_SIZE=$DART_SIZE" >> $GITHUB_ENV
# Generate size comparison JSON
- name: Generate size report
run: |
base_size=${BASE_SIZE:-0}
core_size=${CORE_SIZE:-0}
dart_size=${DART_SIZE:-0}
core_diff=$((core_size - base_size))
dart_diff=$((dart_size - base_size))
cat > macos_arm_size_report.json << EOL
{
"platform": "macos-arm",
"base_size": $base_size,
"opencv_core": {
"total_size": $core_size,
"package_size": $core_diff
},
"opencv_dart": {
"total_size": $dart_size,
"package_size": $dart_diff
}
}
EOL
- name: Upload size report
uses: actions/upload-artifact@v4
with:
name: macos-arm-size-report
path: macos_arm_size_report.json
combine-reports:
name: Generate Combined Size Reports
needs: [windows, android, ios, linux, macos, macos-arm]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# - name: Set up Python
# uses: actions/setup-python@v5
# with:
# python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pandas numpy
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: size-reports
- name: Generate Combined Report
id: combine-reports
run: |
cat << 'EOF' > generate_combined_report.py
import json
import os
import glob
from datetime import datetime
from typing import Dict, Any, Union
import pandas as pd
import numpy as np
def format_size(size: Union[float, str, dict, None]) -> tuple[str, float]:
"""Format size values with appropriate units and return both display and raw values."""
try:
if isinstance(size, (int, float,str)):
raw_value = float(size) / 1024
return f"{raw_value:.2f} MB", raw_value
elif isinstance(size, dict):
return "N/A (nested)", 0.0
return "N/A", 0.0
except Exception as e:
print(f"Error formatting size: {str(e)}")
return "Error", 0.0
def process_platform_data(data: Dict[str, Any]) -> Dict[str, Any]:
"""Process and validate platform-specific data."""
if not isinstance(data, dict):
print(f"Unexpected type: {type(data).__name__} for data: {data}")
return {}
processed = {}
for key, value in data.items():
if isinstance(value, dict):
# Extract total_size and package_size directly for better structure
processed[key] = {
"total_size": format_size(value.get("total_size"))[0],
"package_size": format_size(value.get("package_size"))[0]
}
elif isinstance(value, (int, float, str)):
processed[key] = format_size(value)[0]
else:
processed[key] = "N/A"
return processed
def consolidate_data(reports: list) -> Dict[str, Any]:
"""Consolidate all report data with additional analytics."""
combined = {}
for report_path in reports:
platform = os.path.basename(report_path).replace('_size_report.json', '')
try:
with open(report_path) as f:
data = json.load(f)
for package, sizes in data.items():
if package == 'base_size' or package == 'platform': # Skip platform mapping
continue
if package not in combined:
combined[package] = {}
if sizes is isinstance(sizes, str):
continue
combined[package][platform] = process_platform_data(sizes)
except Exception as e:
print(f"Error processing {report_path}: {str(e)}")
continue
return combined
def main():
try:
reports = glob.glob('size-reports/*/*.json')
if not reports:
raise Exception("No report files found")
combined_data = consolidate_data(reports)
with open("combined_size_report.json", "w") as f:
json.dump(combined_data, f, indent=2)
print("::set-output name=status::success")
print(f"::set-output name=report_count::{len(reports)}")
except Exception as e:
print(f"::error::Error generating combined report: {str(e)}")
print("::set-output name=status::failure")
raise
if __name__ == "__main__":
main()
EOF
python generate_combined_report.py
- name: Generate SVG Reports
if: steps.combine-reports.outputs.status == 'success'
id: generate-svgs
run: |
cat << 'EOF' > generate_svgs.py
import json
from pathlib import Path
from typing import Union, Dict, Any
def create_gradient_definitions() -> str:
"""Create gradient definitions for various visual elements."""
return '''
<defs>
<linearGradient id="headerGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#2d3748;stop-opacity:0.1" />
<stop offset="100%" style="stop-color:#2d3748;stop-opacity:0.05" />
</linearGradient>
<filter id="dropShadow">
<feDropShadow dx="0" dy="1" stdDeviation="2" flood-opacity="0.1"/>
</filter>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&amp;display=swap');
.title { font-family: 'Inter', sans-serif; font-size: 24px; font-weight: 600; }
.subtitle { font-family: 'Inter', sans-serif; font-size: 14px; fill: #666; }
.platform-name { font-family: 'Inter', sans-serif; font-size: 16px; font-weight: 500; }
.data-row { font-family: 'Inter', sans-serif; font-size: 14px; }
.metric-value { font-family: 'Inter', sans-serif; font-size: 14px; font-weight: 500; }
.trend-indicator { font-family: 'Inter', sans-serif; font-size: 12px; }
.section-header { font-family: 'Inter', sans-serif; font-size: 18px; font-weight: 600; }
</style>
</defs>
'''
def create_platform_icon(platform: str, x: int, y: int) -> str:
"""Create platform-specific icon."""
icons = {
'windows': '''
<path d="M0,0 L9,0 9,9 0,9 Z" fill="#00A4EF"/>
<path d="M10,0 L19,0 19,9 10,9 Z" fill="#7FBA00"/>
<path d="M0,10 L9,10 9,19 0,19 Z" fill="#F25022"/>
<path d="M10,10 L19,10 19,19 10,19 Z" fill="#FFB900"/>
''',
'macos': '''
<path d="M18.71 19.5C17.88 20.74 17 21.95 15.66 21.97C14.32 22 13.89 21.18 12.37 21.18C10.84 21.18 10.37 21.95 9.1 22C7.79 22.05 6.8 20.68 5.96 19.47C4.25 17 2.94 12.45 4.7 9.39C5.57 7.87 7.13 6.91 8.82 6.88C10.1 6.86 11.32 7.75 12.11 7.75C12.89 7.75 14.37 6.68 15.92 6.84C16.57 6.87 18.39 7.1 19.56 8.82C19.47 8.88 17.39 10.1 17.41 12.63C17.44 15.65 20.06 16.66 20.09 16.67C20.06 16.74 19.67 18.11 18.71 19.5M13 5.96C13.63 5.19 14.51 4.2 14.24 2.87C13.11 2.96 11.76 3.55 11.12 4.3C10.54 4.98 9.96 5.99 10.27 7.29C11.53 7.33 12.65 6.72 13 5.96" fill="currentColor"/>
''',
# 'linux': '''
# <path d="M12.504 0c-.155 0-.315.008-.48.021-4.226.333-3.105 4.807-3.17 6.298-.076 1.092-.3 1.953-1.05 3.02-.885 1.051-2.127 2.75-2.716 4.521-.278.832-.41 1.684-.287 2.489a.424.424 0 00-.11.135c-.26.268-.45.6-.663.839-.199.199-.485.267-.797.4-.313.135-.658.35-.864.602-.09.11-.176.223-.258.337l-.003.005c-.146.207-.382.434-.558.663-.177.229-.253.375-.539.524l-.73.379c-.425.22-1.41.576-1.788 1.03-.38.456-.537.827-.644 1.346-.037.18.005.347.123.525.269.394.489.749.688 1.095.252.442.495.892.215 1.419-.122.22-.272.418-.43.586-.158.169-.346.306-.568.407-.205.114-.412.21-.614.305-.53.258-.827.598-1.285.781-.735.293-1.152 1.053-1.214 1.676-.097.935.524 1.803 1.728 2.006.583.095 2.047.198 2.602.178.563-.022.975-.23 1.399-.413.485-.188.987-.352 1.543-.448.425-.075.85-.134 1.276-.158.942-.055 1.975.095 2.747.537.772.443 1.71.592 2.632.43.577-.1 1.065-.293 1.534-.57.314-.186.84-.284 1.175-.453.303-.151.722-.306 1.005-.472.264-.155.487-.363.66-.613.188-.266.334-.592.416-.944.154-.577.195-1.23.407-1.769.24-.607.573-1.087.881-1.622.059-.106.172-.27.182-.386.03-.303-.072-.567-.206-.84l-.223-.468c-.418-.956-.585-2.128-.217-3.113.208-.555.517-1.072.867-1.515.194-.247.43-.457.654-.664l.023-.022c.119-.115.287-.245.534-.303.195-.048.375.003.472.064.172.108.318.29.463.439l.314.318c.284.277.558.553.843.821.206.191.44.353.697.487.472.252.93.482 1.446.582.268.053.545.074.814.051.213-.02.426-.084.603-.19.333-.202.514-.532.628-.863.172-.503.193-1.037.216-1.565.022-.499.05-1.018-.075-1.495-.125-.481-.328-.932-.593-1.344-.212-.335-.485-.638-.72-.963l-.105-.145c-.135-.202-.278-.405-.356-.627-.066-.193-.073-.399-.074-.605 0-.163-.015-.327-.027-.492l-.05-.694c-.072-.963-.148-1.957.147-2.884.16-.499.418-.949.742-1.35.257-.32.528-.593.739-.923.136-.207.237-.439.314-.683.081-.244.155-.499.247-.737.127-.323.368-.603.397-.968.014-.156.004-.315-.022-.47-.059-.364-.207-.689-.347-1.015l-.342-.786c-.103-.213-.248-.412-.378-.618-.437-.677-.849-1.371-1.156-2.112C14.89.813 14.31.195 13.441.064c-.294-.042-.592-.043-.888-.043z" fill="#000"/>
# ''',
'android': '''
<path d="M17.6 9.48l1.84-3.18c.16-.31.04-.69-.26-.85a.637.637 0 0 0-.83.22l-1.88 3.24a11.463 11.463 0 0 0-8.94 0L5.65 5.67a.643.643 0 0 0-.87-.2c-.28.18-.37.54-.22.83L6.4 9.48A10.78 10.78 0 0 0 1 18h22a10.78 10.78 0 0 0-5.4-8.52zM7 15.25a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5zm10 0a1.25 1.25 0 1 1 0-2.5 1.25 1.25 0 0 1 0 2.5z" fill="#3DDC84"/>
''',
'ios': '''
<path d="M18.71 19.5C17.88 20.74 17 21.95 15.66 21.97C14.32 22 13.89 21.18 12.37 21.18C10.84 21.18 10.37 21.95 9.1 22C7.79 22.05 6.8 20.68 5.96 19.47C4.25 17 2.94 12.45 4.7 9.39C5.57 7.87 7.13 6.91 8.82 6.88C10.1 6.86 11.32 7.75 12.11 7.75C12.89 7.75 14.37 6.68 15.92 6.84C16.57 6.87 18.39 7.1 19.56 8.82C19.47 8.88 17.39 10.1 17.41 12.63C17.44 15.65 20.06 16.66 20.09 16.67C20.06 16.74 19.67 18.11 18.71 19.5M13 5.96C13.63 5.19 14.51 4.2 14.24 2.87C13.11 2.96 11.76 3.55 11.12 4.3C10.54 4.98 9.96 5.99 10.27 7.29C11.53 7.33 12.65 6.72 13 5.96" fill="currentColor"/>
'''
}
platform_key = platform.lower().replace('_arm', '')
return f'''
<g transform="translate({x}, {y}) scale(0.8)">
{icons.get(platform_key, '')}
</g>
'''
def generate_svg_for_package(package_name: str, data: Dict[str, Any]) -> None:
"""Generate enhanced SVG report for a package."""
platforms = list(data.keys())
# SVG Dimensions and Spacing Constants
HEADER_HEIGHT = 150
PLATFORM_HEADER_HEIGHT = 40
ROW_HEIGHT = 35
PLATFORM_SPACING = 5
ROW_SPACING = 10
SECTION_SPACING = 20
SIDE_PADDING = 40
# Calculate rows needed for each platform
platform_rows = {}
total_rows = 0
for platform, platform_data in data.items():
if platform == 'android':
rows = len(platform_data) # One row per architecture
else:
rows = 1 # One row for total and package size
platform_rows[platform] = rows
total_rows += rows
# Calculate total height components
content_height = (
HEADER_HEIGHT + # Header section
(len(platforms) * PLATFORM_HEADER_HEIGHT) + # Platform headers
(total_rows * ROW_HEIGHT) + # Data rows
(len(platforms) * PLATFORM_SPACING) + # Spacing between platform sections
(total_rows * ROW_SPACING) + # Spacing between rows
(len(platforms) * SECTION_SPACING) # Additional section spacing
)
# Add padding to total height
svg_height = content_height + (2 * SIDE_PADDING)
svg_width = 800
# Create directory for package SVGs
package_dir = Path("packages") / package_name / "images"
package_dir.mkdir(parents=True, exist_ok=True)
svg_content = f'''
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {svg_width} {svg_height}">
{create_gradient_definitions()}
<!-- Background -->
<rect width="100%" height="100%" fill="#ffffff"/>
<!-- Header -->
<g transform="translate({SIDE_PADDING}, {SIDE_PADDING})">
<rect width="{svg_width-2*SIDE_PADDING}" height="80" fill="url(#headerGradient)" rx="8"/>
<text class="title" x="20" y="35">{package_name}</text>
<text class="subtitle" x="20" y="60">Size Analysis Report</text>
</g>
'''
y_offset = HEADER_HEIGHT
# Platform Data
for platform in platforms:
platform_color = {
'windows': '#00A4EF',
'macos': '#999999',
'macos_arm': '#666666',
'linux': '#FCC624',
'android': '#3DDC84',
'ios': '#000000'
}.get(platform, '#444444')
platform_data = data[platform]
# Platform Header
svg_content += f'''
<g transform="translate({SIDE_PADDING}, {y_offset})" filter="url(#dropShadow)">
<rect width="{svg_width-2*SIDE_PADDING}" height="{PLATFORM_HEADER_HEIGHT}" fill="{platform_color}" opacity="0.1" rx="4"/>
{create_platform_icon(platform, 20, 8)}
<text x="50" y="24" class="platform-name" fill="{platform_color}">{platform.upper()}</text>
</g>
'''
y_offset += PLATFORM_SPACING + PLATFORM_HEADER_HEIGHT
if platform == 'android':
for arch, sizes in platform_data.items():
svg_content += f'''
<g transform="translate({SIDE_PADDING + 20}, {y_offset})">
<text class="data-row" x="0" y="15">{arch}</text>
<text class="data-row" x="200" y="15">Total: </text>
<text class="metric-value" x="250" y="15">{sizes['total_size']}</text>
<text class="data-row" x="400" y="15">Package: </text>
<text class="metric-value" x="500" y="15">{sizes['package_size']}</text>
</g>
'''
y_offset += ROW_HEIGHT + ROW_SPACING
else:
svg_content += f'''
<g transform="translate({SIDE_PADDING + 20}, {y_offset})">
<text class="data-row" x="0" y="15">Total: </text>
<text class="metric-value" x="50" y="15">{platform_data['total_size']}</text>
<text class="data-row" x="200" y="15">Package: </text>
<text class="metric-value" x="280" y="15">{platform_data['package_size']}</text>
</g>
'''
y_offset += ROW_HEIGHT + ROW_SPACING
y_offset += SECTION_SPACING
svg_content += '</svg>'
# Save SVG to package-specific directory
svg_path = package_dir / f"{package_name}_size_report.svg"
with svg_path.open("w") as f:
f.write(svg_content)
# Also save to main svg-reports directory for backward compatibility
main_svg_path = Path("svg-reports") / f"{package_name}_size_report.svg"
with main_svg_path.open("w") as f:
f.write(svg_content)
def calculate_package_analytics(package_data: Dict[str, Any]) -> Dict[str, Any]:
"""Calculate analytics for a package across all platforms."""
all_sizes = []
for platform, data in package_data.items():
if platform == 'android':
# For Android, include all architecture sizes
for arch_data in data.values():
all_sizes.append(float(arch_data['total_size'].replace(' MB', '')))
all_sizes.append(float(arch_data['package_size'].replace(' MB', '')))
else:
# For other platforms, include total and package sizes
all_sizes.append(float(data['total_size'].replace(' MB', '')))
all_sizes.append(float(data['package_size'].replace(' MB', '')))
return {
'average_size': f"{sum(all_sizes) / len(all_sizes):.2f} MB",
'size_range': f"{min(all_sizes):.2f} MB - {max(all_sizes):.2f} MB"
}
def main():
try:
# Create output directory
Path("svg-reports").mkdir(exist_ok=True)
# Read the JSON data
with open("combined_size_report.json") as f:
data = json.load(f)
# Process each package
for package_name, package_data in data.items():
# Generate analytics for the package
analytics = calculate_package_analytics(package_data)
# Generate SVG report
generate_svg_for_package(package_name, package_data)
print(f"Generated SVG report for {package_name}")
print("::set-output name=status::success")
except Exception as e:
print(f"::error::Error generating SVG reports: {str(e)}")
print("::set-output name=status::failure")
raise
if __name__ == "__main__":
main()
EOF
python generate_svgs.py
- name: Create Report Summary
if: always()
run: |
echo "## Size Report Generation Summary" >> $GITHUB_STEP_SUMMARY
echo "### Status" >> $GITHUB_STEP_SUMMARY
if [[ "${{ steps.generate-svgs.outputs.status }}" == "success" ]]; then
echo "✅ Report generation completed successfully" >> $GITHUB_STEP_SUMMARY
else
echo "❌ Report generation failed" >> $GITHUB_STEP_SUMMARY
fi
echo "### Details" >> $GITHUB_STEP_SUMMARY
echo "- Reports processed: ${{ steps.combine-reports.outputs.report_count }}" >> $GITHUB_STEP_SUMMARY
echo "- Generated at: $(date -u '+%Y-%m-%d %H:%M:%S UTC')" >> $GITHUB_STEP_SUMMARY
- name: Upload Combined JSON Report
if: steps.combine-reports.outputs.status == 'success'
uses: actions/upload-artifact@v4
with:
name: combined-size-report
path: combined_size_report.json
if-no-files-found: error
- name: Upload SVG Reports
if: steps.generate-svgs.outputs.status == 'success'
uses: actions/upload-artifact@v4
with:
name: svg-size-reports
path: svg-reports/*.svg
if-no-files-found: error
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "Commit SVG reports"
branch: ${{ github.head_ref }}
file_pattern: 'packages/*/*.svg'