Skip to content

decouple sbt-scalafix from scalafix lifecycle #482

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

bjaglin
Copy link
Collaborator

@bjaglin bjaglin commented Apr 13, 2025

Fixes scalacenter/scalafix#1146

Levarages

Instead of supporting a scalafix version in .scalafix.conf (which was the option most discussed in the corresponding issue long time ago), this takes the simpler but effective approach to force the user to explicitly define a scalafix version (via scalafix-interfaces) alongside the sbt-scalafix version in the meta-build, letting sbt handle resolution, evictions and compatibility checks.

When no scalafix-interfaces is explicitly provided, scalafix invocations will fail unless scalafixAllowDynamicFallback := true. Steward and other "one-shot" users are not impacted by this breaking change though, as scalafixEnable sets scalafixAllowDynamicFallback := true, which results in loading the latest stable version of scalafix at invocation time. Since that scalafix version & the corresponding recommended scalameta version are not known by the meta-build, scalafixEnable downloads the latest semanticdb-scalac for Scala 2 projects.

Other build tools integrations should be able to replicate that approach. As for CLI usage (which does not use scalafix-interfaces), coursier makes things easy enough to run a specific cli version, so I am not sure it's worth investing time in supporting a scalafix version in .scalafix.conf just for that use-case.


TODO

  • smooth update
  • interfaces is early-semver, is that what we want? - add scripted to exercise that with old scalafix-interfaces versions?
  • update sbt-specific docs on scalafix repo, picking the sbt-scalafix version from the one injected in the build (updated via steward without delay)
    • installation
    • tutorial
    • local rules
  • update g8
  • update all example repos under scalacenter org

@bjaglin bjaglin changed the title decouple sbt-scalafix & scalafix versioning decouple sbt-scalafix & scalafix lifecycle Apr 13, 2025
@bjaglin bjaglin changed the title decouple sbt-scalafix & scalafix lifecycle decouple sbt-scalafix & scalafix lifecycles Apr 13, 2025
@bjaglin bjaglin force-pushed the decouple branch 6 times, most recently from 69294a5 to 4b53f80 Compare April 13, 2025 22:32

val all = List(
"ch.epfl.scala" % "scalafix-interfaces" % scalafixVersion
exclude ("ch.epfl.scala", "scalafix-properties"),
Copy link
Collaborator Author

@bjaglin bjaglin Apr 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the key change in this PR, leveraging scalacenter/scalafix#2226.

exclude is honored in the POM, and thus will allow a sbt-scalafix-only install to load ScalafixPlugin without linking errors (since classes from interfaces are present) but fail at invocation time (because properties file cannot be found).

@bjaglin bjaglin changed the title decouple sbt-scalafix & scalafix lifecycles decouple sbt-scalafix from scalafix lifecycle Apr 13, 2025
@bjaglin bjaglin requested a review from Copilot April 13, 2025 23:20
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 16 out of 35 changed files in this pull request and generated no comments.

Files not reviewed (19)
  • project/Dependencies.scala: Language not supported
  • project/plugins.sbt: Language not supported
  • src/main/scala/scalafix/internal/sbt/ScalafixInterface.scala: Language not supported
  • src/main/scala/scalafix/sbt/BuildInfo.scala: Language not supported
  • src/main/scala/scalafix/sbt/ScalafixPlugin.scala: Language not supported
  • src/sbt-test/sbt-scalafix/basic/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/build-lint/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/concurrency/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/cross-build/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/custom-config/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/custom-src-directory/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/dependency/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/explicit-files/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/inconfig/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/interfaces-missing/build.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/interfaces-missing/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/interfaces-missing/test: Language not supported
  • src/sbt-test/sbt-scalafix/resolvers/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/root-validation/project/plugins.sbt: Language not supported

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 20 out of 39 changed files in this pull request and generated no comments.

Files not reviewed (19)
  • project/Dependencies.scala: Language not supported
  • project/plugins.sbt: Language not supported
  • src/main/scala/scalafix/sbt/BuildInfo.scala: Language not supported
  • src/main/scala/scalafix/sbt/ScalafixEnable.scala: Language not supported
  • src/main/scala/scalafix/sbt/ScalafixPlugin.scala: Language not supported
  • src/sbt-test/sbt-scalafix/basic/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/build-lint/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/concurrency/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/cross-build/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/custom-config/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/custom-src-directory/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/dependency/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/explicit-files/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/inconfig/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/interfaces-missing/build.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/interfaces-missing/project/plugins.sbt: Language not supported
  • src/sbt-test/sbt-scalafix/interfaces-missing/src/main/scala/A.scala: Language not supported
  • src/sbt-test/sbt-scalafix/interfaces-missing/test: Language not supported
  • src/sbt-test/sbt-scalafix/resolvers/project/plugins.sbt: Language not supported
Comments suppressed due to low confidence (1)

readme.md:22

  • Consider adding an explanatory comment to clarify why the Sonatype snapshots resolver is required for these snapshot dependencies to aid user understanding.
resolvers += Resolver.sonatypeRepo("snapshots")

@bjaglin bjaglin force-pushed the decouple branch 2 times, most recently from eeccb11 to fe1b819 Compare April 16, 2025 08:15

// keep this as low as possible, to allow bumping sbt-scalafix without scalafix-interfaces
def scalafixInterfacesVersion: String =
"0.14.2+48-9b6e03ac-SNAPSHOT" // scala-steward:off
Copy link
Collaborator Author

@bjaglin bjaglin Apr 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛑 this must be updated to the next stable version before merging, or at least tagging a release

- scalafixEnable & scalafixAllowDynamicFallback allow to load the latest cli
  versions if no explicit scalafix-interfaces was provided
- scalafixEnable now simply targets the latest semanticdb-scalac version
- pin build-time interfaces version to decouple as much as possible, but make
  sure to dogfood the latest scalafix version for scalafix invocation time, to
  detect potential regressions
@bjaglin
Copy link
Collaborator Author

bjaglin commented Apr 16, 2025

@adpi2 @tgodzik @olafurpg can I get your opinion on this? I still have to write a salafix rule for a smooth steward migration in project/plugins.sbt and docs to update, but I am satisfied with the overall approach.

@bjaglin bjaglin requested review from tgodzik and adpi2 April 16, 2025 08:35
dependencyOverrides = [
{
dependency = { groupId = "ch.epfl.scala", artifactId = "scalafix-interfaces" },
pullRequests = { frequency = "@asap" },
Copy link
Collaborator Author

@bjaglin bjaglin Apr 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this makes sure the project/plugins.sbt version is up to date, for dogfooding

Comment on lines +92 to +96
val scalafixAllowDynamicFallback: SettingKey[Boolean] =
settingKey[Boolean](
"Control whether the latest version of scalafix may be downloaded instead of failing when " +
"scalafix-interfaces is not explicitly provided. Off by default because non-deterministic."
)
Copy link
Collaborator Author

@bjaglin bjaglin Apr 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was initially a private key used internally by scalafixEnable, but I thought we could make it public for power users, without necessarily advertizing it in the docs

@bjaglin bjaglin marked this pull request as ready for review April 16, 2025 08:43
Copy link

@tgodzik tgodzik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good from my side. For Metals and Scala CLI we already use interfaces, so we should not be impacted, right?

In scalafmt the approach was to include the version in config to make sure we always run the correct version irrelevant of the tool used. It might be less of an issue here, since the rules are much slower to evolve and also don't usually touch the whole code as scalafmt can.

What would be much more useful is having rule deps to be defined in in the config file as it's a bit of a hassle for the user to set up for metals, but we could potentially try exporting that information also

@@ -1,2 +1,4 @@
resolvers += Resolver.sonatypeRepo("public")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % sys.props("plugin.version"))
libraryDependencies += "ch.epfl.scala" % "scalafix-interfaces" %
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So users will have to add the dependency now unless the additional flag is added? Doesn't look like a lot more work, seems sensible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, and I am planning to make that one-shot addition automatic for scala-steward users via a scalafix rule

@bjaglin
Copy link
Collaborator Author

bjaglin commented Apr 17, 2025

For Metals and Scala CLI we already use interfaces, so we should not be impacted, right?

No impact indeed, scalafix-interfaces remains backward compatible for Metals, and there are no changes in scalafix-cli for scala-cli (we can't use scalafix-interfaces there because it wouldn't work on the native bundle).

include the version in config to make sure we always run the correct version irrelevant of the tool used

What would be much more useful is having rule deps to be defined in in the config file

We should have both at the same time indeed (the later is tracked under scalacenter/scalafix#1625 FWIW). And steward should know about these implicit and explicit maven coordinates in .scalafix.conf (just like it knows about the implicit one in .scalafmt.conf) to keep users up to date. Overall, it looked to me like a slightly bigger effort than keeping both as steward-friendly sbt dependencies like in this solution, but I agree this is the end goal which will benefit all scalafix-interfaces clients.

@bjaglin
Copy link
Collaborator Author

bjaglin commented Apr 20, 2025

Overall, it looked to me like a slightly bigger effort than keeping both as steward-friendly sbt dependencies like in this solution, but I agree this is the end goal which will benefit all scalafix-interfaces clients.

Actually, since I’ve got a bit of free time coming up, I’ll give this a shot. If it turns out to be doable with limited efforts, I might just skip the intermediate step this PR was meant to be.

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

Successfully merging this pull request may close these issues.

Separate versioning of sbt-scalafix and scalafix
2 participants