From c78c8a8ac8c57c310366a5347cf17c5456309f96 Mon Sep 17 00:00:00 2001 From: Anatolii Kmetiuk Date: Thu, 25 Jun 2026 15:48:40 +0900 Subject: [PATCH] [1.x] fix: Fixes runner parsing build.properties with whitespaces (#9374) Problem When build.properties contains whitespaces like sbt.version = 1.12.12, parsing fails and the detected sbt version falls back to 2.0.0. Solution Trim whitespaces in build.properties. --- .../src/test/scala/RunnerScriptTest.scala | 31 ++++++++++++++ .../src/test/scala/ShellScriptUtil.scala | 41 ++++++++++++++++--- launcher-package/src/universal/bin/sbt.bat | 8 +++- sbt | 6 +-- 4 files changed, 75 insertions(+), 11 deletions(-) diff --git a/launcher-package/integration-test/src/test/scala/RunnerScriptTest.scala b/launcher-package/integration-test/src/test/scala/RunnerScriptTest.scala index 24dc28853..516f4e4ba 100644 --- a/launcher-package/integration-test/src/test/scala/RunnerScriptTest.scala +++ b/launcher-package/integration-test/src/test/scala/RunnerScriptTest.scala @@ -153,6 +153,37 @@ object RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUtil: assertVersionOutput(out) () + testOutput( + "sbt --version reports spaced sbt.version from project/build.properties (sbt 1.x)", + citestVariant = "citest", + buildPropsContents = "sbt.version = 1.12.11\n", + )("--version"): (out: List[String]) => + val lines = + out.mkString(System.lineSeparator()).linesIterator.map(_.stripPrefix("[0J").trim).toList + assert(lines.exists(_.matches("^sbt version in this project: 1\\.12\\.11\\r?$"))) + assert(lines.exists(_.matches("^sbt runner version: " + versionPattern + "\\r?$"))) + () + + testOutput( + "sbt -v '++ 3' ci does not run native client for spaced sbt 1.x project", + citestVariant = "citest", + buildPropsContents = "sbt.version = 1.12.11\n", + stagedRunnerVersionOverride = "2.0.0", + )("-v", "++ 3", "ci"): (out: List[String]) => + assert(!out.exists(_.contains("running native client"))) + assert(out.exists(_.contains("sbt-launch.jar"))) + () + + // Test for issue #4189: Improve -help and help commands + testOutput( + "sbt --help should show getting-started hints", + citestVariant = "citest", + )("--help"): (out: List[String]) => + val helpText = out.mkString(System.lineSeparator()) + assert(helpText.contains("Getting started with sbt")) + assert(helpText.contains("sbt init")) + assert(helpText.contains("help ")) + testOutput("--sbt-cache")("--sbt-cache", "./cachePath"): (out: List[String]) => assert(out.contains[String]("-Dsbt.global.localcache=./cachePath")) diff --git a/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala b/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala index 6ffa3a72d..8065cd3b6 100644 --- a/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala +++ b/launcher-package/integration-test/src/test/scala/ShellScriptUtil.scala @@ -37,6 +37,8 @@ trait ShellScriptUtil extends BasicTestSuite { distSbtoptsContents: String = "", machineSbtoptsContents: String = "", jvmoptsFileContents: String = "", + buildPropsContents: String = "", + stagedRunnerVersionOverride: String = "", windowsSupport: Boolean = true, citestVariant: String = "citest", )(args: String*)(f: List[String] => Any) = @@ -101,12 +103,18 @@ trait ShellScriptUtil extends BasicTestSuite { } } + if (buildPropsContents.nonEmpty) { + val projectDir = new File(workingDirectory, "project") + projectDir.mkdirs() + IO.write(new File(projectDir, "build.properties"), buildPropsContents) + } + val envVars = scala.collection.mutable.Map[String, String]() // Set up dist sbtopts if provided // Note: sbt script derives sbt_home from script location, not SBT_HOME env var // Copy the sbt staging directory to a temp location to avoid modifying the staging directory - if (distSbtoptsContents.nonEmpty) { + if (distSbtoptsContents.nonEmpty || stagedRunnerVersionOverride.nonEmpty) { val originalSbtHome = sbtScript.getParentFile.getParentFile val tempSbtHomeDir = Files.createTempDirectory("sbt-home-test").toFile tempSbtHome = Some(tempSbtHomeDir) @@ -115,11 +123,32 @@ trait ShellScriptUtil extends BasicTestSuite { // Get the script from the copied directory val binDir = new File(tempSbtHomeDir, "bin") testSbtScript = new File(binDir, sbtScript.getName) - // Create dist sbtopts in the copied directory - val distSbtoptsDir = new File(tempSbtHomeDir, "conf") - distSbtoptsDir.mkdirs() - val distSbtoptsFile = new File(distSbtoptsDir, "sbtopts") - IO.write(distSbtoptsFile, distSbtoptsContents) + if (distSbtoptsContents.nonEmpty) { + // Create dist sbtopts in the copied directory + val distSbtoptsDir = new File(tempSbtHomeDir, "conf") + distSbtoptsDir.mkdirs() + IO.write(new File(distSbtoptsDir, "sbtopts"), distSbtoptsContents) + } + if (stagedRunnerVersionOverride.nonEmpty) { + val isBat = testSbtScript.getName.endsWith(".bat") + val prefix = + if (isBat) "set init_sbt_version=" else "declare init_sbt_version=" + val pattern = + if (isBat) "(?m)^set init_sbt_version=.*$" + else "(?m)^declare init_sbt_version=.*$" + val original = IO.read(testSbtScript) + val regex = pattern.r + assert( + regex.findFirstIn(original).nonEmpty, + s"init_sbt_version line not found in $testSbtScript" + ) + val replacement = + java.util.regex.Matcher.quoteReplacement(prefix + stagedRunnerVersionOverride) + val updated = regex.replaceAllIn(original, replacement) + assert(updated.contains(prefix + stagedRunnerVersionOverride)) + IO.write(testSbtScript, updated) + if (!isBat) testSbtScript.setExecutable(true) + } // Store reference for cleanup sbtHome = Some(tempSbtHomeDir) } diff --git a/launcher-package/src/universal/bin/sbt.bat b/launcher-package/src/universal/bin/sbt.bat index 3f6dc216a..a8a04dbad 100755 --- a/launcher-package/src/universal/bin/sbt.bat +++ b/launcher-package/src/universal/bin/sbt.bat @@ -95,8 +95,12 @@ if defined JAVA_HOMES ( if exist "project\build.properties" ( for /F "eol=# delims== tokens=1*" %%a in (project\build.properties) do ( - if "%%a" == "sbt.version" if not "%%b" == "" ( - set build_props_sbt_version=%%b + set "_prop_key=" + set "_prop_val=" + for /F "tokens=1 delims= " %%k in ("%%a") do set "_prop_key=%%k" + for /F "tokens=1 delims= " %%v in ("%%b") do set "_prop_val=%%v" + if "!_prop_key!" == "sbt.version" if not "!_prop_val!" == "" ( + set "build_props_sbt_version=!_prop_val!" ) ) ) diff --git a/sbt b/sbt index 81a2ad3f9..2405e16f8 100755 --- a/sbt +++ b/sbt @@ -791,10 +791,10 @@ loadConfigFile() { } loadPropFile() { - # trim key and value so as to be more forgiving with spaces around the '=': - k=$(trimString $k) - v=$(trimString $v) while IFS='=' read -r k v; do + # trim key and value so as to be more forgiving with spaces around the '=': + k=$(trimString "$k") + v=$(trimString "$v") if [[ "$k" == "sbt.version" ]]; then build_props_sbt_version="$v" fi