[2.x] fix: Remove projectDescriptors key that leaks Ivy types (#8894)

**Problem**
The projectDescriptors key in sbt.Keys leaked Ivy classes
(ModuleRevisionId, ModuleDescriptor) through its API surface.
No external plugins use this key.

**Solution**
Remove projectDescriptors entirely. IvyDependencyPlugin now uses
its internal depMap method directly for projectResolverTask and
coursierExtraProjectsTask, eliminating unnecessary Any casts.
The descriptors field is also removed from GlobalPluginData.

Fixes sbt#8865
This commit is contained in:
Dream 2026-03-11 14:53:39 -04:00 committed by GitHub
parent d6e6d28f87
commit b3d2733fb1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 18 additions and 23 deletions

View File

@ -745,6 +745,19 @@ lazy val mainProj = (project in file("main"))
exclude[DirectMissingMethodProblem]("sbt.Classpaths.mkIvyConfiguration"),
exclude[MissingClassProblem]("sbt.internal.librarymanagement.IvyXml"),
exclude[MissingClassProblem]("sbt.internal.librarymanagement.IvyXml$"),
// Removed projectDescriptors key (sbt#8865)
exclude[DirectMissingMethodProblem]("sbt.Keys.projectDescriptors"),
// Removed descriptors field from GlobalPluginData (sbt#8865)
exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData.apply"),
exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData.this"),
exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData.descriptors"),
exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData.copy"),
exclude[IncompatibleResultTypeProblem]("sbt.internal.GlobalPluginData.copy$default$3"),
exclude[IncompatibleResultTypeProblem]("sbt.internal.GlobalPluginData.copy$default$4"),
exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData.copy$default$6"),
exclude[IncompatibleResultTypeProblem]("sbt.internal.GlobalPluginData._3"),
exclude[IncompatibleResultTypeProblem]("sbt.internal.GlobalPluginData._4"),
exclude[DirectMissingMethodProblem]("sbt.internal.GlobalPluginData._6"),
),
)
.dependsOn(lmCore, lmCoursierShadedPublishing)

View File

@ -3134,7 +3134,6 @@ object Classpaths {
makePom / artifact := Artifact.pom(moduleName.value),
projectID := defaultProjectID.value,
projectID := pluginProjectID.value,
projectDescriptors := Def.uncached(Map.empty[Any, Any]),
updateConfiguration := {
// Tell the UpdateConfiguration which artifact types are special (for sources and javadocs)
val specialArtifactTypes = sourceArtifactTypes.value.toSet union docArtifactTypes.value.toSet

View File

@ -635,7 +635,6 @@ object Keys {
@transient
val publishTo = taskKey[Option[Resolver]]("The resolver to publish to.").withRank(ASetting)
val artifacts = settingKey[Seq[Artifact]]("The artifact definitions for the current module. Must be consistent with " + packagedArtifacts.key.label + ".").withRank(BSetting)
val projectDescriptors = taskKey[Map[Any, Any]]("Project dependency map for the inter-project resolver.").withRank(DTask)
val autoUpdate = settingKey[Boolean]("<unimplemented>").withRank(Invisible)
val retrieveManaged = settingKey[Boolean]("If true, enables retrieving dependencies to the current build. Otherwise, dependencies are used directly from the cache.").withRank(BSetting)
val retrieveManagedSync = settingKey[Boolean]("If true, enables synchronizing the dependencies retrieved to the current build by removed unneeded files.").withRank(BSetting)

View File

@ -103,7 +103,7 @@ object CoursierInputsTasks {
private[sbt] def coursierExtraProjectsTask: Def.Initialize[sbt.Task[Seq[CProject]]] = {
Def.task {
// Coursier handles inter-project dependencies natively via csrInterProjectDependencies.
// The Ivy-typed projectDescriptors are only populated when IvyDependencyPlugin is enabled.
// When IvyDependencyPlugin is enabled, it overrides csrExtraProjects with Ivy descriptors.
Seq.empty[CProject]
}
}

View File

@ -31,7 +31,6 @@ object GlobalPlugin {
// static here meaning that the relevant tasks for the global plugin have already been evaluated
def inject(gp: GlobalPluginData): Seq[Setting[?]] =
Seq[Setting[?]](
projectDescriptors ~= { _ ++ gp.descriptors },
projectDependencies ++= gp.projectID +: gp.dependencies,
resolvers := {
val rs = resolvers.value
@ -77,12 +76,10 @@ object GlobalPlugin {
val taskInit = Def.task {
val intcp = (Runtime / internalDependencyClasspath).value
val prods = (Runtime / exportedProducts).value
val depMap = projectDescriptors.value
GlobalPluginData(
projectID.value,
projectDependencies.value,
depMap,
resolvers.value.toVector,
(Runtime / fullClasspath).value,
(prods ++ intcp).distinct
@ -121,7 +118,6 @@ object GlobalPlugin {
final case class GlobalPluginData(
projectID: ModuleID,
dependencies: Seq[ModuleID],
descriptors: Map[Any, Any],
resolvers: Vector[Resolver],
fullClasspath: Classpath,
internalClasspath: Classpath

View File

@ -93,15 +93,6 @@ object IvyDependencyPlugin extends AutoPlugin:
)
.value
),
projectDescriptors := Def.uncached(
Def
.ifS(Def.task { useIvy.value })(
Def.task { depMap.value.asInstanceOf[Map[Any, Any]] }
)(
Def.task { Map.empty[Any, Any] }
)
.value
),
projectResolver := Def.uncached(
Def
.ifS(Def.task { useIvy.value })(
@ -166,9 +157,8 @@ object IvyDependencyPlugin extends AutoPlugin:
}
private def projectResolverTask: Initialize[Task[Resolver]] =
projectDescriptors.map { m =>
val typedMap = m.asInstanceOf[Map[ModuleRevisionId, ModuleDescriptor]]
val resolver = new ProjectResolver(ProjectResolver.InterProject, typedMap)
depMap.map { m =>
val resolver = new ProjectResolver(ProjectResolver.InterProject, m)
new RawRepository(resolver, resolver.getName)
}
@ -194,10 +184,8 @@ object IvyDependencyPlugin extends AutoPlugin:
Def.task {
val projects = csrInterProjectDependencies.value
val projectModules = projects.map(_.module).toSet
projectDescriptors.value
.map { (k, v) =>
val id = k.asInstanceOf[ModuleRevisionId]
val desc = v.asInstanceOf[ModuleDescriptor]
depMap.value
.map { (id, desc) =>
moduleFromIvy(id) -> desc
}
.filter { case (module, _) =>