Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/workflows/api-compatibility-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: API Compatibility Check

on:
pull_request:
types: [opened, synchronize, reopened, labeled]
branches:
- develop
- main

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

jobs:
api-compatibility:
name: Metalava API Compatibility
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- uses: GetStream/android-ci-actions/actions/setup-java@main

- name: Check API compatibility
id: api-check
run: |
./gradlew \
:stream-video-android-core:metalavaCheckCompatibilityRelease \
:stream-video-android-ui-core:metalavaCheckCompatibilityRelease \
--no-configuration-cache --stacktrace
continue-on-error: true

- name: Generate current API signature
if: steps.api-check.outcome == 'failure'
run: |
./gradlew \
:stream-video-android-core:metalavaGenerateSignatureRelease \
:stream-video-android-ui-core:metalavaGenerateSignatureRelease \
--no-configuration-cache

- name: Diff API changes
if: steps.api-check.outcome == 'failure'
run: |

Check failure on line 42 in .github/workflows/api-compatibility-check.yml

View workflow job for this annotation

GitHub Actions / base-android-ci / Run actionlint

shellcheck reported issue in this script: SC2129:style:8:7: Consider using { cmd1; cmd2; } >> file instead of individual redirects

Check failure on line 42 in .github/workflows/api-compatibility-check.yml

View workflow job for this annotation

GitHub Actions / base-android-ci / Run actionlint

shellcheck reported issue in this script: SC2086:info:9:25: Double quote to prevent globbing and word splitting

Check failure on line 42 in .github/workflows/api-compatibility-check.yml

View workflow job for this annotation

GitHub Actions / base-android-ci / Run actionlint

shellcheck reported issue in this script: SC2086:info:8:29: Double quote to prevent globbing and word splitting

Check failure on line 42 in .github/workflows/api-compatibility-check.yml

View workflow job for this annotation

GitHub Actions / base-android-ci / Run actionlint

shellcheck reported issue in this script: SC2086:info:2:12: Double quote to prevent globbing and word splitting

Check failure on line 42 in .github/workflows/api-compatibility-check.yml

View workflow job for this annotation

GitHub Actions / base-android-ci / Run actionlint

shellcheck reported issue in this script: SC2086:info:1:35: Double quote to prevent globbing and word splitting

Check failure on line 42 in .github/workflows/api-compatibility-check.yml

View workflow job for this annotation

GitHub Actions / base-android-ci / Run actionlint

shellcheck reported issue in this script: SC2086:info:18:79: Double quote to prevent globbing and word splitting

Check failure on line 42 in .github/workflows/api-compatibility-check.yml

View workflow job for this annotation

GitHub Actions / base-android-ci / Run actionlint

shellcheck reported issue in this script: SC2086:info:17:73: Double quote to prevent globbing and word splitting

Check failure on line 42 in .github/workflows/api-compatibility-check.yml

View workflow job for this annotation

GitHub Actions / base-android-ci / Run actionlint

shellcheck reported issue in this script: SC2086:info:12:18: Double quote to prevent globbing and word splitting

Check failure on line 42 in .github/workflows/api-compatibility-check.yml

View workflow job for this annotation

GitHub Actions / base-android-ci / Run actionlint

shellcheck reported issue in this script: SC2086:info:11:21: Double quote to prevent globbing and word splitting

Check failure on line 42 in .github/workflows/api-compatibility-check.yml

View workflow job for this annotation

GitHub Actions / base-android-ci / Run actionlint

shellcheck reported issue in this script: SC2086:info:10:23: Double quote to prevent globbing and word splitting
echo "## API Changes Detected" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY

for module in stream-video-android-core stream-video-android-ui-core; do
if [ -f "$module/api/current.txt" ]; then
DIFF=$(git diff --no-color -- "$module/api/current.txt" || true)
if [ -n "$DIFF" ]; then
echo "### $module" >> $GITHUB_STEP_SUMMARY
echo '```diff' >> $GITHUB_STEP_SUMMARY
echo "$DIFF" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
fi
fi
done

echo "> **Action required:** This PR contains breaking API changes." >> $GITHUB_STEP_SUMMARY
echo "> Add the \`approved-api-change\` label to acknowledge and proceed." >> $GITHUB_STEP_SUMMARY

- name: Check for approval label
if: steps.api-check.outcome == 'failure'
run: |
if echo '${{ toJson(github.event.pull_request.labels.*.name) }}' | grep -q 'approved-api-change'; then
echo "✅ API breaking change approved via label"
echo "## ✅ Breaking API change approved" >> $GITHUB_STEP_SUMMARY
echo "The \`approved-api-change\` label is present. This change has been explicitly approved." >> $GITHUB_STEP_SUMMARY
else
echo "❌ Breaking API changes detected without approval"
echo ""
echo "To approve this change:"
echo " 1. Review the API diff in the job summary above"
echo " 2. Add the 'approved-api-change' label to this PR"
echo " 3. Re-run this workflow"
exit 1
fi
5 changes: 5 additions & 0 deletions build-logic/convention/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies {
compileOnly(libs.kotlin.gradlePlugin)
// compileOnly(libs.compose.compiler.gradlePlugin) -> Enable with Kotlin 2.0+
compileOnly(libs.spotless.gradlePlugin)
runtimeOnly(libs.metalava.gradlePlugin)
}

gradlePlugin {
Expand All @@ -38,5 +39,9 @@ gradlePlugin {
id = "io.getstream.video.android.demoflavor"
implementationClass = "DemoFlavorConventionPlugin"
}
register("metalavaConvention") {
id = "io.getstream.video.android.metalava"
implementationClass = "MetalavaConventionPlugin"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import io.getstream.video.configureMetalava
import org.gradle.api.Plugin
import org.gradle.api.Project

class MetalavaConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
pluginManager.apply("me.tylerbwong.gradle.metalava")
configureMetalava()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.getstream.video

import org.gradle.api.Project
import org.gradle.api.provider.Property
import org.gradle.api.provider.SetProperty

/**
* Configures the Metalava plugin for API signature generation and compatibility tracking.
*
* @param hiddenAnnotations Annotations whose marked APIs are excluded from the signature.
* @param extraArguments Additional metalava CLI arguments (use `--flag=value` format).
*/
fun Project.configureMetalava(
hiddenAnnotations: Set<String> = emptySet(),
extraArguments: Set<String> = emptySet(),
) {
val metalavaEnabled = providers.gradleProperty("metalava.enabled")
.getOrElse("true").toBoolean()

val metalava = extensions.getByName("metalava")

@Suppress("UNCHECKED_CAST")
(metalava.javaClass.getMethod("getFilename").invoke(metalava) as Property<String>)
.set("api/current.txt")

if (hiddenAnnotations.isNotEmpty()) {
@Suppress("UNCHECKED_CAST")
(metalava.javaClass.getMethod("getHiddenAnnotations").invoke(metalava) as SetProperty<String>)
.set(hiddenAnnotations)
}

val defaultArgs = setOf(
"--hide=ReferencesHidden",
"--hide=HiddenTypeParameter",
"--hide=UnavailableSymbol",
"--hide=IoError",
)
@Suppress("UNCHECKED_CAST")
(metalava.javaClass.getMethod("getArguments").invoke(metalava) as SetProperty<String>)
.set(defaultArgs + extraArguments)

afterEvaluate {
tasks.matching { it.name.startsWith("metalava") }.configureEach {
setEnabled(metalavaEnabled)
}
tasks.named("apiDump") {
if (metalavaEnabled) {
finalizedBy("metalavaGenerateSignatureRelease")
}
}
}
}
1 change: 1 addition & 0 deletions build-logic/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ dependencyResolutionManagement {
google()
mavenCentral()
mavenLocal()
gradlePluginPortal()
}
versionCatalogs {
create("libs") {
Expand Down
23 changes: 14 additions & 9 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,20 @@ subprojects {
//}

afterEvaluate {
println("Running Add Pre Commit Git Hook Script on Build")
exec {
if (System.getProperty("os.name").toLowerCase().contains("win")) {
// Windows-specific command
commandLine("cmd", "/c", "copy", ".\\scripts\\git-hooks\\pre-push", ".\\.git\\hooks")
} else {
// Unix-based systems
commandLine("cp", "./scripts/git-hooks/pre-push", "./.git/hooks")
val gitDir = file(".git")
if (gitDir.isDirectory) {
println("Running Add Pre Commit Git Hook Script on Build")
exec {
if (System.getProperty("os.name").toLowerCase().contains("win")) {
// Windows-specific command
commandLine("cmd", "/c", "copy", ".\\scripts\\git-hooks\\pre-push", ".\\.git\\hooks")
} else {
// Unix-based systems
commandLine("cp", "./scripts/git-hooks/pre-push", "./.git/hooks")
}
}
println("Added pre-push Git Hook Script.")
} else {
println("Skipping git hook installation (worktree or non-standard .git)")
}
println("Added pre-push Git Hook Script.")
}
3 changes: 3 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,8 @@ android.suppressUnsupportedCompileSdk=34
enableComposeCompilerMetrics=true
enableComposeCompilerReports=true

# Metalava API signature generation (set to false to disable)
metalava.enabled=true

# Project version
version=1.19.0
3 changes: 3 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ playAppUpdate = "2.1.0"
hilt = "2.52"
leakCanary = "2.13"
binaryCompatabilityValidator = "0.16.3"
metalavaGradle = "0.5.0"
playPublisher = "3.12.1"

googleMlKitSelfieSegmentation = "16.0.0-beta6"
Expand Down Expand Up @@ -223,6 +224,7 @@ android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", ver
kotlin-gradlePlugin = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" }
compose-compiler-gradlePlugin = { group = "org.jetbrains.kotlin", name = "compose-compiler-gradle-plugin", version.ref = "kotlin" }
spotless-gradlePlugin = { group = "com.diffplug.spotless", name = "spotless-plugin-gradle", version.ref = "spotless" }
metalava-gradlePlugin = { group = "me.tylerbwong.gradle.metalava", name = "me.tylerbwong.gradle.metalava.gradle.plugin", version.ref = "metalavaGradle" }
androidx-camera-core = { group = "androidx.camera", name = "camera-core", version.ref = "cameraCore" }
play-services-mlkit-barcode-scanning = { group = "com.google.android.gms", name = "play-services-mlkit-barcode-scanning", version = "18.3.0" }
androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraCore" }
Expand Down Expand Up @@ -256,3 +258,4 @@ firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
play-publisher = { id = "com.github.triplet.play", version.ref = "playPublisher" }
baseline-profile = { id = "androidx.baselineprofile", version.ref = "androidxMacroBenchmark" }
metalava-gradle = { id = "me.tylerbwong.gradle.metalava", version.ref = "metalavaGradle" }
7 changes: 7 additions & 0 deletions stream-video-android-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ plugins {
id(libs.plugins.kotlin.serialization.get().pluginId)
id(libs.plugins.kotlin.parcelize.get().pluginId)
id(libs.plugins.wire.get().pluginId)
id("io.getstream.video.android.metalava")
}

wire {
Expand Down Expand Up @@ -52,6 +53,12 @@ apiValidation {
nonPublicMarkers.add("io.getstream.video.android.core.internal.InternalStreamVideoApi")
}

metalava {
hiddenAnnotations.set(
setOf("io.getstream.video.android.core.internal.InternalStreamVideoApi"),
)
}

android {
namespace = "io.getstream.video.android.core"
compileSdk = libs.versions.compileSdk.get().toInt()
Expand Down
1 change: 1 addition & 0 deletions stream-video-android-ui-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

plugins {
id("io.getstream.video.android.library")
id("io.getstream.video.android.metalava")
}

android {
Expand Down
Loading