how are people generally handling versioning with ...
# plugin-development
t
how are people generally handling versioning with their Gradle plugins? is anyone doing strict semver where any breaking change to the public API (including but not limited to the plugin's tasks) triggers a major version bump? I'm finding myself hesitant to do a major version bump for each such change because most people don't actually use the plugin programmatically and when they see a major new release they're hesitant to adopt it and I end up doing all kinds of comms around how it's safe and won't be difficult etc
m
I think it makes sense. This is what KGP has been doing for a while. Is the KGP API stable these days? Feels like it's changing a lot less often
But in all cases,
kotlin-gradle-plugin
is a lot less stable than
kotlin-stdlib
and I think it's fine?
In our case, we don't change the Gradle plugin so much so we can wait for the major versions of the runtime to do breaking changes in the Gradle plugin so I think it follows semver but if we really needed we would probably allow ourselves to do the occasional breaking change
> including but not limited to the plugin's tasks > I consider the tasks as implementation details even if they are technically accessible from the build scripts. Everything that is not in the extension may break any time.
o
Personally, I do not see much problem with bumping a major version every time for an API breaking change, but Tony has a point about non-API consumers and adoption. If major plugins are using your APIs in the downstream, it might be better to cut a major version, though. If not, maybe one could mark API as Incubating so that there is no strong commitment to maintain compatibility there - https://docs.gradle.org/current/javadoc/org/gradle/api/Incubating.html. Then you can still do Semver without doing it 🙂
t
I'm trying to "hide" things with package visibility these days, because Gradle doesn't really care that classes are public or not. This only leaves the actual API / DSL (extension and task types) public, and every breaking change there means a major bump.
☝️ 2
But overall, a major bump is cheap compared to a "minor bump with actual breaking changes", so be safe, just do major bumps for each and every breaking changes (for types that are clearly public API: anything that's publicly accessible but clearly marked as internal is not public: FooImpl, an "internal" package, etc. there's a limit to Hyrum's law 😉)
☝️ 1
1
(people hesitant to adopt a new version are just adding to their technical debt: their problem, not yours)
👍 1
💯 1
👍🏾 1
t
thanks everyone for the feedback! along these lines, what are you all doing for breaking ABI tracking? is there a good example I could borrow? I've been meaning to do that for ages but there's always a higher priority
m
a
Metalava can help track it too
plus1 2
m
We need a BCV vs Metalava showdown 🙂
m
We were using Metalava initially and then switched to BCV because it was first party but sounds like Metalava can do everything BCV does and even more these days
a
AGP is using metalava to track their source and binary compat
t
looking at the BCV link from above, I see this:
The development of a separate binary compatibility validator Gradle plugin has been discontinued, and all its functionality will be moved to Kotlin Gradle Plugin starting from the 2.2.0 release.
that's slightly annoying, but maybe it doesn't matter in practice. I'll take a look at metalava! thanks
Aurimas, is metalava "just" this binary?
Copy code
com.android.tools.metalava:metalava:1.0.0-alpha11
and then that Metalava.kt file you shared is one way in which one can use it?
a
metalava is a Java CLI tool

https://youtu.be/JKLqQiYh8GQ?si=UCwQEkoeLHZ1J4vD&t=1328

has details on the very basic invocation for it
thank you 1
m
@Aurimas Liutikas any thoughts about the metalava gradle plugin? https://github.com/tylerbwong/metalava-gradle
a
I haven't used it, so I don't have much to share. CLI is quote quite simple through so custom tasks are generally small
👍 1
t
omfg, metalava is so bloated. I don't use the
google()
repo for my build-logic, so I added it just for this dep, and this is what I ended up with in order to fully resolve it and all its dependencies
Copy code
exclusiveContent {
  forRepository {
    maven(url = "<https://dl.google.com/dl/android/maven2/>")
  }
  filter {
    includeGroup("com.android.tools.metalava")
    includeGroup("com.android.tools")
    includeGroup("com.android.tools.layoutlib")
    includeGroup("com.android.tools.ddms")
    includeGroup("com.android.tools.build")
    includeGroup("com.android.tools.analytics-library")
    includeGroup("com.android.tools.lint")
    includeGroup("com.android.tools.external.com-intellij")
    includeGroup("com.android.tools.external.org-jetbrains")
  }
}
a
we build on top of android lint
feel free to use BCV if that's too much for you
t
I had to download 198 MiB worth of jars to resolve this
😮 2
a
most of it is intellij
t
I'm not trying to be a jerk. I'm just pointing out that this is a very large dependency
a
this is pretty much headless intelij
t
it's bizarre seeing android's layoutlib show up for this use case. Feels like some restructuring of the modules in question might be useful
a
why is your CI fetching sources
t
that's not CI, it's a local IDE sync
a
got it
you can forward all of your complains to android lint team
t
haha
noted
a
metalava itself is fairly light on depenencies outside of lint
t
seems like it ought to be, which is why I was surprised
t
Aurimas, I'm iterating on the metalava stuff and got this error. Any idea why?
Copy code
> Task :metalava
Exception in thread "main" java.lang.IllegalStateException: Invalid type in API surface: PsiType:<ErrorType> for element TYPE_REFERENCE in file /User
s/trobalik/Development/dependency-analysis-gradle-plugin/src/main/kotlin/com/autonomousapps/internal/utils/moshi.kt:106
        at com.android.tools.metalava.model.psi.PsiTypeItemFactory.createTypeItem(PsiTypeItemFactory.kt:318)
Here's the code in question:
Copy code
// the function definition is line 106 from the stacktrace
inline fun <reified K, reified V> File.bufferWriteJsonMap(
  set: Map<K, V>,
  withNulls: Boolean = false,
  indent: String = noJsonIndent
) {
  JsonWriter.of(sink().buffer()).use { writer ->
    getJsonMapAdapter<K, V>(withNulls).indent(indent).toJson(writer, set)
  }
}
I'm using this as a guide
oh I wonder if I'm missing the JDK itself when configuring the classpath, per this from the KSP link
Copy code
val jdkHome = org.gradle.internal.jvm.Jvm.current().javaHome.absolutePath
yup! task passes now rubberduck
uhhh why are the generated API stubs including reference to androidx annotations, which my (Gradle plugin) code does not use?
Copy code
public AbstractExtension(@androidx.annotation.NonNull org.gradle.api.model.ObjectFactory objects
ah ok, that would be this line