**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**
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
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/
- **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>
When a test fails with ClassNotFoundException/IllegalAccessError, sbt
suggests a set command to change classLoaderLayeringStrategy. If the
project name contains hyphens (e.g. bug-report), the suggested command
was syntactically invalid because it parses as subtraction in Scala.
Quote project IDs using Util.quoteIfNotScalaId so the suggested
commands are valid when copy-pasted.
Fixes#5803
**Problem**
Running sbt 2.x with JDK 8 produces a confusing "server was not
detected" error because the JDK version check only required JDK 8+
and only ran in the non-native-client path.
**Solution**
Move java_version detection before the native client decision and add
checkJava17ForSbt2 that requires JDK 17+ when sbt major version >= 2.
Fixes#8813
* [2.x] fix: Fail early when sbt 2.x is run with JDK < 17 (sbtw)
Move JDK version check before native client decision in sbtw and
require JDK 17+ when build.properties declares sbt 2.x.
* [2.x] fix: Fail early when sbt 2.x is run with JDK < 17 (sbt.bat)
Move checkjava before native client decision in sbt.bat and require
JDK 17+ when build.properties declares sbt 2.x.
* [2.x] test: Add minimumJdkVersion helper and unit tests for sbtw
Extract JDK version check logic into Runner.minimumJdkVersion for
testability. Add RunnerSpec with tests for sbt 1.x, 2.x, and 3.x
version detection.
* [2.x] test: Bump fake java to JDK 17 for integration tests
The fake java script used by launcher integration tests reported
JDK 8. Since sbt 2.x now requires JDK 17+, the citest2 (sbt 2.x)
integration tests would fail with the new JDK version check.
* Simulate JDK 9+ rt.jar handling in fake java script
Instead of silently ignoring --rt-ext-dir (which causes sbt.bat
to mkdir on an empty string), properly simulate JDK 9+ behavior
by creating a temp directory with java9-rt-ext- prefix and a
dummy rt.jar inside it.
**Problem**
`scalaOrganization` was ignored during compiler bridge resolution because `ZincLmUtil.getDefaultBridgeModule` hard-coded `ScalaArtifacts.Organization` (`org.scala-lang`).
**Solution**
Add a `scalaOrganization` parameter to `ZincLmUtil` methods, and passing `scalaOrganization` value to those methods.
Generated-by: Claude Opus 4.6
**Problem**
Forked console currently pulls in full Zinc, which includes JLine.
**Solution**
This implements a lighter-weight, full Java ForkConsoleMain,
which no longer depends on JLine.
**Problem**
Apparently sbt can fail when it doesn't have rm,
which can happen "when building relocatable RPM's and building an OS image in a chroot."
**Solution**
It was suggested that we require coreutils.
**Problem**
The configuration name translation in logging was incorrect. When a
configuration like MultiJvm (id="MultiJvm", name="multi-jvm") was
displayed, it showed "Multi-jvm" instead of "MultiJvm" because the
display logic was guessing the identifier by capitalizing the ivy
config name.
**Solution**
This fix:
- Adds configNameToIdent reverse mapping in ConfigIndex to look up
the correct Configuration.id from the ivy config name
- Adds toConfigIdent method in KeyIndex trait for display lookup
- Updates Scope.display to accept a config name lookup function
- Updates showLoadingKey and showContextKey to use the index lookup
Fixes#5211
Generated-by: Claude
**Consuming BOMs**
- You can declare a BOM with `.pomOnly()` and versionless deps with `"*"`:
- `libraryDependencies += ("com.fasterxml.jackson" % "jackson-bom" % "2.21.0").pomOnly()`
- `libraryDependencies += "com.fasterxml.jackson.core" % "jackson-core" % "*"`
- BOMs are passed to Coursier via `Resolve.addBom()`; version `"*"` is resolved from the BOM.
**makePom**
- POM-only dependencies are emitted under `<dependencyManagement><dependencies>...</dependencies></dependencyManagement>` with `<type>pom</type>` and `<scope>import</scope>`.
- Dependencies with version `"*"` are emitted without a `<version>` element so Maven uses the BOM-managed version.
**Ivy / publishLocal emulation**
- When publishing to Ivy (e.g. `publishLocal`), BOM-resolved versions (deps that had `"*"`) are written into the published `ivy.xml` as forced dependencies (`force="true"`), so consumers that depend on this module get those versions.
In PR #8621, I added a new `keepTempDirectory` parameter to `ScriptedRun.run()` and `invoke()` methods. To suppress MiMa warnings, I added `mimaBinaryIssueFilters` for:
- `DirectMissingMethodProblem("sbt.ScriptedRun.run")`
- `DirectMissingMethodProblem("sbt.ScriptedRun.invoke")`
- `DirectMissingMethodProblem` for various internal `RunV1`, `RunV2`, `RunInParallelV1`, `RunInParallelV2` classes
However, this broke binary compatibility, which prevents sbt 1.x from calling sbt 2.x for cross-building (building sbt 2.x plugins using sbt 1.x).
This adds a scripted integration test to verify that the `bspConfig` task correctly generates a valid BSP connection file (`.bsp/sbt.json`) when the embedded launcher JAR is present in the classpath.
**Problem**
When running scripted tests to debug sbt plugins, the temporary directories (`/private/var/folder/...`) are automatically deleted after tests complete. This makes it difficult to inspect the test state for debugging purposes, requiring workarounds like adding `$ pause` commands and manually copying directories.
**Solution**
Added a new `scriptedKeepTempDirectory` setting that allows users to preserve temporary directories after scripted tests complete. When enabled, the temporary directory path is logged so users can inspect it.
Usage:
```scala
scriptedKeepTempDirectory := true
```
**What it does**
When you run `dependencyLock`, sbt generates a `deps.lock` file that captures your resolved dependencies. This file can be checked into version control to ensure reproducible builds across different machines and CI environments.
**New tasks**
- **`dependencyLock`** - Generates the lock file from the current resolution
- **`dependencyLockCheck`** - Validates the lock file is up-to-date (fails build if stale)
**How it works**
The lock file stores a hash of your declared dependencies and resolvers. When dependencies change, the hash changes, and `dependencyLockCheck` will fail until you regenerate the lock file.
If no lock file exists, `dependencyLockCheck` passes silently - this allows gradual adoption.
* test: Migrate TrackedSpec.scala to verify.BasicTestSuite
Migrate TrackedSpec.scala from ScalaTest's AnyFlatSpec to
verify.BasicTestSuite, following the pattern established by other
test files in the sbt codebase.
Changes:
- Replace AnyFlatSpec class with BasicTestSuite object
- Convert 'should ... in' syntax to 'test(...)' syntax
- Use Scala 3 syntax with colon indentation
- Change === to == for assertions (BasicTestSuite style)
- Replace fail() with throw new AssertionError for explicit failures
- Add 'end TrackedSpec' marker
- Convert braces to colon-style for if expressions