No description
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
makai.lester f32c9648f4 Fix: KMP Expect/Actual Analysis (#3)
## Summary

- Update to Detekt 2.0.0-alpha.6 and keep KMP `detektAll` on syntax-only and source-set tasks by default, avoiding expect/actual, generated serializer, and BuildConfig analysis failures.
- Add explicit DSL and Gradle property opt-ins for compilation-wide KMP type resolution while leaving those tasks directly runnable.
- Rework the custom rules to use conservative PSI and import matching, with unit and functional regression coverage plus 0.1.4 release documentation.

Co-authored-by: Makai Lester <support@makailester.com>
Reviewed-on: #3
2026-09-02 03:46:07 +00:00
.forgejo/workflows Initial commit 2026-08-30 17:53:13 -07:00
gradle Fix: KMP Expect/Actual Analysis (#3) 2026-09-02 03:46:07 +00:00
gradle-plugin Fix: KMP Expect/Actual Analysis (#3) 2026-09-02 03:46:07 +00:00
rules Fix: KMP Expect/Actual Analysis (#3) 2026-09-02 03:46:07 +00:00
.gitignore Initial commit 2026-08-30 17:53:13 -07:00
AGENTS.md Initial commit 2026-08-30 17:53:13 -07:00
build.gradle.kts Fix: KMP Expect/Actual Analysis (#3) 2026-09-02 03:46:07 +00:00
CHANGELOG.md Fix: KMP Expect/Actual Analysis (#3) 2026-09-02 03:46:07 +00:00
detekt.yml Initial commit 2026-08-30 17:53:13 -07:00
gradle.properties Initial commit 2026-08-30 17:53:13 -07:00
gradlew Initial commit 2026-08-30 17:53:13 -07:00
gradlew.bat Initial commit 2026-08-30 17:53:13 -07:00
LICENSE.txt Initial commit 2026-08-30 17:53:13 -07:00
README.md Fix: KMP Expect/Actual Analysis (#3) 2026-09-02 03:46:07 +00:00
settings.gradle.kts Initial commit 2026-08-30 17:53:13 -07:00

detekt-conventions

Reusable Detekt rules and Gradle conventions for Kotlin JVM, Kotlin Multiplatform, and Android Kotlin projects.

Artifacts

Purpose Identifier
Detekt rules com.makailester:detekt-rules:0.1.4
Gradle plugin implementation com.makailester:detekt-gradle-plugin:0.1.4
Gradle plugin com.makailester.detekt
Detekt rule set makai

The Gradle plugin applies the compatible Detekt version and adds the rules artifact to detektPlugins. Consumers should not apply dev.detekt separately.

Using the plugin

Add Maven Central to plugin resolution in settings.gradle.kts. The dependency repository is also needed because the plugin loads the rules artifact at runtime:

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

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

Add a version-catalog alias:

[versions]
makai-detekt = "0.1.4"

[plugins]
makai-detekt = { id = "com.makailester.detekt", version.ref = "makai-detekt" }

Apply the plugin in every Kotlin module:

plugins {
    alias(libs.plugins.makai.detekt)
}

Applying the same plugin to the root project adds a root detektAll task. That task aggregates detektAll from every subproject that also applies the plugin. In JVM and Android modules, a module-level detektAll task runs every Detekt analysis task registered for that module. check depends on detektAll.

Kotlin Multiplatform type resolution

In KMP modules, detektAll runs Detekt's syntax-only detekt and source-set tasks by default. It leaves compilation-wide type-resolution tasks such as detektMainJvm out of the aggregate. Detekt currently analyzes expect and actual declarations as one module, and its analysis cannot see some compiler-generated declarations such as kotlinx serialization's serializer(). Android-generated BuildConfig can fail for the same reason. The source-set tasks still run the packaged rules because those rules use Kotlin syntax and unambiguous imports instead of compiler analysis.

The compilation-wide tasks remain available to run directly. A project that accepts the known compiler-analysis errors can also put them back into detektAll with the DSL:

makaiDetekt {
    runKmpTypeResolutionTasks.set(true)
}

CI can opt in without editing a build script:

./gradlew detektAll -Pmakai.detekt.runKmpTypeResolutionTasks=true

The property accepts only true or false. An explicit DSL value takes precedence over the Gradle property.

The plugin reads optional detekt.yml and detekt.yaml files from the root project and the current module. Module configuration is loaded after root configuration. The packaged defaults provide the shared core-rule policy and enable both custom rules:

  • ExplicitSerialName requires @SerialName on serialized properties and enum entries.
  • NoEnsureNotNull rejects Arrow's ensureNotNull in favor of an Elvis expression with raise, or ensure when only presence needs checking.

Both custom rules skip a reference when imports or local declarations make its target ambiguous. This avoids reporting an unrelated annotation or function that happens to use the same short name.

The shared policy enables named-argument and unnamed-parameter checks with exceptions for idiomatic Kotlin, Ktor, serialization, and Exposed APIs. It also carries the common complexity, naming, style, coroutine, and exception choices. Consumer configuration should contain only project-specific overrides.

Baselines live in each module's detekt-baselines directory. The file name follows the analysis task, such as main.xml or main-jvm.xml.

Development

Use JDK 21. Analyze the repository before running tests:

./gradlew detektAll
./gradlew test functionalTest

functionalTest first publishes all artifacts to build/test-maven-repository. Its isolated JVM and KMP fixtures then resolve the plugin through the normal Gradle plugin marker.

Publishing

Maven Central

The Maven Central release contains these publications in one deployment:

  • com.makailester:detekt-rules
  • com.makailester:detekt-gradle-plugin
  • com.makailester.detekt:com.makailester.detekt.gradle.plugin

Before the first upload, verify the com.makailester namespace in the Central Portal with a DNS TXT record on makailester.com. Generate a Portal user token and a password-protected OpenPGP signing key. Publish the public key to a public keyserver.

The Upload Maven Central release workflow needs four repository secrets:

Secret name Value
MAVEN_CENTRAL_USERNAME Portal token username
MAVEN_CENTRAL_PASSWORD Portal token password
MAVEN_SIGNING_KEY ASCII-armored private key
MAVEN_SIGNING_PASSWORD Private-key passphrase

Do not commit these values. The workflow disables Gradle's configuration cache while it handles them.

Check the release metadata locally before dispatching the workflow:

./gradlew validateReleaseVersion centralPublicationCheck

Run nmcpZipAggregation with the signing properties configured to inspect the exact upload at build/nmcp/zip/aggregation.zip. The workflow performs this check after analysis and tests, then uploads a USER_MANAGED deployment. Review the validated deployment in the Central Portal before clicking Publish. Maven Central versions are immutable.

Forgejo mirror

Forgejo publishing reads forgejoToken or FORGEJO_TOKEN. Review the local publications and tests before running:

./gradlew publishAllPublicationsToForgejoRepository

For Forgejo Actions, create a personal access token owned by makai.lester. Set package to Read and write (write:package) and leave every other permission at No access. Package tokens cannot use the Specific repositories restriction. Use Public only when the package owner is public, or All otherwise.

Add the token as the MAVEN_PACKAGE_TOKEN repository secret, then manually run the Publish Forgejo Maven release workflow from main. The workflow maps the secret to FORGEJO_TOKEN for Gradle, uses JDK 21, runs the analysis and test tasks above, then publishes every Maven publication.

Forgejo package versions are immutable. Publish a new patch version instead of replacing an existing release.