diffplug / spotless

Keep your code spotless
Apache License 2.0
4.5k stars 455 forks source link

Avoid check of the ratchetFrom branch when Spotless not invoked #1902

Open helloncode opened 10 months ago

helloncode commented 10 months ago

Currently, Spotless (Gradle Plugin) performs a check for the presence of the ratchetFrom branch on every command execution. This includes instances where Spotless-specific commands aren't called, which can lead to unnecessary checks, and adds the workaround (e.g. git fetch depth:0) for all cases.

Could we refine the ratchetFrom functionality so that it checks for branch presence only when a Spotless command is actually invoked?

I believe this adjustment could be beneficial for users who integrate Spotless into their development workflows, ensuring that the tool remains as efficient and unobtrusive as possible.

popematt commented 6 months ago

It would also be nice if the ratchetFrom would accept a lazy string as a () -> String, Supplier<String>, or similar and not get the value of the lazy string until running a Spotless task.

In one of my build scripts, I've written some logic that shells out to git to find the name of the remote for the source repository. If the rachetFrom branch name was lazily evaluated only when a Spotless task is actually running, then I could perform the lookup as a Gradle task and cache the result as a task output.

popematt commented 5 months ago

I managed to come up with a workaround using a Git tag.

Using the Kotlin Gradle DSL, and assuming there is a runCommand() extension function that does exactly what its name implies...

val SPOTLESS_TAG = "spotless-rachet-from-this-commit"
val rachetFromCommitIsh by lazy { TODO("Your logic here") } 

spotless {
    "git tag -f $SPOTLESS_TAG".runCommand()
    rachetFrom(SPOTLESS_TAG)
    // If we delete it now, it's too soon, but we can delete it once the task graph is complete 
    // so that it's cleaned up if no `spotless` tasks are going to be run.
    gradle.taskGraph.addTaskExecutionGraphListener { 
        "git tag -d $SPOTLESS_CHECKPOINT_TAG".runCommand()
    }
}

tasks.withType<SpotlessTask> {
    doFirst { "git tag -f $SPOTLESS_TAG $rachetFromCommitIsh".runCommand() }
    doLast { "git tag -d $SPOTLESS_TAG".runCommand() }
}

The downside is that this creates a new tag in (the local copy of) your Git repository. I can definitely live with that in a CI's local copy of the repo, and I think I can live with that on my local copy too.