In sbt 2.x, running batch commands through the thin client (sbtn) suppresses stack traces for all tasks because the server's shell command unconditionally sets state.interactive = true. This causes LogManager.defaultTraceLevel to return -1 (suppressed) even when the client explicitly signals non-interactive (batch) mode via Attach(interactive=false).
This fixes the shell command to check the originating NetworkChannel's interactive flag before setting state.interactive, so thin client batch commands correctly get Int.MaxValue trace level and display full stack traces.
**Problem**
PomGenerator (introduced in #8873) was missing <description> and <url>
elements that the old Ivy-based MakePom included. Setting `description`
and `homepage` in build.sbt had no effect on the generated pom.xml.
**Solution**
Add makeDescription and makeHomePage helpers to PomGenerator, matching
the behavior of MakePom. Add test assertions for both fields.
Fixes#9054
**Problem**
Like File, Path normally captures the absolute path,
so likely not a good candidate for caching.
**Solution**
Ban it.
---------
Co-authored-by: Anatolii Kmetiuk <anatoliikmt@proton.me>
**Problem**
When usePipelining := true in a multi-project build, sbt appends
-Ypickle-java and -Ypickle-write <path>/early.jar to scalacOptions
for fast parallel compilation. These flags were being leaked into the Scala
REPL, causing failure on console
**Solution**
Strip pipelining flags before forwarding to the REPL in Deafults.scala.
**Problem**
There seems to be multiple problems around metabuild reloading (reload plugins).
1. Originally reported issue 9006 states that the build can't come back
from reload plugins.
2. During the course of fixing, I discovered that when reload plugins is used
the project name is set to "project" instead of foo-build etc.
3. Also there was a bug in the rootPaths when reload plugins is used,
which results in class not defined error.
**Solution**
1. Fix the plugin context so reload plugin still behaves like a metabuild.
2. Fix the rootPaths.
**Problem**
When build.sbt contains no explicit project definition (e.g. just bare settings with no lazy val root = project.in(file("."))), sbt creates a synthetic root project. In Load.loadTransitive, this synthetic root was processed with expand = false, which prevented AutoPlugin.derivedProjects from being called.
Any plugin relying on derivedProjects to inject projects would fail with "Reference to undefined setting" errors.
The workaround was to add an explicit root project in build.sbt (val root = project.in(file("."))), which caused the Some(root) branch to execute with expand = true.
**Solution**
Removed the expand variable from loadTransitive and pass true directly to processProject. Previously, the Some(root) branch set expand = true while the None branch set expand = false. Since derivedProjects should always be invoked for the root project regardless of whether it was explicitly defined or auto-generated, both branches should behave the same way. Eliminating the variable makes this intent clear.
* [2.x] feat: Add cacheVersion setting for global cache invalidation
**Problem**
There was no escape hatch to invalidate all task caches when needed.
**Solution**
Add `Global / cacheVersion` setting that incorporates into the cache key
hash. Changing it invalidates all caches. Defaults to reading system
property `sbt.cacheversion`, or else 0L. When 0L, the hash is identical
to the previous behavior (backward compatible).
Fixes#8992
* [2.x] refactor: Simplify BuildWideCacheConfiguration and add cacheVersion test
- Replace auxiliary constructors with default parameter values
- Add unit test verifying cacheVersion invalidates the cache
* [2.x] fix: Restore auxiliary constructors for binary compatibility
* [2.x] test: Improve cacheVersion scripted test and add release note
- Scripted test now verifies cache invalidation via a counter
that increments only when the task body actually executes
- Add release note documenting the cacheVersion setting
publishM2 never wrote maven-metadata-local.xml, which Maven uses to
distinguish local installs from remote artifacts. Without it, Maven
re-downloads remote SNAPSHOTs even when a local copy exists, making
publishM2 effectively broken for SNAPSHOT workflows.
Fixes#2053
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
**Problem**
When dependencyMode := Direct is set, the filtering was applied at
the managedClasspath level, which removed transitive dependencies
from all downstream classpaths including Test / dependencyClasspath.
This caused runtime test failures because transitive deps like
hamcrest-core (pulled in by junit) were missing.
**Solution**
Move the dependencyMode filtering from managedClasspath to a new
filteredDependencyClasspath task, and wire dependencyPicklePath
(the classpath used by the compiler) to use it. Runtime classpaths
like dependencyClasspath and fullClasspath remain unfiltered,
preserving all transitive dependencies for test execution.
Fixes#8989
**Problem**
ProjectMatrix.baseSettings computes sourceDirectory and unmanagedBase using base.getAbsoluteFile, which resolves relative paths against the JVM's working directory. This works fine within a single build, but breaks for source dependencies - when an external build loaded via ProjectRef(file("ext/lib"), "lib") uses projectMatrix.in(file(".")), the file(".") resolves to the root project's directory instead of ext/lib/.
As a result, the matrix project picks up wrong sources and compilation fails.
**Solution**
Replace base.getAbsoluteFile with IO.resolve((ThisBuild / baseDirectory).value, base). Since ThisBuild / baseDirectory is set per build unit during loading, this correctly resolves against each build's own root directory.
sourceDirectory and unmanagedBase now derive from the resolved projectMatrixBaseDirectory setting.
PomGenerator never emitted <type> for dependencies with explicit
artifacts, so a WAR dependency would appear in the POM without
<type>war</type>. Maven then treats it as a JAR dependency, resolving
the wrong artifact.
Uses the primary (non-classifier) artifact to determine the type,
so .withSources()/.withJavadoc() classifier artifacts don't produce
spurious <type>doc</type> or <type>src</type> elements.
Fixes#1979
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
**Problem**
sbt always includes all transitive dependencies on the classpath.
This makes it easy to accidentally depend on transitive dependencies
without declaring them, leading to fragile builds that break when
a library changes its own dependencies.
**Solution**
Add a `dependencyMode` setting with three modes:
- DependencyMode.Transitive (default) — current behavior, all
transitive dependencies on the classpath
- DependencyMode.Direct — only declared dependencies plus
scala-library on the classpath
- DependencyMode.PlusOne — declared dependencies plus their
immediate transitive dependencies plus scala-library
Fixessbt/sbt#8942
**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.
**Problem**
When scalaVersion is Scala 3.x and a dependency brings a newer
scala3-library_3, sbt did not report an eviction error or warning.
The compiler could be older than the standard library on the
classpath, breaking compile-time alignment (e.g. scala/scala3#25406).
**Solution**
- In Compiler.scala, add an else if ScalaArtifacts.isScala3(sv) branch
in scalaInstanceConfigFromUpdate that finds scala3-library_* on the
Compile report and, if scalaVersion < that revision, fails (or warns
when allowUnsafeScalaLibUpgrade := true) with the same pattern as
the existing Scala 2.13 check (PR 7480). Message uses compile-time
alignment wording and "See evicted to know why ... was upgraded from".
- Set allowUnsafeScalaLibUpgrade := true on b3 in stdlib-unfreeze so
existing b3/run and b3/checkScala still pass.
- Add scripted tests: stdlib-unfreeze-scala3-eviction (expect compile
to fail) and stdlib-unfreeze-scala3-warn (expect success with warning).
Closes#6694
**Problem**
When scalaVersion is Scala 3.x and a dependency brings a newer
scala3-library_3, sbt did not report an eviction error or warning.
The compiler could be older than the standard library on the
classpath, breaking compile-time alignment (e.g. scala/scala3#25406).
**Solution**
- In Compiler.scala, add an else if ScalaArtifacts.isScala3(sv) branch
in scalaInstanceConfigFromUpdate that finds scala3-library_* on the
Compile report and, if scalaVersion < that revision, fails (or warns
when allowUnsafeScalaLibUpgrade := true) with the same pattern as
the existing Scala 2.13 check (PR 7480). Message uses compile-time
alignment wording and "See evicted to know why ... was upgraded from".
- Set allowUnsafeScalaLibUpgrade := true on b3 in stdlib-unfreeze so
existing b3/run and b3/checkScala still pass.
- Add scripted tests: stdlib-unfreeze-scala3-eviction (expect compile
to fail) and stdlib-unfreeze-scala3-warn (expect success with warning).
Closes#6694
**Problem**
The tests/nested-testquick scripted test only verified test and testQuick but did not exercise testOnly against individual test
classes, nor did it verify that a failing nested test class is detected.
**Solution**
- Added GoodCalcTest.java (with a Nested inner class) as a passing test.
- Added changed/BadCalcTest.java (with a Nested`inner class) as a deliberately failing test.
Running sbt new scala/toolkit.local with the template slug on the command line throws:
The same templates work when chosen from the interactive menu (sbt new with no args). The code path for “arguments provided” only consulted external template resolvers (e.g. Giter8), which do not handle these built-in local templates.
Step 5 of #7640 — removes the compile-time dependency on lm-ivy from main/ by creating a standalone sbt-ivy plugin module.
- Create new sbt-ivy/ subproject with IvyDependencyPlugin AutoPlugin that provides all Ivy-specific functionality (ivySbt, ivyModule, ivyConfiguration, publisher, projectDescriptors, deliver/makeIvyXml)
- Move IvyXml.scala from main/ to sbt-ivy/
**Summary**
This adds an --append flag to the dependencyTree input task when used with --out. It allows running dependencyTree in multiple subprojects and appending their outputs to the same target file instead of overwriting.
**Solution**
- Add Arg.Append in the CLI parsing
- When --append is set, write using IO.write(..., append = true)
**Problem**
When a dependency is declared with a classifier (e.g., classifier "linux-x86_64"),
the UpdateReport > ModuleReport.module.explicitArtifacts is empty. The classifier
data is available as Publication objects during Coursier resolution but is lost
when SbtUpdateReport reconstructs the ModuleID.
**Solution**
In SbtUpdateReport.moduleReport, extract explicit artifacts from Publications
with non-empty classifiers and apply them to the ModuleID used by ModuleReport.
This is done on a per-report copy to avoid mutating the shared moduleId cache.
Fixes#5491
- Replace `ivyModule` with Coursier's `dependencyResolution.moduleDescriptor` in the `evicted` task, removing a direct Ivy dependency from the eviction-checking path
- Gate Ivy-specific tasks (`makePom`, `makeMavenPomOfSbtPlugin`, `deliverTask`, `publisher`, `publishOrSkip`, `depMap`, `GlobalPlugin.extract`) behind `useIvy` so they error or return empty when `useIvy = false`
- Fix `.value` hoisting bug in `ivylessPublishTask` where `ivyModule.value` and `publisher.value` inside `match/case` fallback branches were macro-hoisted as task dependencies regardless of runtime path, breaking all ivyless publishes
- Add `ivylessPublishM2Task` so `publishM2` works with `useIvy = false` (publishes to `~/.m2` via `ivylessPublishMavenToFile`)
- Flip `useIvy` default from `true` to `false`
Summary
- Adds support for passing JVM arguments inline to `run`, `runMain`, `bgRun`, `bgRunMain`, and `fgRun`/`fgRunMain` using `--` as a delimiter
- Syntax: `run <jvmArgs> -- <appArgs>` (e.g., `run -Xmx2G -Dapp.mode=debug -- arg1 arg2`)
- Fully backward compatible — without `--`, all arguments are treated as app args as before
- When `fork` is `false`, a warning is logged that JVM arguments will be ignored
Fixes unresolved dependency path reporting for Coursier (`ResolveException.failedPaths`) and adds a stable scripted regression.
This PR addresses 5168 by reconstructing unresolved dependency caller chains from the Coursier resolution graph and attaching them to `ResolveException.failedPaths`. That allows unresolved warnings to show the full path from the missing module up to the root project.
It also adds and stabilizes `lm-coursier/unresolved-path` scripted coverage by:
- using a local test Maven repo fixture (no flaky remote test dependency)
- checking update stream output via an sbt task (`checkLog`) instead of shell `grep`
- asserting the unresolved path includes missing module, transitive caller, and root project
- **Remove `ivyModule` from `updateTask0`**: Replace `IvySbt#Module` with `moduleSettings` + `DependencyResolution.moduleDescriptor()`, eliminating the Ivy dependency in the update path.
- **Replace direct Ivy usage in `Load.scala` and `TemplateCommandUtil`**: Use Coursier's `DependencyResolution` API for plugin bootstrapping and template resolution instead of constructing `IvySbt` instances directly.
- **Break `lm-coursier`'s dependency on `lm-ivy`**: Remove `IvySbt#Module` pattern match from `CoursierDependencyResolution`, replace `IBiblioResolver` usage in `Resolvers` with reflection, and switch build dependencies from `lmIvy` to `lmCore`.
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>