-
Notifications
You must be signed in to change notification settings - Fork 460
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
Command Line Application #527
Comments
Some responses to that comment:
Bazel piggybacking on CLI is exactly what I want, but I'm wary of piggybacking the CLI on top of Gradle. It feels like Bazel calling Gradle with extra steps. I'm not sure I can articulate why, but my intuition would prefer to have a "native" Spotless CLI. I could maybe be convinced, though.
If by package you mean java package, then If by package you mean directory, then I absolutely agree.
I'm pretty sure I understand what you're getting at here, but I'm not sure I understand why these are special concerns. For the first question, I was thinking the latter. I was expecting that the CLI should do everything that Spotless does with Gradle. One should be able to run the CLI with a configuration completely analogous to a Gradle configuration as though they were running a Gradle task and get the same result. For example, I was thinking you could have a configuration like the following (definitely open to your preferences on the format): java:
target:
- pattern: "**/*.java"
formatterSteps:
googleJavaFormat:
version: "1.2"
aosp: true
format:
name: "docs"
target:
- pattern: "docs/*.md"
formatterSteps:
trimTrailingWhitespace: True
endWithNewLine: True and this would be equivalent to the following Gradle configuration: spotless {
java {
target '**/*.java'
googleJavaFormat('1.2').aosp()
}
format('docs') {
target 'docs/*.md'
trimTrailingWhitespace()
endWithNewLine()
}
} I know that this would limit one's ability to use arbitrary logic in defining the targets, but isn't that preferred in some ways? One of the most common complaints I hear about Gradle is that it lets you do whatever you want and things get unnecessarily complicated as a result. Plus, isn't Spotless designed to allow only intended configuration options? As you've seen, I've tried to do weird stuff with Spotless in Gradle and it (seemingly by design) hasn't worked out great :P. Doing things with this configuration would just inhibit its users trying to break it. For the other question, I guess what I had in mind was to build the Spotless CLI with Bazel or Gradle, and all of the dependencies would be specified that way. I guess it seems suboptimal to have everything packaged in the binary even if you're not going to use it, but I wanted all the dependencies to be part of the Spotless CLI build, rather than the Spotless run. It doesn't have that many optional dependencies, right? As a side note about that question, Bazel handles dependencies through what it calls repositories. In a file called maven_install(
name = "maven",
artifacts = [
"junit:junit:4.12",
"com.google.guava:guava:27.0-android",
],
repositories = [
"https://maven.google.com",
"https://repo1.maven.org/maven2",
],
) For maven projects which do not have Bazel build configurations, Bazel will generate them so you can reference their components as Bazel targets from the
This I don't understand. We would certainly use a build system to build the CLI binary (we could use any of them; Bazel, Gradle, Maven), but I don't see why it would be especially difficult to get it to run on its own without one of them. Can you elaborate? |
Dependencies
Definitely hundreds, and possibly thousands :D Spotless supports many versions of many formatters. For example, when you say spotless/lib/src/main/java/com/diffplug/spotless/Provisioner.java Lines 49 to 54 in c007dba
This is all rigorously cached, so it is fast, but you have to provide some implementation of this interface (bullet 3). It is not practicable to shadow every single dependency of every single JDT, and even if you did you'd need to invent some kind of packaging / unpackaging system. This interface is very easy to implement within the context of a Gradle plugin or a Maven plugin, which is a benefit of piggybacking on "bazel calls cli that calls gradle". Jbang is an example of using gradle as a dynamic-dependency-grabber backend. One bazel-native solution could be descriptive error messages. For example, "Spotless tried to resolve XXX, but it is not present. Add it to the maven_install(artifacts = " section. That is painful, but workable. Files
Resolving this wildcard is expensive. You can build a caching system which can resolve this wildcard much more quickly, and even build it so that only the changed files are passed along. Building this system from scratch within Minimal implementation (Bazel vs CLI)Spotless' pitch is that it is a "switchboard" - it is glue code and infrastructure, and the two systems above are the missing pieces that it gets from the host. I think a naked CLI would implement them very differently than a bazel-native tool, which is why I think that either:
The advantage of embedding Gradle is that now you have a truly naked CLI, which can be used anywhere. Going the non-naked route where you're still using the host to implement Implement Provisioner with no-op.If you look at the above and say "wow, that's a lot of complexity", you're right! With formatters, fixing a bug means reformatting code. It's a useful feature of Spotless that you can get bugfixes and features in the infrastructure, without also being forced to adopt the newest version of whatever formatters you use. That "switchboard" functionality does come at a high cost, but it's the minimum cost required to achieve that goal. Another alternative is to consume Spotless as a library, declare the formatter and version you care about as a static dependencies, and implement Provisioner as "search the classpath for 'google-java-format-1.7.jar'" and return that. Such an implementation is not "a Spotless", it's just "using Spotless", but that's fine! We've always maintained semver on My instinct is that stealing JBang's dep resolution will be the easiest path forward, and that once you've gotten that far it might turnout to be pretty easy to reuse the Gradle DSL. My other instinct is that building a CLI absent a specific host-build-system usecase is likely to result in a lot of unused code. I would focus on doing what Bazel needs, and then generalize - rather than build a CLI and then figure out how to use it with Bazel. |
Relevant discussion in #529 (comment) Hmmm...I see your point. I will definitely take these things into consideration. I feel really weird using a Gradle build script as a configuration for another build tool, but I guess my concerns are not worth all of the other problems not using Gradle would come with. I also see your point about a CLI not really being necessary. It seems like Spotless is fundamentally intended to aid in aggregating and integrating a variety of formatting functionality into build systems that the linters in question do not support natively or do not support well. The issue is that, based on my investigation so far, it seems like Bazel's simplistic approach makes it prefer to simply provide inputs to an executable that produces outputs, declaring a clear relationship between the two. It doesn't have much support for using a library to perform build steps, no matter the language. Note, however, that Bazel itself is written in Java and runs on the JVM. Admittedly, most of my knowledge here comes from personal experience and internet searches. It seems worthwhile to contact the Bazel community directly, as they are very knowledgeable and usually quick to respond. I have leaned on them in several cases, and they have been able to help me resolve problems that had me pulling my hair out. I will reach out to them before proceeding. |
|
I've since learned from a conversation over at openrewrite that jbang can be used as a middleman to download things from Maven Central, and in turn build a CLI app around, as per this example. But re-reading this thread, I see that jbang has been mentioned a few times, so maybe this idea for making a Spotless CLI app was already mentioned and I've just missed it? The downside seems to be that jbang itself has to be installed? I've never used it before, so I'm not entirely sure. I've also just remembered about a Scala library/tool called Coursier for downloading things from Maven Central. Perhaps Coursier could be used instead? |
fyi - coursier needs more things downloaded than jbang. If you amde a diffplug/jbang-catalog repo and had a spotless cli jar you would be able to do:
that curl can be replaced with whatever package manager you like (see https://jbang.dev/download) but curl works on all major platforms, even windows bash based shells. users can add extra deps if that makes sense by |
incorrect. jbang uses shrinkwrap resolver which in turn uses aether (which gradle itself uses too afaik). |
Not sure if this is any use, but a while ago i worked out the minimal code needed to drive Maven's dependency resolution machinery using its libraries - see maven-resolver-demo. I think it would be possible to write a provisioner using code like this. A command-line Spotless tool using this would have some Maven libraries as dependencies (and Guice, and SLF4J, and a few other things), but it would not need Maven installed locally as a tool. |
@tomwhoiscontrary fyi thats what jbang is :) we've now since this thread update moved All we need is a spotless jar with a main method and we are golden. |
As discussed in #76, building a binary wrapper for Spotless seems to be a (probably?) necessary step to building a cleanly written Bazel extension. Since much of the work needs to be done anyway, why not make that executable accessible to Spotless users as a command line application?
@nedtwigg has kindly already provided some input on this in this comment
The text was updated successfully, but these errors were encountered: