Skip to content
Open
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
2 changes: 2 additions & 0 deletions .circleci/agp9-smoke/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.gradle/
build/
6 changes: 6 additions & 0 deletions .circleci/agp9-smoke/.sdkmanrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# JDK 21 and Gradle 9.1 are required by AGP 9.
# This .sdkmanrc is consumed by `sdk env install` in the agp9-smoke-test
# CircleCI job. The project root pins JDK 17 (used by every other job);
# only this smoke test needs JDK 21 + a newer Gradle.
java=21.0.7-librca
gradle=9.1.0
50 changes: 50 additions & 0 deletions .circleci/agp9-smoke/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# AGP 9 smoke test for `purchases_ui_flutter`

A minimal Gradle-only fixture that exercises the AGP-9 branch of
`purchases_ui_flutter/android/build.gradle`, without needing Flutter
or a full app build.

## What it tests

`purchases_ui_flutter/android/build.gradle` switches behaviour based on the
detected Android Gradle Plugin major version:

- `agpMajor < 9` — applies the `kotlin-android` plugin and uses the
legacy `android.kotlinOptions { … }` DSL.
- `agpMajor >= 9` — relies on AGP's built-in Kotlin and uses the
top-level `kotlin { compilerOptions { … } }` DSL.

The first branch is exercised by every regular CI build (the
`purchase_tester` example pins AGP 8 / KGP 1.x).

The AGP 9 branch only runs when a downstream user is on a recent enough
Flutter (3.44+) and AGP 9. This fixture lets CI catch regressions on
that branch at configuration time.

## How it works

`settings.gradle` pins AGP `9.0.1` and KGP `2.2.10` via
`pluginManagement`, then includes the plugin's `android/` directory as
a subproject. The plugin's own `buildscript { }` declares a lower AGP
version; Gradle resolves the classpath to the highest declared version,
so the plugin's `build.gradle` is evaluated under AGP 9 here.

`run.sh` asserts:

1. `agpMajor == 9` after the plugin's `buildscript { }` runs — proves
the AGP-9 branches of the plugin's `build.gradle` are reached.
2. The `compileDebugKotlin` task graph resolves under `--dry-run` —
proves AGP applied cleanly and the new top-level
`kotlin.compilerOptions` DSL is valid.

This is a configuration-time smoke test, not a full build. It will not
catch issues that only manifest during actual compilation (e.g. Kotlin
source incompatibilities with KGP 2.x).

## Running locally

Requires JDK 21, Gradle 9.1+, and an Android SDK installed.

```bash
bash .circleci/agp9-smoke/run.sh
```
36 changes: 36 additions & 0 deletions .circleci/agp9-smoke/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
#
# Smoke-test purchases_ui_flutter/android/build.gradle under AGP 9.
#
# Uses the sibling settings.gradle, which pins AGP 9.0.1 + KGP 2.2.10
# via pluginManagement. The plugin's own buildscript declares a lower
# AGP version, but Gradle resolves the classpath to the highest, so
# the plugin's build.gradle is evaluated with AGP 9 here.
#
# Assertions:
# 1. agpMajor == 9 (so the AGP 9 else-branch in build.gradle ran).
# 2. compileDebugKotlin task graph resolves (built-in Kotlin works).
#
# This is a configuration-time smoke test, not a full build.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"

GRADLE_ARGS=(--no-daemon -q)

echo "==> Asserting agpMajor == 9"
agp_major="$(gradle "${GRADLE_ARGS[@]}" :purchases_ui_flutter:properties | awk '/^agpMajor: / {print $2}')"
if [[ "$agp_major" != "9" ]]; then
echo "FAIL: expected agpMajor=9 but got '${agp_major}'." >&2
echo " The AGP 9 branch of purchases_ui_flutter/android/build.gradle was not exercised." >&2
exit 1
fi
echo "OK: agpMajor=${agp_major}"

echo "==> Asserting compileDebugKotlin task graph resolves under AGP 9"
gradle "${GRADLE_ARGS[@]}" :purchases_ui_flutter:compileDebugKotlin --dry-run
echo "OK: AGP 9 + built-in Kotlin wired up correctly"

echo "==> AGP 9 smoke test passed"
30 changes: 30 additions & 0 deletions .circleci/agp9-smoke/settings.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Settings file for the AGP 9 smoke test of purchases_ui_flutter.
// See README.md in this directory for context.

pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}

// Pin the AGP and Kotlin Gradle Plugin versions used during this smoke
// test. The plugin's own buildscript declares a lower AGP version; Gradle
// resolves the buildscript classpath to the highest declared version, so
// the plugin gets evaluated with AGP 9 here.
plugins {
id "com.android.application" version "9.0.1" apply false
id "org.jetbrains.kotlin.android" version "2.2.10" apply false
}

dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}

rootProject.name = "agp9-smoke"
include ":purchases_ui_flutter"
project(":purchases_ui_flutter").projectDir = file("../../purchases_ui_flutter/android")
22 changes: 22 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,27 @@ jobs:
./gradlew app:assembleAndroidTest
./gradlew app:assembleDebug -Ptarget=`pwd`/../integration_test/app_test.dart

agp9-smoke-test:
description: "Smoke-test purchases_ui_flutter under AGP 9 (configuration-time only)"
docker:
- image: ghcr.io/cirruslabs/flutter:stable
resource_class: medium
steps:
- checkout
- revenuecat/install-sdkman
- run:
name: Install JDK 21 and Gradle 9.1 (required by AGP 9)
working_directory: .circleci/agp9-smoke
command: sdk env install
- run:
name: Print versions
command: |
java -version
gradle --version
- run:
name: Run AGP 9 smoke test
command: bash .circleci/agp9-smoke/run.sh

android-integration-test:
description: "Run Android integration tests for flutter"
<<: *android-machine-emulator
Expand Down Expand Up @@ -589,6 +610,7 @@ workflows:
- ios-integration-test-spm
- macos-integration-test-spm
- android-integration-test-build
- agp9-smoke-test
- run-maestro-e2e-tests-ios:
context:
- e2e-tests
Expand Down
8 changes: 0 additions & 8 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ group 'com.revenuecat.purchases_flutter'
version '10.2.0'

buildscript {
ext.kotlin_version = '1.8.22'
ext.common_version = '18.9.1'
repositories {
google()
Expand All @@ -11,7 +10,6 @@ buildscript {

dependencies {
classpath 'com.android.tools.build:gradle:8.13.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}

Expand All @@ -23,7 +21,6 @@ rootProject.allprojects {
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
compileSdk 34
Expand All @@ -38,10 +35,6 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = '1.8'
}

defaultConfig {
minSdkVersion 21
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand All @@ -64,6 +57,5 @@ android {
}

dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.revenuecat.purchases:purchases-hybrid-common:$common_version"
}
35 changes: 29 additions & 6 deletions purchases_ui_flutter/android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
group 'com.revenuecat.purchases_ui_flutter'
version '10.2.0'

// Apply the legacy kotlin-android plugin only when AGP isn't providing
// Kotlin itself — i.e. AGP < 9, or AGP 9+ with `android.builtInKotlin=false`. See:
// https://docs.flutter.dev/release/breaking-changes/migrate-to-built-in-kotlin/for-plugin-authors
buildscript {
ext.kotlin_version = '1.9.20'
ext.common_version = '18.9.1'
ext.agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0] as int
ext.builtInKotlinEnabled = (findProperty('android.builtInKotlin') ?: 'true') == 'true'
ext.applyLegacyKgp = agpMajor < 9 || !builtInKotlinEnabled
repositories {
google()
mavenCentral()
}

dependencies {
classpath 'com.android.tools.build:gradle:8.13.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
if (applyLegacyKgp) {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
}

Expand All @@ -23,7 +31,10 @@ allprojects {
}

apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

if (applyLegacyKgp) {
apply plugin: 'kotlin-android'
}

android {
if (project.android.hasProperty("namespace")) {
Expand All @@ -35,10 +46,6 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = '1.8'
}

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
Expand All @@ -56,3 +63,19 @@ android {
implementation 'androidx.compose.ui:ui-android:1.5.4'
}
}

// jvmTarget DSL matches the Kotlin plugin in use: `android.kotlinOptions`
// for kotlin-android, top-level `kotlin {}` for AGP's built-in Kotlin.
if (applyLegacyKgp) {
android {
kotlinOptions {
jvmTarget = '1.8'
}
}
} else {
kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8
}
}
}