[2.x] fix: Preserve user-specified scope axes in command instead of silently discarding them (#8916)

**Problem**
set every silently discards scope axes the user provides. Running:

set every Test / sources := Nil

empties sources in **all** scopes including Compile, not just Test. This happens because rescope in SettingCompletions.setAll strips the scope and forces Global.

**Solution**
Changed rescope to keep the user's config/task/extra axes and only wildcard the project axis. When no scope is given, behavior is unchanged - all axes match everything as before.
This commit is contained in:
BitToby 2026-03-18 06:53:54 +02:00 committed by GitHub
parent e6a2b536bf
commit 963acca8ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 48 additions and 2 deletions

View File

@ -55,12 +55,28 @@ private[sbt] object SettingCompletions {
def resolve(s: Setting[?]): Seq[Setting[?]] =
Load.transformSettings(projectScope, currentRef.build, rootProject, s :: Nil)
def axisMatches[T](user: ScopeAxis[T], existing: ScopeAxis[T]): Boolean =
user match
case Zero | This => true
case Select(_) => user == existing
def rescope[T](setting: Setting[T]): Seq[Setting[?]] = {
val akey = setting.key.key
val global = ScopedKey(Global, akey)
val userScope = setting.key.scope
val baseScope = Scope(
Zero,
Scope.subThis(Zero, userScope.config),
Scope.subThis(Zero, userScope.task),
Scope.subThis(Zero, userScope.extra),
)
val global = ScopedKey(baseScope, akey)
val globalSetting = resolve(Def.setting(global, setting.init, setting.pos))
globalSetting ++ allDefs.flatMap { d =>
if d.key == akey then Seq((d.scope / SettingKey(akey)) := global.value)
if d.key == akey
&& axisMatches(userScope.config, d.scope.config)
&& axisMatches(userScope.task, d.scope.task)
&& axisMatches(userScope.extra, d.scope.extra)
then Seq((d.scope / SettingKey(akey)) := global.value)
else Nil
}
}

View File

@ -0,0 +1 @@
object A

View File

@ -0,0 +1 @@
object B

View File

@ -0,0 +1,18 @@
val a = project
val b = project
val checkCompileSourcesNonEmpty = taskKey[Unit]("Verify Compile / sources is still non-empty")
checkCompileSourcesNonEmpty := {
val srcs = (Compile / sources).value
if (srcs.isEmpty)
sys.error("Compile / sources should not be empty, but it was.")
}
val checkTestSourcesEmpty = taskKey[Unit]("Verify Test / sources is empty")
checkTestSourcesEmpty := {
val srcs = (Test / sources).value
if (srcs.nonEmpty)
sys.error(s"Test / sources should be empty, but had: ${srcs}")
}

View File

@ -0,0 +1,10 @@
# set every with a scoped key should only affect that scope across all projects
> set every Test / sources := Nil
# Compile / sources should still be non-empty for all projects
> a/checkCompileSourcesNonEmpty
> b/checkCompileSourcesNonEmpty
# Test / sources should be empty for all projects
> a/checkTestSourcesEmpty
> b/checkTestSourcesEmpty