diff --git a/.claude/commands/fix-github-issue.md b/.claude/commands/fix-github-issue.md new file mode 100644 index 00000000..2fe1d47c --- /dev/null +++ b/.claude/commands/fix-github-issue.md @@ -0,0 +1,13 @@ +Please analyze and fix the GitHub issue: $ARGUMENTS. + +Follow these steps: + +1. Use `gh issue view` to get the issue details +2. Understand the problem described in the issue +3. Search the codebase for relevant files +4. Implement the necessary changes to fix the issue +5. Write and run tests to verify the fix +6. Ensure code passes linting and type checking +7. Create a descriptive commit message + +Remember to use the GitHub CLI (`gh`) for all GitHub-related tasks. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..1dbe70dc --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,28 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Build and Test Commands + +- Build project: `./gradlew build` +- Run all tests: `./gradlew test` +- Run a single test: `./gradlew test --tests "org.jmailen.gradle.kotlinter.functional.KotlinProjectTest.testName"` +- Run integration tests projects (from test-project* directories): `../gradlew lintKotlin formatKotlin` +- Lint Kotlin code: `./gradlew lintKotlin` +- Format Kotlin code: `./gradlew formatKotlin` +- Lint specific source set: `./gradlew lintKotlinMain` +- Format specific source set: `./gradlew formatKotlinMain` + +## Code Style Guidelines + +- Kotlin code follows ktlint rules with editorconfig customizations +- Max line length: 140 characters +- Indentation: 4 spaces +- New line at EOF required +- Trailing commas allowed in declarations and function calls +- Imports follow ktlint standard ordering +- Use kotlinter extension for configuration in Gradle projects +- Tests use JUnit 5 (Jupiter) assertions +- Functional tests extend `WithGradleTest` for Gradle TestKit integration +- Error handling uses custom `KotlinterError` and `LintFailure` classes +- When adding features, ensure backward compatibility as this is a Gradle plugin \ No newline at end of file diff --git a/README.md b/README.md index 89fee406..b196a8d4 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Available on the Gradle Plugins Portal: https://plugins.gradle.org/plugin/org.jm ```kotlin plugins { - id("org.jmailen.kotlinter") version "5.0.1" + id("org.jmailen.kotlinter") version "5.0.2" } ``` @@ -36,7 +36,7 @@ plugins { ```groovy plugins { - id "org.jmailen.kotlinter" version "5.0.1" + id "org.jmailen.kotlinter" version "5.0.2" } ``` @@ -50,7 +50,7 @@ Root `build.gradle.kts` ```kotlin plugins { - id("org.jmailen.kotlinter") version "5.0.1" apply false + id("org.jmailen.kotlinter") version "5.0.2" apply false } ``` @@ -70,7 +70,7 @@ Root `build.gradle` ```groovy plugins { - id 'org.jmailen.kotlinter' version "5.0.1" apply false + id 'org.jmailen.kotlinter' version "5.0.2" apply false } ``` diff --git a/build.gradle.kts b/build.gradle.kts index fc7fb0f7..de2e5afc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,8 +9,7 @@ plugins { idea alias(libs.plugins.kotlin.jvm) alias(libs.plugins.gradle.publish) - // Remove temporarily until 2.1.0 compatibility is released - // alias(libs.plugins.kotlinter) + alias(libs.plugins.kotlinter) } repositories { @@ -23,7 +22,7 @@ val githubUrl = "https://github.com/jeremymailen/kotlinter-gradle" val webUrl = "https://github.com/jeremymailen/kotlinter-gradle" val projectDescription = "Lint and formatting for Kotlin using ktlint with configuration-free setup on JVM and Android projects" -version = "5.0.1" +version = "5.0.2" group = "org.jmailen.gradle" description = projectDescription @@ -77,7 +76,7 @@ tasks { """ version = $projectVersion ktlintVersion = ${libs.versions.ktlint.get()} - """.trimIndent() + """.trimIndent(), ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 00e26565..4cc92ab2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,12 +1,12 @@ [versions] -kotlin = "2.1.0" +kotlin = "2.1.20" ktlint = "1.5.0" android-tools = "7.3.1" -junit = "5.11.3" +junit = "5.11.4" mockito = "4.1.0" -commons-io = "2.18.0" -gradle-publish = "1.3.0" -kotlinter-check = "4.5.0" +commons-io = "2.19.0" +gradle-publish = "1.3.1" +kotlinter-check = "5.0.1" [libraries] ktlint-rule-engine = { group = "com.pinterest.ktlint", name = "ktlint-rule-engine", version.ref = "ktlint" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 017c9582..12c27d6c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version "0.9.0" + id("org.gradle.toolchains.foojay-resolver-convention") version "0.10.0" } rootProject.name = "kotlinter-gradle" diff --git a/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt b/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt index 24f3936b..45861179 100644 --- a/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt +++ b/src/main/kotlin/org/jmailen/gradle/kotlinter/KotlinterPlugin.kt @@ -35,6 +35,12 @@ class KotlinterPlugin : Plugin { registerPrePushHookTask() } + // Configure all tasks including custom user tasks regardless of which plugins are applied + // This ensures that custom tasks work even without a Kotlin plugin + tasks.withType(ConfigurableKtLintTask::class.java).configureEach { task -> + task.ktlintClasspath.from(ktlintConfiguration) + } + // for known kotlin plugins, register tasks by convention. extendablePlugins.forEach { (pluginId, sourceResolver) -> pluginManager.withPlugin(pluginId) { @@ -42,11 +48,6 @@ class KotlinterPlugin : Plugin { val formatKotlin = registerParentFormatTask() registerSourceSetTasks(kotlinterExtension, sourceResolver, lintKotlin, formatKotlin) - - // Configure all tasks including custom user tasks - tasks.withType(ConfigurableKtLintTask::class.java).configureEach { task -> - task.ktlintClasspath.from(ktlintConfiguration) - } } } } @@ -69,12 +70,21 @@ class KotlinterPlugin : Plugin { isCanBeConsumed = false isVisible = false - val dependencyProvider = provider { - // Even though we don't use CLI, it bundles all the runtime dependencies we need. - val ktlintVersion = kotlinterExtension.ktlintVersion - this@createKtLintConfiguration.dependencies.create("com.pinterest.ktlint:ktlint-cli:$ktlintVersion") + // Use individual ktlint dependencies rather than the CLI to avoid variant selection issues + val ktlintVersion = kotlinterExtension.ktlintVersion + val deps = listOf( + "com.pinterest.ktlint:ktlint-rule-engine:$ktlintVersion", + "com.pinterest.ktlint:ktlint-ruleset-standard:$ktlintVersion", + "com.pinterest.ktlint:ktlint-cli-reporter-core:$ktlintVersion", + "com.pinterest.ktlint:ktlint-cli-reporter-plain:$ktlintVersion", + "com.pinterest.ktlint:ktlint-cli-reporter-html:$ktlintVersion", + "com.pinterest.ktlint:ktlint-cli-reporter-checkstyle:$ktlintVersion", + "com.pinterest.ktlint:ktlint-cli-reporter-json:$ktlintVersion", + "com.pinterest.ktlint:ktlint-cli-reporter-sarif:$ktlintVersion", + ) + deps.forEach { dep -> + dependencies.add(project.dependencies.create(dep)) } - dependencies.addLater(dependencyProvider) } return configuration } diff --git a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/CustomTaskTest.kt b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/CustomTaskTest.kt index ae046a14..54744b2a 100644 --- a/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/CustomTaskTest.kt +++ b/src/test/kotlin/org/jmailen/gradle/kotlinter/functional/CustomTaskTest.kt @@ -1,5 +1,6 @@ package org.jmailen.gradle.kotlinter.functional +import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome import org.jmailen.gradle.kotlinter.functional.utils.editorConfig import org.jmailen.gradle.kotlinter.functional.utils.kotlinClass @@ -256,4 +257,49 @@ class CustomTaskTest : WithGradleTest.Kotlin() { assertTrue(projectRoot.resolve("build/lint-report.txt").exists()) } } + + @Test + fun `ktLint custom task works without kotlin plugin applied`() { + // Create a new project directory without the kotlin plugin + val noKotlinProjectDir = File(testProjectDir.parentFile, "no-kotlin-project").apply { + mkdirs() + } + + noKotlinProjectDir.resolve("settings.gradle") { + writeText(settingsFile) + } + + noKotlinProjectDir.resolve("build.gradle") { + // language=groovy + val buildScript = + """ + plugins { + // No kotlin plugin applied + id 'org.jmailen.kotlinter' + } + $repositories + + import org.jmailen.gradle.kotlinter.tasks.FormatTask + + task customFormatTask(type: FormatTask) { + source files('src') + } + """ + writeText(buildScript) + } + + noKotlinProjectDir.resolve("src/main/kotlin/Simple.kt") { + writeText(kotlinClass("Simple")) + } + + // Create a custom Gradle runner for this specific test + val runner = GradleRunner.create() + .withProjectDir(noKotlinProjectDir) + .withArguments("customFormatTask", "--stacktrace") + .withPluginClasspath() + .forwardOutput() + + val result = runner.build() + assertEquals(TaskOutcome.SUCCESS, result.task(":customFormatTask")?.outcome) + } }