scalacenter / scalafix

Refactoring and linting tool for Scala
https://scalacenter.github.io/scalafix/
BSD 3-Clause "New" or "Revised" License
830 stars 186 forks source link

Add `scalafixProject` helper to sbt-scalafix? #1619

Open armanbilge opened 2 years ago

armanbilge commented 2 years ago

In https://github.com/typelevel/typelevel-scalafix/pull/21 we added a helper method to setup scalafix projects. The actual plugin source can be found in: https://github.com/typelevel/typelevel-scalafix/blob/676237e55e3cf5f8c52743929137dd119685a08b/project/ScalafixProjectPlugin.scala

The idea is conceptually similar to the crossProject helper from sbt-crossproject: essentially, to setup a composite project that helps configure the related sub-projects (i.e., rules, input, output, and test).

Here's an example of how it can be used:

lazy val cats = scalafixProject("cats")
  .inputSettings(
    libraryDependencies ++= Seq(
      "org.typelevel" %% "cats-core" % CatsVersion
    )
  )

This sets up:

  1. a cats-rules project, with a dependency to scalafix-core.
  2. a cats-input project, set to no-publish, and with a dependency to cats-core.
  3. a cats-output project, set to no-publish.
  4. a cats-tests project:
    • depends on the cats-rules sub-project
    • enables ScalafixTestkitPlugin
    • automatically configures scalafixTestkitOutputSourceDirectories, scalafixTestkitInputSourceDirectories, scalafixTestkitInputClasspath, scalafixTestkitInputScalacOptions, scalafixTestkitInputScalaVersion
    • set to no-publish

Is this something sbt-scalafix would be interested in? I'm happy to make a PR. Thanks!

bjaglin commented 2 years ago

I am really happy to get some feedback about sbt-scalafix which could get some UX love. Thanks for the initiative and for opening up a discussion about upstream it!

I agree that there is currently a lot of boilerplate to set up a sbt project defining scalafix rules - https://github.com/scalacenter/scalafix.g8 was built to try to mitigate that.

At this point, scalafix is not published for Scala 3 (main blocker being that scalameta isn't either), but does support Scala 3 sources through scalafix-*_2.x (except for advanced semantic rules relying on the presentation compiler). That's why sbt-projectmatrix was introduced in the g8 template. It's definitely not pretty, but it's the most standard solution we found (superior to any hack tried on top of crossScalaVersions). I assume we could backport that "test Scala 3 targets with scalafix-*_2.x" behind your DSL, but that would be even more magic. Note that there is a GSoC starting up that might result in scalafix being published for Scala 3, which would remove this extra constraint. So I would recommend waiting a few weeks to see where we land on that.

On the proposal overall, I have mixed feelings about abstracting the testkit infrastructure behind that DSL, as it adds more magic on top of vanilla sbt. I have the feeling that verbose/explicit syntax might be more accessible for newcomers. We'd have to ask the community if "less is more" applies in that context I guess.

About the naming, I feel like scalafixProject() might be ambiguous as rule consumers might think it's for them - what about scalafixRulesProject(), or even scalafixTestkitProject?

armanbilge commented 2 years ago

Thanks for the response, and your interest! :)


Note that there is a GSoC starting up that might result in scalafix being published for Scala 3, which would remove this extra constraint.

Ah yes, good point about Scala 3, completely agree about that. If/when that comes through I agree this will work out much nicer, fingers crossed for GSoC 🤞


On the proposal overall, I have mixed feelings about abstracting the testkit infrastructure behind that DSL, as it adds more magic on top of vanilla sbt.

Absolutely, it's a trade-off. From my work on sbt-typelevel, my default stance is any boilerplate configuration that is repeated in 90-something % of builds should be upstreamed and baked into some "magic" command. Another advantage of this is it's easier to roll out improvements. When you update a template, it doesn't help the all the projects that used an old version of the template.

I do think there's an interesting comparison here with crossProject from sbt-crossproject for cross-building JVM/JS/Native. It is also "magical" and this is occasionally confusing for users, but in my experience it ultimately means more projects cross-publish because it makes it easy.

So my argument for it would be the same here: if setting up a publishable scalafix project becomes one line of "magic" sbt, then hopefully more people will do it, and that's the most important thing of all :) IMHO.

Also note that having this helper method doesn't preclude users from doing it the manual way, and for the docs/templates to demonstrate that as well.


About the naming, I feel like scalafixProject() might be ambiguous as rule consumers might think it's for them - what about scalafixRulesProject(), or even scalafixTestkitProject?

For sure, I didn't think too hard about the name at this stage :) scalafixRulesProject sounds good to me 👍 and I'm sure some other bits can be bike-shed as well.