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

Don't fail after first codestyle violation #287

Open
ubuntudroid opened this issue Aug 29, 2018 · 12 comments
Open

Don't fail after first codestyle violation #287

ubuntudroid opened this issue Aug 29, 2018 · 12 comments

Comments

@ubuntudroid
Copy link

ubuntudroid commented Aug 29, 2018

I tried out spotless today for the first time on one of our Kotlin projects and thus used the ktlint plugin.

It seems to work quite well so far, however spotlessCheck always fails right after finding the first codestyle violation while ktlint (when directly run on the codebase) lists all violations in a neat list.

I also tried to apply various different ktlint reporters (like the checkstyle one), but the result always seems to be the same - failure after the first violation (and in this case also no checkstyle XML is generated).

Long story short: can I force spotless (or the ktlint plugin, if that's the culprit) to continue its checks after it found a violation so that I can see all violations at a glance (and if possible without that stacktrace which causes the need for a lot of scrolling to find the error)?

Config looks like this:

spotless {
    kotlin {
        ktlint().userData(['android': 'true', 'color': 'true', 'reporter': 'checkstyle'])
        target '**/*.kt'
    }
}

Sample output:

./gradlew :app:spotlessCheck 

> Configure project :app 
registerResGeneratingTask is deprecated, use registerGeneratedResFolders(FileCollection)
registerResGeneratingTask is deprecated, use registerGeneratedResFolders(FileCollection)
registerResGeneratingTask is deprecated, use registerGeneratedResFolders(FileCollection)

> Task :app:spotlessKotlin FAILED
Step 'ktlint' found problem in 'app/src/main/java/my/app/package/repository/game/MatchItemMapper.kt':
Error on line: 27, column: 1
Exceeded max line length (100)
java.lang.AssertionError: Error on line: 27, column: 1
Exceeded max line length (100)
        at com.diffplug.spotless.kotlin.KtLintStep$State.lambda$createFormat$0(KtLintStep.java:114)
        at com.sun.proxy.$Proxy96.invoke(Unknown Source)
        at com.github.shyiko.ktlint.core.KtLint$format$2$1.invoke(KtLint.kt:354)
        at com.github.shyiko.ktlint.core.KtLint$format$2$1.invoke(KtLint.kt:36)
        at com.github.shyiko.ktlint.ruleset.standard.MaxLineLengthRule.visit(MaxLineLengthRule.kt:69)
        at com.github.shyiko.ktlint.core.KtLint$format$2.invoke(KtLint.kt:352)
        at com.github.shyiko.ktlint.core.KtLint$format$2.invoke(KtLint.kt:36)
        at com.github.shyiko.ktlint.core.KtLint$visitor$2$3.invoke(KtLint.kt:216)
        at com.github.shyiko.ktlint.core.KtLint$visitor$2$3.invoke(KtLint.kt:36)
        at com.github.shyiko.ktlint.core.KtLint.visit(KtLint.kt:489)
        at com.github.shyiko.ktlint.core.KtLint.visit(KtLint.kt:490)
        at com.github.shyiko.ktlint.core.KtLint.visit(KtLint.kt:490)
        at com.github.shyiko.ktlint.core.KtLint.visit(KtLint.kt:490)
        at com.github.shyiko.ktlint.core.KtLint.visit(KtLint.kt:490)
        at com.github.shyiko.ktlint.core.KtLint.access$visit(KtLint.kt:36)
        at com.github.shyiko.ktlint.core.KtLint$visitor$2.invoke(KtLint.kt:214)
        at com.github.shyiko.ktlint.core.KtLint$visitor$2.invoke(KtLint.kt:36)
        at com.github.shyiko.ktlint.core.KtLint.format(KtLint.kt:347)
        at com.github.shyiko.ktlint.core.KtLint.format(KtLint.kt:277)
        at com.diffplug.spotless.kotlin.KtLintStep$State.lambda$createFormat$1(KtLintStep.java:128)
        at com.diffplug.spotless.FormatterStepImpl$Standard.format(FormatterStepImpl.java:78)
        at com.diffplug.spotless.FormatterStep$Strict.format(FormatterStep.java:76)
        at com.diffplug.spotless.Formatter.compute(Formatter.java:230)
        at com.diffplug.spotless.Formatter.isClean(Formatter.java:167)
        at com.diffplug.gradle.spotless.SpotlessTask.check(SpotlessTask.java:263)
        at com.diffplug.gradle.spotless.SpotlessTask.performAction(SpotlessTask.java:205)
        at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
        at org.gradle.api.internal.project.taskfactory.IncrementalTaskAction.doExecute(IncrementalTaskAction.java:50)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:39)
        at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:26)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:124)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:113)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:95)
        at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:73)
        at org.gradle.api.internal.tasks.execution.OutputDirectoryCreatingTaskExecuter.execute(OutputDirectoryCreatingTaskExecuter.java:51)
        at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:101)
        at org.gradle.api.internal.tasks.execution.FinalizeInputFilePropertiesTaskExecuter.execute(FinalizeInputFilePropertiesTaskExecuter.java:44)
        at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:91)
        at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:62)
        at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:59)
        at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
        at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
        at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:256)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:199)
        at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:110)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:249)
        at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:238)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:123)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:79)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:104)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:98)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:663)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:597)
        at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:98)
        at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
        at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
        at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:spotlessKotlin'.
> Error on line: 27, column: 1
  Exceeded max line length (100)

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s
1 actionable task: 1 executed
@nedtwigg
Copy link
Member

This would probably be a helpful feature. Formatting failures can be split into two categories:

  • automatically fixable
  • user intervention required

Spotless focuses heavily on the first category, because they're "free" from a developer effort standpoint. Issues that can't be fixed automatically require some amount of developer attention, which may or may not be worth it to any given project. That said, there are a lot of formatters out there that require user intervention, and there are users who want that.

This is the code that applies the steps, where the error is thrown and passed to the format exception policy:

/**
* Returns the result of calling all of the FormatterSteps.
* The input must have unix line endings, and the output
* is guaranteed to also have unix line endings.
*/
public String compute(String unix, File file) {
Objects.requireNonNull(unix, "unix");
Objects.requireNonNull(file, "file");
for (FormatterStep step : steps) {
try {
String formatted = step.format(unix, file);
if (formatted == null) {
// This probably means it was a step that only checks
// for errors and doesn't actually have any fixes.
// No exception was thrown so we can just continue.
} else {
// Should already be unix-only, but some steps might misbehave.
unix = LineEnding.toUnix(formatted);
}
} catch (Throwable e) {
String relativePath = rootDir.relativize(file.toPath()).toString();
exceptionPolicy.handleError(e, step, relativePath);
}
}
return unix;
}

This is the formatter exception policy used by the gradle plugin, which rethrows the error as runtime exception and stops the formatting:

@Override
public void handleError(Throwable e, FormatterStep step, String relativePath) {
Objects.requireNonNull(e, "e");
Objects.requireNonNull(step, "step");
Objects.requireNonNull(relativePath, "relativePath");
if (excludeSteps.contains(step.getName())) {
FormatExceptionPolicyLegacy.warning(e, step, relativePath);
} else {
if (excludePaths.contains(relativePath)) {
FormatExceptionPolicyLegacy.warning(e, step, relativePath);
} else {
FormatExceptionPolicyLegacy.error(e, step, relativePath);
throw ThrowingEx.asRuntimeRethrowError(e);
}
}
}

To make the change you're suggesting, here is how I would go about it:

Btw, there is an open bug related to this at #194. It might be a good idea to start by understanding and fixing that bug.

@JLLeitschuh
Copy link
Member

Also, Gradle has the --continue flag.

@ubuntudroid
Copy link
Author

Sorry for the late response. Was busy on other projects.

@JLLeitschuh This is exactly what I needed and seems to be already the solution. I switched over to ktlint-gradle anyway as I mainly intended to use spotless for the ktlint checks and with ktlint-gradle your suggestion works perfectly fine.

I will try out this suggestion for spotless as well next week, just to ensure that we have a proper outcome of this issue.

@JLLeitschuh
Copy link
Member

This comment made me chuckle as I'm a maintainer on both projects.
If you don't mind me asking, were there any other reasons why you chose ktlint-gradle over spotless?

Also, if this issue is truly resolved for you, please close the issue when you've confirmed it's fixed.

@ubuntudroid
Copy link
Author

@JLLeitschuh Haha, yeah, I saw that. ;)

The main reason for switching to ktlint-gradle is that I like to keep the toolchain as lean as possible and we really only want the checks in ktlint for now. No other reasons at the moment. :)

I will make sure to close the issue after confirming that --continue works.

@ubuntudroid
Copy link
Author

So I check on a project with two different violations in two different files.
First violation was a missing space after if, the second one a too long line.

Unfortunately --continue didn't work. :( With and without the flag I just get the line length error and the task ends failing.

So I have to keep the issue open.

@MRezaNasirloo
Copy link

Hi,
Any solution or workaround for this?

@JLLeitschuh
Copy link
Member

The reason that --continue isn't working I presume is because Gradle is probably treating java.lang.AssertionError as a fatal error. I think the best thing to do is figure out where the AssertionError is being thrown (probably from inside the KTlint codebase) and correct it to be an exception, instead of an error.

That's just a guess though. The root cause may not be that.

@getachewreda
Copy link

Any found a workaround for this?

@tiagodocouto
Copy link

sorry for getting here one year later
did anyone get a solution for this?

@nedtwigg
Copy link
Member

nedtwigg commented Feb 3, 2023

It is blocked on #1097.

@WillHolbrook
Copy link

Just to add to this I run into a similar issue with only reporting the first style violation when using a multiple subproject build. When I run ./gradlew spotlessCheck I get

.
FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':sub-module-1:spotlessJavaCheck'.
.

Then I run ':sub-module-1:spotlessApply' and it fixes a load of files then when I run ./gradlew spotlessCheck a second time I get and the process continues until I have formatted all of my files

.
FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':sub-module-2:spotlessJavaCheck'.
.

I have the following in my root gradle

buildscript {
    .
    .
    .
    dependencies {
        .
        .
        .
        classpath "com.diffplug.spotless:spotless-plugin-gradle:6.25.0"
    }
}
subprojects {
    apply plugin: "java-library"
    apply plugin: "jacoco"
    apply plugin: "com.diffplug.spotless"

    spotless {
        java {
            removeUnusedImports()
            palantirJavaFormat()
        }
    }

For context I am trying to apply spotless to my entire codebase for the first time

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants