Skip to content

Commit d6d89a6

Browse files
Move from bcv plugin to KGP api validation (#6362)
* build: Move from the binary-compatibility-validator plugin to KGP ABI validation * build: Validate the ABI of sentry-opentelemetry-agentless Co-Authored-By: Markus Hintersteiner <markus.hintersteiner@sentry.io> * build: Drop the apiDump and apiCheck aliases in favour of the KGP task names * build: Restore the root build file changes from main lost in the merge * build: Chain the ABI filters onto the abiValidation lookup --------- Co-authored-by: sentry-junior[bot] <264270552+sentry-junior[bot]@users.noreply.github.com>
1 parent 16a1cdd commit d6d89a6

67 files changed

Lines changed: 129 additions & 69 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.claude/skills/create-java-pr/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ Derive the branch name from the changes being made. Use `feat/`, `fix/`, `ref/`,
7474
## Step 2: Format Code and Regenerate API Files
7575

7676
```bash
77-
./gradlew spotlessApply apiDump
77+
./gradlew spotlessApply updateKotlinAbi
7878
```
7979

8080
This is **required** before every PR in this repo. It formats all Java/Kotlin code via Spotless and regenerates the `.api` binary compatibility files.

‎.cursor/BUGBOT.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,6 @@ rule file in [`.cursor/rules/`](rules) for the area the diff touches (`api`, `op
120120
## What NOT to flag
121121

122122
- Formatting and import order — Spotless owns it.
123-
- Contents of generated `.api` files, beyond confirming `apiDump` was run.
123+
- Contents of generated `.api` files, beyond confirming `updateKotlinAbi` was run.
124124
- Conventional commit / PR title format, and missing changelog entries — CI and Danger check both.
125125
- Speculative refactors or improvements unrelated to the diff.

‎.cursor/rules/api.mdc‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ description: Public API surface, binary compatibility, and common classes to mod
66

77
## API Compatibility
88

9-
Public API is tracked via `.api` files generated by the [Binary Compatibility Validator](https://github.com/Kotlin/binary-compatibility-validator) Gradle plugin. Each module has its own file at `<module>/api/<module>.api`.
9+
Public API is tracked via `.api` files generated by the [ABI validation](https://kotlinlang.org/docs/gradle-binary-compatibility-validation.html) built into the Kotlin Gradle plugin. Each module has its own file at `<module>/api/<module>.api`.
1010

11-
- **Never edit `.api` files manually.** Run `./gradlew apiDump` to regenerate them.
11+
- **Never edit `.api` files manually.** Run `./gradlew updateKotlinAbi` to regenerate them.
1212
- `./gradlew check` validates current code against `.api` files and fails on unintended changes.
1313
- `@ApiStatus.Internal` marks classes/methods as internal — they still appear in `.api` files but are not part of the public contract.
1414
- `@ApiStatus.Experimental` marks API that may change in future versions.

‎.cursor/rules/options.mdc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,4 @@ When a feature has its own capture path (e.g. `captureLog`), the relevant classe
112112
4. `ManifestMetadataReader.java` — Android manifest support (if Android-relevant)
113113
5. `SentryAutoConfigurationTest.kt` — Spring Boot property binding tests (all three Spring Boot modules)
114114
6. Tests for all of the above (`SentryOptionsTest`, `ExternalOptionsTest`, `ManifestMetadataReaderTest`)
115-
7. Run `./gradlew apiDump` — the nested class and its methods appear in `sentry.api`
115+
7. Run `./gradlew updateKotlinAbi` — the nested class and its methods appear in `sentry.api`

‎AGENTS.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ rule file in `.cursor/rules/`:
99

1010
| Rule | Read it when working on |
1111
|---|---|
12-
| `api` | Public API surface, binary compatibility, `.api` files, `apiDump`, `IScope`/`IScopes`/`Sentry` static API, protocol classes |
12+
| `api` | Public API surface, binary compatibility, `.api` files, `updateKotlinAbi`, `IScope`/`IScopes`/`Sentry` static API, protocol classes |
1313
| `options` | `SentryOptions`, namespaced options, `ExternalOptions`, `sentry.properties`, `ManifestMetadataReader`, Spring Boot properties |
1414
| `scopes` | Scope management, forking, lifecycle, `ScopeType`, thread-local storage, scope bleeding, Hub → Scopes migration |
1515
| `deduplication` | Duplicate event detection, `DuplicateEventDetectionEventProcessor`, `enableDeduplication` |
@@ -42,7 +42,7 @@ The project uses **Gradle** with Kotlin DSL. Key build files:
4242

4343
```bash
4444
# Format code and regenerate .api files (REQUIRED before committing)
45-
./gradlew spotlessApply apiDump
45+
./gradlew spotlessApply updateKotlinAbi
4646

4747
# Run all tests and linter
4848
./gradlew check
@@ -74,7 +74,7 @@ resolves the per-module test task, including integration test suites, for you.
7474
3. **Use todo tracking**: Work through todo items, marking them as complete as you go
7575
4. **High-level communication**: Give high-level explanations of changes made, not step-by-step descriptions
7676
5. **Simplicity first**: Make every task and code change as simple as possible. Avoid massive or complex changes. Impact as little code as possible.
77-
6. **Format and regenerate**: Once done, format code and regenerate .api files: `./gradlew spotlessApply apiDump`
77+
6. **Format and regenerate**: Once done, format code and regenerate .api files: `./gradlew spotlessApply updateKotlinAbi`
7878
7. **Propose commit**: As final step, git stage relevant files and propose (but not execute) a single git commit command. This applies to implementation work; when the task is to open a PR, the `create-java-pr` skill takes over from here and does commit, push, and open it.
7979

8080
## Repository Skills

‎CONTRIBUTING.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ or
8181

8282
# Binary compatibility validation
8383

84-
To prevent breaking ABI changes and exposing things we should not, we make use of https://github.com/Kotlin/binary-compatibility-validator. If your change intended to introduce a new public method/property or modify the existing one you can overwrite the API declarations to make CI happy as follows (overwrites them from scratch):
84+
To prevent breaking ABI changes and exposing things we should not, we make use of https://kotlinlang.org/docs/gradle-binary-compatibility-validation.html. If your change intended to introduce a new public method/property or modify the existing one you can overwrite the API declarations to make CI happy as follows (overwrites them from scratch):
8585

8686
```shell
8787
make api
@@ -90,7 +90,7 @@ make api
9090
or
9191

9292
```shell
93-
./gradlew apiDump
93+
./gradlew updateKotlinAbi
9494
```
9595

9696
However, if your change did not intend to modify the public API, consider changing the method/property visibility or removing the change altogether.

‎Makefile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ checkFormat:
3232

3333
# Binary compatibility validator
3434
api:
35-
./gradlew apiDump
35+
./gradlew updateKotlinAbi
3636

3737
# Assemble release and Android test apk of the uitest-android module
3838
assembleUiTestRelease:

‎build-logic/build.gradle.kts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ gradlePlugin {
1616
id = "io.sentry.jdk-target"
1717
implementationClass = "io.sentry.gradle.JdkTargetConvention"
1818
}
19+
register("sentryApiValidation") {
20+
id = "io.sentry.api-validation"
21+
implementationClass = "io.sentry.gradle.SentryApiValidationPlugin"
22+
}
1923
register("sentryAnimalSniffer") {
2024
id = "io.sentry.animalsniffer"
2125
implementationClass = "io.sentry.gradle.SentryAnimalSnifferPlugin"
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.sentry.gradle
2+
3+
import org.gradle.api.provider.SetProperty
4+
5+
/** Configuration for the `io.sentry.api-validation` convention plugin. */
6+
abstract class ApiValidationExtension {
7+
/**
8+
* Packages that are public to the JVM but not part of the SDK's API, and so are left out of the
9+
* dump. Subpackages are left out too.
10+
*/
11+
abstract val ignoredPackages: SetProperty<String>
12+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package io.sentry.gradle
2+
3+
import org.gradle.api.GradleException
4+
import org.gradle.api.Plugin
5+
import org.gradle.api.Project
6+
import org.gradle.api.plugins.ExtensionAware
7+
import org.gradle.kotlin.dsl.create
8+
import org.jetbrains.kotlin.gradle.dsl.abi.AbiValidationExtension
9+
import org.jetbrains.kotlin.gradle.dsl.abi.AbiValidationMultiplatformExtension
10+
import org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation
11+
12+
private val KOTLIN_PLUGIN_IDS =
13+
listOf(
14+
"org.jetbrains.kotlin.jvm",
15+
"org.jetbrains.kotlin.android",
16+
"org.jetbrains.kotlin.multiplatform",
17+
)
18+
19+
/**
20+
* Checks the public ABI of a module against the dump checked into its `api` directory, using the
21+
* Kotlin Gradle plugin's own ABI validation. Adds `checkKotlinAbi`, which `check` depends on, and
22+
* `updateKotlinAbi`, which rewrites the dump.
23+
*/
24+
@OptIn(ExperimentalAbiValidation::class)
25+
class SentryApiValidationPlugin : Plugin<Project> {
26+
override fun apply(project: Project) {
27+
val extension = project.extensions.create<ApiValidationExtension>("sentryApiValidation")
28+
29+
// The abiValidation extension only exists once a Kotlin plugin has been applied, and a module
30+
// may list this plugin before that one.
31+
KOTLIN_PLUGIN_IDS.forEach {
32+
project.pluginManager.withPlugin(it) { enable(project, extension) }
33+
}
34+
}
35+
36+
private fun enable(project: Project, extension: ApiValidationExtension) {
37+
val kotlin = project.extensions.getByName("kotlin") as ExtensionAware
38+
when (val abi = kotlin.extensions.getByName("abiValidation")) {
39+
is AbiValidationMultiplatformExtension -> abi.also { it.enabled.set(true) }
40+
is AbiValidationExtension -> abi.also { it.enabled.set(true) }
41+
else -> throw GradleException("Unexpected abiValidation extension: ${abi.javaClass.name}")
42+
}.filters {
43+
// Lazy, because the module configures the extension after this plugin has been applied.
44+
exclude { byNames.addAll(extension.ignoredPackages.map { it.map { pkg -> "$pkg.**" } }) }
45+
}
46+
}
47+
}

0 commit comments

Comments
 (0)