adding size comparison #74
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Size Comparison | |
on: | |
push: | |
branches: ["main"] | |
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 | |
- name: Create base project | |
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") | |
BASE_SIZE_ARMV7=$(stat -c%s "build/app/outputs/apk/release/app-armeabi-v7a-release.apk" || echo "0") | |
BASE_SIZE_X64=$(stat -c%s "build/app/outputs/apk/release/app-x86_64-release.apk" || echo "0") | |
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 and measure opencv_core | |
- name: opencv_core size | |
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") | |
CORE_SIZE_ARMV7=$(stat -c%s "build/app/outputs/apk/release/app-armeabi-v7a-release.apk" || echo "0") | |
CORE_SIZE_X64=$(stat -c%s "build/app/outputs/apk/release/app-x86_64-release.apk" || echo "0") | |
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 and measure opencv_dart | |
- name: opencv_dart size | |
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") | |
DART_SIZE_ARMV7=$(stat -c%s "build/app/outputs/apk/release/app-armeabi-v7a-release.apk" || echo "0") | |
DART_SIZE_X64=$(stat -c%s "build/app/outputs/apk/release/app-x86_64-release.apk" || echo "0") | |
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 | |
# 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} | |
}, | |
"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} )) | |
} | |
}, | |
"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} )) | |
} | |
} | |
} | |
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: | |
- name: Set up Python | |
uses: actions/setup-python@v5 | |
with: | |
python-version: '3.11' | |
cache: 'pip' | |
- 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 | |
pattern: '*-size-report' | |
merge-multiple: true | |
- 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)): | |
raw_value = float(size) | |
if size > 1024: | |
return f"{size/1024:.2f} GB", raw_value | |
return f"{size:.2f} MB", raw_value | |
elif isinstance(size, str): | |
try: | |
float_val = float(size) | |
return format_size(float_val) | |
except ValueError: | |
return size, 0.0 | |
elif isinstance(size, dict): | |
return "N/A (nested)", 0.0 | |
return "N/A", 0.0 | |
except Exception: | |
return "Error", 0.0 | |
def process_platform_data(data: Dict[str, Any]) -> Dict[str, Any]: | |
"""Process and validate platform-specific data.""" | |
processed = {} | |
for key, value in data.items(): | |
if isinstance(value, dict): | |
processed[key] = { | |
k: format_size(v)[0] if not isinstance(v, dict) else process_platform_data(v) | |
for k, v in value.items() | |
} | |
else: | |
processed[key] = format_size(value)[0] | |
return processed | |
def generate_trend_data(data: Dict[str, Any]) -> Dict[str, Any]: | |
"""Generate trend analysis from historical data.""" | |
trends = {} | |
for package, platforms in data.items(): | |
if package == "platform": | |
continue | |
trends[package] = {} | |
for platform, sizes in platforms.items(): | |
if isinstance(sizes, dict): | |
if "total_size" in sizes: | |
_, raw_size = format_size(sizes["total_size"]) | |
trends[package][platform] = raw_size | |
else: | |
platform_avg = np.mean([ | |
format_size(s.get("total_size", 0))[1] | |
for s in sizes.values() | |
if isinstance(s, dict) | |
]) | |
trends[package][platform] = platform_avg | |
else: | |
_, raw_size = format_size(sizes) | |
trends[package][platform] = raw_size | |
return trends | |
def consolidate_data(reports: list) -> Dict[str, Any]: | |
"""Consolidate all report data with additional analytics.""" | |
combined = { | |
"metadata": { | |
"timestamp": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ"), | |
"report_count": len(reports), | |
"generated_at": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC") | |
}, | |
"packages": {} | |
} | |
for report_path in reports: | |
platform = os.path.basename(os.path.dirname(report_path)).replace('-size-report', '') | |
try: | |
with open(report_path) as f: | |
data = json.load(f) | |
for package, sizes in data.items(): | |
if package not in combined["packages"]: | |
combined["packages"][package] = {} | |
combined["packages"][package][platform] = process_platform_data(sizes) | |
except Exception as e: | |
print(f"Error processing {report_path}: {str(e)}") | |
continue | |
# Generate trends and statistics | |
trends = generate_trend_data(combined["packages"]) | |
combined["analytics"] = { | |
"trends": trends, | |
"summary": { | |
package: { | |
"average_size": f"{np.mean(list(platforms.values())):.2f} MB", | |
"size_range": f"{min(platforms.values()):.2f} - {max(platforms.values()):.2f} MB" | |
} | |
for package, platforms in trends.items() | |
} | |
} | |
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) | |
# Save combined report | |
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 | |
import math | |
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&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"/> | |
''', | |
'apple': ''' | |
<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-.043zm1.054 1.779c.096.019.19.042.279.066.299.079.599.223.838.412.238.19.487.402.607.645.087.176.147.384.188.583.062.318.123.646.133.96.01.459-.144.883-.38 1.265-.163.262-.367.512-.555.75-.353.45-.604.977-.762 1.523-.242.822-.208 1.773-.147 2.636l.047.694.012.165c.014.355.037.722.148 1.062.14.427.401.805.638 1.17l.087.12c.242.335.516.645.733.986.216.343.387.704.488 1.085.04.111.074.224.107.336.037.154.057.316.075.476.073.582.074 1.165.044 1.748-.029.578-.077 1.208-.307 1.738-.177.404-.478.762-.905.935-.396.159-.916.186-1.327.105-.866 1.523-.381 1.265-.293.584-.39.962-.133.96.01.318.101.726.188.583.12.243.369.455.607.645.239.189.539.333.838.412.089.024.183.047.279.066z" 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"/> | |
''' | |
} | |
base_icon = icons.get(platform.lower().replace('-arm', ''), '') | |
if not base_icon: | |
return '' | |
return f''' | |
<g transform="translate({x}, {y}) scale(0.8)"> | |
{base_icon} | |
</g> | |
''' | |
def generate_svg_for_package(package_name: str, data: Dict[str, Any], analytics: Dict[str, Any]) -> None: | |
"""Generate enhanced SVG report for a package with analytics.""" | |
platforms = list(data.keys()) | |
# Calculate dimensions | |
row_height = 40 | |
header_height = 150 | |
platform_spacing = 20 | |
max_rows_per_platform = max( | |
len(sizes) if isinstance(sizes, dict) else 1 | |
for sizes in data.values() | |
) | |
svg_height = header_height + (len(platforms) * (row_height * max_rows_per_platform + platform_spacing)) + 100 | |
svg_width = 1200 | |
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(40, 40)"> | |
<rect width="{svg_width-80}" 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> | |
<!-- Summary Metrics --> | |
<g transform="translate({svg_width-400}, 20)"> | |
<text class="metric-value" x="0" y="15"> | |
Avg Size: {analytics['summary'][package_name]['average_size']} | |
</text> | |
<text class="metric-value" x="0" y="35"> | |
Range: {analytics['summary'][package_name]['size_range']} | |
</text> | |
</g> | |
</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(40, {y_offset})" filter="url(#dropShadow)"> | |
<rect width="{svg_width-80}" height="36" 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 += 40 | |
if isinstance(platform_data, dict): | |
if any(isinstance(v, dict) and 'total_size' in v for v in platform_data.values()): | |
# Multi-architecture data | |
for arch, sizes in platform_data.items(): | |
if isinstance(sizes, dict): | |
total_size = sizes.get('total_size', 'N/A') | |
package_size = sizes.get('package_size', 'N/A') | |
svg_content += f''' | |
<g transform="translate(60, {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">{total_size}</text> | |
<text class="data-row" x="400" y="15">Package: </text> | |
<text class="metric-value" x="450" y="15">{package_size}</text> | |
</g> | |
''' | |
y_offset += row_height | |
else: | |
# Single architecture | |
total_size = platform_data.get('total_size', 'N/A') | |
package_size = platform_data.get('package_size', 'N/A') | |
svg_content += f''' | |
<g transform="translate(60, {y_offset})"> | |
<text class="data-row" x="0" y="15">Total: </text> | |
<text class="metric-value" x="50" y="15">{total_size}</text> | |
<text class="data-row" x="200" y="15">Package: </text> | |
<text class="metric-value" x="250" y="15">{package_size}</text> | |
</g> | |
''' | |
y_offset += row_height | |
else: | |
svg_content += f''' | |
<g transform="translate(60, {y_offset})"> | |
<text class="data-row" x="0" y="15">Size: </text> | |
<text class="metric-value" x="50" y="15">{platform_data}</text> | |
</g> | |
''' | |
y_offset += row_height | |
y_offset += platform_spacing | |
svg_content += '</svg>' | |
# Save SVG | |
svg_path = Path("svg-reports") / f"{package_name}_size_report.svg" | |
svg_path.parent.mkdir(exist_ok=True) | |
with svg_path.open("w") as f: | |
f.write(svg_content) | |
def main(): | |
try: | |
with open("combined_size_report.json") as f: | |
data = json.load(f) | |
Path("svg-reports").mkdir(exist_ok=True) | |
for package, package_data in data["packages"].items(): | |
if package != "platform": # Skip platform mapping | |
generate_svg_for_package( | |
package, | |
package_data, | |
data["analytics"] | |
) | |
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 |