Skip to content

Commit

Permalink
AND-116: SDK size reporting (APK comparison) (#5539)
Browse files Browse the repository at this point in the history
* Compare APKs to calculate SDK sizes

* Use release to build the comparison table

* Trigger updates on merging to develop

* changes after rebasing from develop which contains version catalog and kts refactoring

* add a single android application project for metrics

* rename build flavors to simplify logic in the CI script

* check sizes using the single metrics android project

* update sizes using the single metrics android project

* Update SDK size metrics

* Update SDK size badges

* Remove test setup

* Add a README note for the SDK sizes

* Use GITHUB_TOKEN instead of the personal token

* test

* Fix

* Push changes

* Remove multiple metrics modules

* Remove test setup

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
andremion and github-actions[bot] authored Jan 2, 2025
1 parent 521b42f commit e71d2ce
Show file tree
Hide file tree
Showing 9 changed files with 375 additions and 0 deletions.
121 changes: 121 additions & 0 deletions .github/workflows/sdk-size-checks.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
name: SDK size checks

on:
pull_request:

env:
METRICS_PROJECT: "stream-chat-android-metrics"
MODULES: "stream-chat-android-client stream-chat-android-offline stream-chat-android-ui-components stream-chat-android-compose"
METRICS_FILE: "metrics/size.json"
MAX_TOLERANCE: 500
FINE_TOLERANCE: 250

jobs:
compare-sdk-sizes:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3
- uses: ./.github/actions/setup-java
- uses: ./.github/actions/gradle-cache
with:
key-prefix: gradle-build

- name: Assemble release for metrics
run: ./gradlew :metrics:$METRICS_PROJECT:assembleRelease

- name: Get current SDK sizes
run: |
# Reads current SDK sizes from the metrics file
# and define to a variable using a compact JSON format
# so it can be exported for the next job step
CURRENT_SDK_SIZES=$(jq -c .release $METRICS_FILE)
echo "CURRENT_SDK_SIZES=$CURRENT_SDK_SIZES" >> $GITHUB_ENV
- name: Calculate PR branch SDK sizes
run: |
echo '{}' > pr_sdk_sizes.json
# Calculate sizes from the .apk files and save them into a temporary JSON file
# so it can be exported for the next job step
for module in $MODULES; do
baselineFile="metrics/$METRICS_PROJECT/build/outputs/apk/$module-baseline/release/$METRICS_PROJECT-$module-baseline-release.apk"
streamFile="metrics/$METRICS_PROJECT/build/outputs/apk/$module-stream/release/$METRICS_PROJECT-$module-stream-release.apk"
baselineSize=$(du -k "$baselineFile" | awk '{print $1}')
streamSize=$(du -k "$streamFile" | awk '{print $1}')
size=$((streamSize - baselineSize))
jq -c --arg sdk "$module" --arg size "$size" '. + {($sdk): ($size | tonumber)}' pr_sdk_sizes.json > temp.json && mv temp.json pr_sdk_sizes.json
done
echo "PR_SDK_SIZES=$(cat pr_sdk_sizes.json)" >> $GITHUB_ENV
- name: Post comment on PR
uses: actions/github-script@v6
with:
script: |
const maxTolerance = process.env.MAX_TOLERANCE
const fineTolerance = process.env.FINE_TOLERANCE
const currentSdkSizes = process.env.CURRENT_SDK_SIZES ? JSON.parse(process.env.CURRENT_SDK_SIZES) : {};
const prSdkSizes = JSON.parse(process.env.PR_SDK_SIZES);
const commentHeader = '## SDK Size Comparison 📏';
// Prepare the comparison table
let commentBody = `
${commentHeader}
| SDK | Before | After | Difference | Status |
|-|-|-|-|-|
`;
Object.keys(prSdkSizes).forEach(sdk => {
const currentSize = currentSdkSizes[sdk] || 0;
const prSize = prSdkSizes[sdk];
const diff = prSize - currentSize;
const currentSizeInMb = (currentSize / 1024).toFixed(2);
const prSizeInMb = (prSize / 1024).toFixed(2);
const diffInMb = (diff / 1024).toFixed(2);
let status = "🟢";
if (diff < 0) {
status = "🚀";
} else if (diff >= maxTolerance) {
status = "🔴";
} else if (diff >= fineTolerance) {
status = "🟡";
}
commentBody += `| ${sdk} | ${currentSizeInMb} MB | ${prSizeInMb} MB | ${diffInMb} MB | ${status} |\n`;
});
// Post or update the PR comment
// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
});
const comment = comments.find(c => c.body.includes(commentHeader));
if (comment) {
// Update the existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: comment.id,
body: commentBody,
});
} else {
// Create a new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: commentBody,
});
}
95 changes: 95 additions & 0 deletions .github/workflows/sdk-size-updates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: SDK size updates

on:
push:
branches:
- develop

workflow_dispatch:

concurrency:
group: ${{ github.ref }}
cancel-in-progress: true

env:
METRICS_PROJECT: "stream-chat-android-metrics"
MODULES: "stream-chat-android-client stream-chat-android-offline stream-chat-android-ui-components stream-chat-android-compose"
VARIANTS: "debug release"
METRICS_FILE: "metrics/size.json"
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}

jobs:
update-sdk-sizes:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v3
with:
persist-credentials: false
- uses: ./.github/actions/setup-java
- uses: ./.github/actions/gradle-cache
with:
key-prefix: gradle-build

- name: Assemble release and debug for metrics
run: ./gradlew :metrics:$METRICS_PROJECT:assemble

- name: Update size metrics
run: |
# Create temporary JSON file
echo '{}' > metrics.json
# Calculate sizes
for module in $MODULES; do
for variant in $VARIANTS; do
baselineFile="metrics/$METRICS_PROJECT/build/outputs/apk/$module-baseline/$variant/$METRICS_PROJECT-$module-baseline-$variant.apk"
streamFile="metrics/$METRICS_PROJECT/build/outputs/apk/$module-stream/$variant/$METRICS_PROJECT-$module-stream-$variant.apk"
# Ensure files exist
if [[ -f "$baselineFile" && -f "$streamFile" ]]; then
baselineSize=$(du -k "$baselineFile" | awk '{print $1}')
streamSize=$(du -k "$streamFile" | awk '{print $1}')
size=$((streamSize - baselineSize))
else
echo "Warning: $baselineFile or $streamFile not found. Setting size to 0."
size=0
fi
# Update JSON
jq --arg module "$module" --arg variant "$variant" --argjson size "$size" \
".\"$variant\".\"$module\" = $size" metrics.json > temp.json && mv temp.json metrics.json
done
done
# Validate Generated JSON
jq . metrics.json
# Move temporary JSON file to the final file
mv metrics.json $METRICS_FILE
- name: Update size badges
run: |
for module in $MODULES; do
size=$(jq --arg module "$module" ".release.\"$module\"" $METRICS_FILE)
sizeInMb=$(echo "scale=2; $size / 1024" | bc)
badgeUrl="https://img.shields.io/badge/${module//-/--}-$sizeInMb%20MB-lightgreen"
sed -i "s|!\[$module\](.*)|![$module](${badgeUrl})|" README.md
done
- name: Commit changes
run: |
git fetch origin $BRANCH_NAME
git checkout $BRANCH_NAME
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add $METRICS_FILE README.md
git commit -m "Update SDK sizes" || echo "No changes to commit"
- name: Push changes
uses: ad-m/github-push-action@master
with:
github_token: ${{ secrets.STREAM_PUBLIC_BOT_TOKEN }}
branch: ${{ env.BRANCH_NAME }}
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@
<a href="https://github.com/GetStream/stream-chat-android/releases"><img src="https://img.shields.io/github/v/release/GetStream/stream-chat-android" /></a>
</p>

<div align="center">

![stream-chat-android-client](https://img.shields.io/badge/stream--chat--android--client-3.04%20MB-lightgreen)
![stream-chat-android-offline](https://img.shields.io/badge/stream--chat--android--offline-3.24%20MB-lightgreen)
![stream-chat-android-ui-components](https://img.shields.io/badge/stream--chat--android--ui--components-7.90%20MB-lightgreen)
![stream-chat-android-compose](https://img.shields.io/badge/stream--chat--android--compose-9.03%20MB-lightgreen)

</div>

> **Note:** The SDK sizes reflect the maximum possible addition if none of their internal dependencies are already in your app. In most cases, the actual impact will be smaller.
This is the official Android SDK for [Stream Chat](https://getstream.io/chat/sdk/android/), a service for building chat and messaging applications. This library includes both a low-level chat SDK and a set of reusable UI components. Most users start with the UI components, and fall back to the lower level API when they want to customize things.

We're proud to say that we're the first Android Chat SDK that supports Jetpack Compose! We [released](https://github.com/GetStream/stream-chat-android/releases/tag/4.15.0) our Compose UI Components one day after the official Jetpack Compose 1.0 release and our team members have been working hard on it since then.
Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ apiValidation {
"stream-chat-android-test",
"stream-chat-android-compose-sample",
"stream-chat-android-ui-guides",
"stream-chat-android-metrics",
)

nonPublicMarkers += listOf(
Expand Down
14 changes: 14 additions & 0 deletions metrics/size.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"debug": {
"stream-chat-android-client": 15036,
"stream-chat-android-offline": 15336,
"stream-chat-android-ui-components": 21028,
"stream-chat-android-compose": 22716
},
"release": {
"stream-chat-android-client": 3116,
"stream-chat-android-offline": 3324,
"stream-chat-android-ui-components": 8096,
"stream-chat-android-compose": 9256
}
}
78 changes: 78 additions & 0 deletions metrics/stream-chat-android-metrics/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}

apply(from = "$rootDir/scripts/android.gradle")

android {
namespace = "io.getstream.chat.android.metrics"
buildTypes {
release {
signingConfig = signingConfigs.findByName("debug")
}
}

flavorDimensions += "sdk"

productFlavors {
create("stream-chat-android-client-baseline") {
dimension = "sdk"
}
create("stream-chat-android-client-stream") {
dimension = "sdk"
}
create("stream-chat-android-offline-baseline") {
dimension = "sdk"
}
create("stream-chat-android-offline-stream") {
dimension = "sdk"
}
create("stream-chat-android-compose-baseline") {
dimension = "sdk"
}
create("stream-chat-android-compose-stream") {
dimension = "sdk"
}
create("stream-chat-android-ui-components-baseline") {
dimension = "sdk"
}
create("stream-chat-android-ui-components-stream") {
dimension = "sdk"
}
}
}

afterEvaluate {
android.productFlavors.forEach { flavor ->
val flavorName = flavor.name
// For compose flavors, we apply the compose plugin,
// set up build features and add common compose dependencies.
if (flavorName.contains("compose")) {
val composePlugin = libs.plugins.kotlin.compose.get()
plugins.apply(composePlugin.pluginId)
android.buildFeatures.compose = true
val configurationName = "${flavorName}Implementation"
dependencies.add(configurationName, libs.androidx.compose.ui)
dependencies.add(configurationName, libs.androidx.compose.ui.tooling)
dependencies.add(configurationName, libs.androidx.compose.foundation)
dependencies.add(configurationName, libs.androidx.activity.compose)
dependencies.add(configurationName, libs.androidx.lifecycle.runtime.compose)
dependencies.add(configurationName, libs.androidx.lifecycle.viewmodel.compose)
}
}
}

dependencies {
detektPlugins(libs.detekt.formatting)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.androidx.appcompat)

"stream-chat-android-client-streamImplementation"(project(":stream-chat-android-client"))

"stream-chat-android-offline-streamImplementation"(project(":stream-chat-android-offline"))

"stream-chat-android-ui-components-streamImplementation"(project(":stream-chat-android-ui-components"))

"stream-chat-android-compose-streamImplementation"(project(":stream-chat-android-compose"))
}
33 changes: 33 additions & 0 deletions metrics/stream-chat-android-metrics/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (c) 2014-2024 Stream.io Inc. All rights reserved.
Licensed under the Stream License;
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
>

<application>
<activity
android:name=".MainActivity"
android:exported="true"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2014-2024 Stream.io Inc. All rights reserved.
*
* Licensed under the Stream License;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://github.com/GetStream/stream-chat-android/blob/main/LICENSE
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.getstream.chat.android.metrics

import androidx.activity.ComponentActivity

class MainActivity : ComponentActivity()
Loading

0 comments on commit e71d2ce

Please sign in to comment.