Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Kotlin Multiplatform support with a Compiler plugin #46

Merged
merged 29 commits into from
Dec 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d2ff103
Changed plugin id to `io.github.gmazzo.codeowners.jvm`
gmazzo Dec 18, 2023
638a907
Changed plugin id to `io.github.gmazzo.codeowners.kotlin`
gmazzo Dec 3, 2023
a43290c
Fixed issues after merging with `.jvm` approach
gmazzo Dec 20, 2023
a4f49b1
Removed `codeOwners` property from jvm extension
gmazzo Dec 20, 2023
3d86eb5
Extracted common logic to `base-plugin`
gmazzo Dec 20, 2023
64090d6
Renamed `plugin` folder to `plugins`
gmazzo Dec 20, 2023
dc732d7
Renamed `demo-project` folder to `demo-project-kotlin`
gmazzo Dec 20, 2023
12823d9
Removed `addCoreDependency` property on JVM plugin
gmazzo Dec 20, 2023
8606c52
Added `demo-project-jvm`
gmazzo Dec 20, 2023
a41cc06
Added test tasks for mappings on demo project
gmazzo Dec 20, 2023
8187265
Added test tasks for mappings on demo project
gmazzo Dec 20, 2023
efbffaf
Migrated mapping tests to its own project
gmazzo Dec 21, 2023
8f7df6a
Iterated plugin wrong usage logic
gmazzo Dec 21, 2023
7535a1d
Generating reports from base plugin
gmazzo Dec 22, 2023
c6184e0
CodeOwners report for classes (instead of files)
gmazzo Dec 23, 2023
e54f8fa
`CodeOwnersSourceSet` refactor
gmazzo Dec 23, 2023
3eb5761
Repackaging demos
gmazzo Dec 24, 2023
3299c09
Added output checks for reports task
gmazzo Dec 24, 2023
7ca4a2b
Added warning when Kotlin version does not match
gmazzo Dec 24, 2023
8664389
Renamed `codeOwnersXXXReport` name pattern
gmazzo Dec 24, 2023
5dbb4bb
Fixing missing Android classes on general report
gmazzo Dec 24, 2023
e6ec1cc
Added kdoc
gmazzo Dec 24, 2023
c3b0e27
Renamed `CodeOwnersTask` to `CodeOwnersResourcesTask`
gmazzo Dec 24, 2023
9ab6d28
Added compatibility tests
gmazzo Dec 24, 2023
86ee77d
Renamed `addCodeOwnershipAsResources` to `enabled`
gmazzo Dec 25, 2023
87aeb5c
UPDATED `README.md`
gmazzo Dec 25, 2023
b785031
Added `CodeOwnersMatcherTest`
gmazzo Dec 25, 2023
4bae2ff
Migrated to `macos-latest` for native/iOS builds
gmazzo Dec 25, 2023
26dec61
Fixed `yarn` issue on CI
gmazzo Dec 25, 2023
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: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ permissions:
jobs:
build:
name: Build
runs-on: ubuntu-latest
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
release:
name: Release
needs: build
runs-on: ubuntu-latest
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.gradle/
.idea/
build/
kotlin-js-store/
6 changes: 3 additions & 3 deletions .run/All tests.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="scriptParameters" value="--continue" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="test" />
<option value="integrationTest" />
<option value="check" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>false</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>true</RunAsTest>
<method v="2" />
</configuration>
</component>
1 change: 1 addition & 0 deletions .yarnrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
--install.mutex network
57 changes: 57 additions & 0 deletions README-jvm.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# The `io.github.gmazzo.codeowners.jvm` plugin
Propagates CODEOWNERS to JVM classes, by generating a package-level `.codeowners` resource files.

> [!NOTE]
> This plugin aggregates the `io.github.gmazzo.codeowners` plugin, so you don't need to apply it too.

# How it works
This plugin is mean to work at JVM level: compatible with `java`, `groovy` and `kotlin` and Android.

The plugin binds by default on the compilation toolchain, inspecting the source folders and generating a set of java `.codeowners` resources that later the provided `xxx.codeowners` function will use to resolve the ownership information (or `getCodeOwners` on Java, see [Usage](#usage))

You can later query its ownership information as described at [Usage](./README.md#usage).

## Caveats on the approach
The plugin will do its best to provide a reliable owners attribution on classes, even by inspecting dependencies looking for collision of packages.

For instance, multiple source folders
- `src/main/java/com/test/myapp/aaa`
- `src/main/kotlin/com/test/myapp/bbb`

may be contributing to the same `com.test.myapp` package.

If you `.CODEOWNERS` file looks similar to this:
```
aaaOwner src/*/java
bbbOwner src/*/kotlin
```
The final owners for classes located at the given package will be:

| pacakge | owners |
|----------------------|---------------------------|
| `com.test.myapp` | `aaaOwner` and `bbbOwner` |
| `com.test.myapp.aaa` | `aaaOwner` |
| `com.test.myapp.bbb` | `bbbOwner` |

## General advices for structuring CODEOWNERS file for this plugin
Given the known limitations on the JVM resources approach, here is a list tips to have a 100% owners attributes accuracy:
1) Use a dedicated Java package per Gradle module
2) Prefer directory patterns over file extension ones

## Disable it for specific `SourceSet`s (Java)
You can use the `codeOwners.enabled` property to configure it.
For instance, the following code will disable it for the `test` source set:
```kotlin
sourceSets.test {
codeOwners.enabled = false
}
```

## Disable it for specific `Variant`s (Android)
You can use the `codeOwners.enabled` property to configure it.
For instance, the following code will enable it only for variants of `debug` build type:
```kotlin
androidComponents.onVariants { variant ->
variant.codeOwners.enabled = false
}
```
33 changes: 33 additions & 0 deletions README-kotlin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# The `io.github.gmazzo.codeowners.jvm` plugin
Propagates CODEOWNERS to Kotlin classes, by generating a `@CodeOwners` annotation on each Kotlin Class/File at compilation time.

> [!NOTE]
> This plugin aggregates the `io.github.gmazzo.codeowners` plugin, so you don't need to apply it too.

# How it works
When applies, it binds a Kotlin IR compiler plugin that hooks on the compilation process, and it will decorate all processed Kotlin classes/files with a `@CodeOwners` (or `@file:CodeOwners`) annotation.

You can later query its ownership information as described at [Usage](./README.md#usage).

## Disable it for specific `KotlinTarget`s
You can use the `codeOwners.enabled` property to configure it.
For instance, the following code will disable it for the `jvm` target:
```kotlin
kotlin.targets.named("jvm") {
codeOwners.enabled = false
}
```

## Disable it for specific `KotlinCompilation`s
You can use the `codeOwners.enabled` property to configure it.
For instance, the following code will disable it for any non-`main` compilation of any `KotlinTarget` target:
```kotlin
kotlin.targets.configureEach {
compilations.configureEach {
codeOwners.enabled = name == "main"
}
}
```

> [!NOTE]
> It supports any Kotlin subplugin: `jvm`, `android`, including `multiplatform` ones targeting `native` or `js` platforms.
15 changes: 15 additions & 0 deletions README-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# The `io.github.gmazzo.codeowners` plugin
Generates `codeOwnersReport` a task that produces a `.CODEOWNERS`-like file with class names:
```CODEOWNERS
# CodeOwners of module ':demo-project-jvm:app'

org/test/jvm/app/AppClass app-devs
org/test/jvm/app/AppOwnersTest test-devs
org/test/jvm/app/BuildConfig app-devs
org/test/jvm/app/test/BuildConfig app-devs
org/test/jvm/utils/AppUtils app-devs
```
This plugin integrates with Java, Android and Kotlin plugins, generating dedicated `codeOwnersXXXReport` tasks per source set/variant:
- For Java, creates a task per `SourceSet`
- For Android, creates a task per `Variant` (including their `UnitTest` and `AndroidTest`s)
- For Kotlin, creates a task per `KotlinSourceSet`. Supports any Kotlin subplugin: `jvm`, `android`, and `multiplatform`
129 changes: 27 additions & 102 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,48 @@
![GitHub](https://img.shields.io/github/license/gmazzo/gradle-codeowners-plugin)
[![Maven Central](https://img.shields.io/maven-central/v/io.github.gmazzo.codeowners/core)](https://central.sonatype.com/artifact/io.github.gmazzo.codeowners/core)
[![Maven Central](https://img.shields.io/maven-central/v/io.github.gmazzo.codeowners/matcher)](https://central.sonatype.com/artifact/io.github.gmazzo.codeowners/matcher)
[![Gradle Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/io.github.gmazzo.codeowners)](https://plugins.gradle.org/plugin/io.github.gmazzo.codeowners)
[![Build Status](https://github.com/gmazzo/gradle-codeowners-plugin/actions/workflows/build.yaml/badge.svg)](https://github.com/gmazzo/gradle-codeowners-plugin/actions/workflows/build.yaml)
[![Coverage](https://codecov.io/gh/gmazzo/gradle-codeowners-plugin/branch/main/graph/badge.svg?token=ExYkP1Q9oE)](https://codecov.io/gh/gmazzo/gradle-codeowners-plugin)

# gradle-codeowners-plugin
A Gradle plugin to propagate CODEOWNERS to JVM classes
A Gradle plugin to propagate CODEOWNERS information to classes

It consists on 3 different plugins, each one providing a different set of features and targeting different use cases:
1) [The `io.github.gmazzo.codeowners` plugin](README-report.md) adds a Gradle report of classes' ownership
2) [The `io.github.gmazzo.codeowners.jvm` plugin](README-jvm.md) propagates the classes' ownership information to runtime. It supports any JVM build (`java`, `groovy`, etc) that produces `.class` files, but **JVM-only**
3) [The `io.github.gmazzo.codeowners.kotlin` Kotlin Compiler plugin](README-kotlin.md) propagates the classes' ownership information to runtime. It supports any Kotlin build (`jvm`, `android`, `multiplatform`, etc, but **Kotlin-only**)

# Usage
This plugin is designed to work as a whole build plugin, but you can selectively apply it to target the modules where you actually care about CODEOWNERS propagation.

The simplest setup is to apply the plugin at the root project, and then to each submodule. At root `build.gradle.kts` add:
```kotlin
plugins {
id("io.github.gmazzo.codeowners") version "<latest>"
id("io.github.gmazzo.codeowners") version "<latest>"
}

subprojects {
apply(plugin = "io.github.gmazzo.codeowners")
}
```

You should apply the right plugins according to your needs:
- `io.github.gmazzo.codeowners` (_Report_) is for the generating codeowners-like file by Java/Kotlin classes names
- `io.github.gmazzo.codeowners.jvm` (_JVM_) is for propagating codeowners info to JVM-only projects
- `io.github.gmazzo.codeowners.kotlin` (_Kotlin_) (recommended) is for propagating codeowners info to for **pure Kotlin** projects (including JVM and multiplatform)

| Plugin / Feature | _Report_ | _JVM_ | _Kotlin_ |
|----------------------------------------------------------------------|----------|-------|----------|
| Generates class-like reports at build time | ✅ | ✅ * | ✅ * |
| Propagates codeowners info to runtime | ❌ | ✅ | ✅ |
| Works with JVM projects | ✅ | ✅ | ✅ |
| Works with Multiplatform projects | ❌ | ❌ | ✅ |
| Acurrancy:<br/>Codeowners info matches alwats the original file ones | 🟢 | 🟡 ** | 🟢 |

(*) inherited from `io.github.gmazzo.codeowners` (_Report_) plugin<br/>
(**) because how the Java Resources API on JVM the ownership information may be inaccurate in some cases. See [Caveats on the approach](./README-jvm.md#caveats-on-the-approach) for more details.

## Getting ownership information at runtime
Later, you can query a class's owner by:
```kotlin
val ownersOfFoo = codeOwnersOf<Foo>()
Expand Down Expand Up @@ -63,75 +85,10 @@ subprojects {
apply(plugin = "io.github.gmazzo.codeowners")
}
```
You must apply the plugin on every project that has source files. Those classes won't be attributed otherwise.
You must apply the plugin on **every project that has source files**. Those classes won't be computed otherwise.
Applying it at the root project only, will only make sense on single module builds.

## Disable it for specific `SourceSet`s (Java)
You can use the `sourceSet.codeOwners.enabled` property to configure it.
For instance, the following code will disable it `test`:
```kotlin
sourceSets.test {
codeOwners.enabled.set(false)
}
```

## Disable it for specific `Variant`s (Android)
You can use the `variant.codeOwners.enabled` property to configure it.
For instance, the following code will enable it only for `debug`:
```kotlin
androidComponents.onVariants {
it.codeOwners.enabled.set(it.buildType == "debug")
}
```

## Excluding default `io.github.gmazzo.codeowners:core` dependency
By default, a dependency to this plugin runtime DSL (the `.codeOwners` functions) will be added to those `SourceSet`s where CodeOwners are computed.

You can opt out of this behavior by adding `codeowners.default.dependency=false` to your `gradle.properties` file and then manually add it on the `Configuration` that fits better for your build:
```kotlin
dependencies {
implementation("io.github.gmazzo.codeowners:core")
}
```

## Consuming the generated `mappedCodeOwnersFile`
Each `CodeOwnersTask` produces a .CODEOWNERS like file which translates build directories to Java packages (in folder format, not '.').

To explain this better, given a `.CODEOWNERS` file:
```
src/main/java @java-devs
src/main/kotlin @koltin-devs
```
And some source files:
```
src/main/java/org/example/app/App.java
src/main/java/org/example/foo/Foo.java
src/main/kotlin/org/example/app/AppKt.kt
src/main/kotlin/org/example/bar/Bar.kt
```
The generated `mappedCodeOwnersFile` will contain
```
org/example/app @koltin-devs @java-devs
org/example/foo @java-devs
org/example/bar @koltin-devs
```

You could use these files up feed any observability service for instance, allowing to have owners attributions given a package name/path.

In case you want to generate these but don't want to pollute/expose your production code with the ownership information, you use the `addCodeOwnershipAsResources` DSL to prevent the resources to be added (still generating the mapping files):
```kotlin
codeOwners.addCodeOwnershipAsResources.set(false)
```

To consume the `mappedCodeOwnersFile`, use the `.map`Property API, to ensure task dependencies are correctly computed, for instance:
```kotlin
tasks.register<Copy>("collectMappingFiles") {
from(tasks.named("generateCodeOwnersResources").flatMap { it.mappedCodeOwnersFile })
into(project.layout.buildDirectory.dir("mappings"))
}
```

## The CODEOWNERS file
# The CODEOWNERS file
The expected format is the same as [GitHub's](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-syntax) and it can be located at any of the following paths:
- `$rootDir/CODEOWNERS`
- `$rootDir/.github/CODEOWNERS`
Expand All @@ -142,35 +99,3 @@ The expected format is the same as [GitHub's](https://docs.github.com/en/reposit
```kotlin
codeOwners.codeOwnersFile.set(layout.projectDirectory.file("somePath/.CODEOWNERS"))
```

# How it works
This plugin is mean to work at JVM level: compatible with `java`, `groovy` and `kotlin` and Android.

The plugin binds by default on the compilation toolchain, inspecting the source folders and generating a set of java `.codeowners` resources that later the provided `xxx.codeowners` function will use to resolve the ownership information (or `getCodeOwners` on Java, see [Usage](#usage))

## Caveats on the approach
The plugin will do its best to provide a reliable owners attribution on classes, even by inspecting dependencies looking for collision of packages.

For instance, multiple source folders
- `src/main/java/com/test/myapp/aaa`
- `src/main/kotlin/com/test/myapp/bbb`

may be contributing to the same `com.test.myapp` package.

If you `.CODEOWNERS` file looks similar to this:
```
aaaOwner src/*/java
bbbOwner src/*/kotlin
```
The final owners for classes located at the given package will be:

| pacakge | owners |
|----------------------|---------------------------|
| `com.test.myapp` | `aaaOwner` and `bbbOwner` |
| `com.test.myapp.aaa` | `aaaOwner` |
| `com.test.myapp.bbb` | `bbbOwner` |

## General advices for structuring CODEOWNERS file for this plugin
Given the known limitations on the JVM resources approach, here is a list tips to have a 100% owners attributes accuracy:
1) Use a dedicated Java package per Gradle module
2) Prefer directory patterns over file extension ones
41 changes: 19 additions & 22 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,33 +1,30 @@
plugins {
base
`maven-publish`
alias(libs.plugins.android.application) apply false
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.gradle.nexusPublish)
alias(libs.plugins.kotlin.multiplatform) apply false
alias(libs.plugins.publicationsReport)
`git-versioning`
id("io.github.gmazzo.codeowners.jvm") apply false
id("io.github.gmazzo.codeowners.kotlin") apply false
}

nexusPublishing {
repositories {
sonatype {
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
}
}
}

allprojects {
group = "io.github.gmazzo.codeowners"
val pluginsBuild = gradle.includedBuild("plugins")

plugins.withId("jacoco") {
val jacocoTasks = tasks.withType<JacocoReport>()
tasks.build {
dependsOn(pluginsBuild.task(":$name"))
}

jacocoTasks.configureEach {
reports.xml.required = true
}
tasks.check {
dependsOn(pluginsBuild.task(":$name"))
}

tasks.check {
dependsOn(jacocoTasks)
}
}
tasks.publish {
dependsOn(pluginsBuild.task(":$name"))
}

tasks.publishToMavenLocal {
dependsOn(pluginsBuild.task(":$name"))
}
15 changes: 0 additions & 15 deletions buildSrc/build.gradle.kts

This file was deleted.

Loading