mirror of https://github.com/sbt/sbt.git
[1.x] bport: Fix sbtopts files priority in sbt launch script (#8520)
- Change from prepend to append for machine/dist sbtopts - Change project .sbtopts from prepend to append so it appears last - Project-level .sbtopts now correctly overrides machine-wide configs - Add integration tests to verify sbtopts priority order - Extend testOutput to support dist and machine sbtopts testing - Copy staging directory to temp location to avoid modifying staging dir
This commit is contained in:
parent
b352eb1ff6
commit
1fd24af87e
|
|
@ -122,4 +122,76 @@ object RunnerScriptTest extends verify.BasicTestSuite with ShellScriptUtil:
|
||||||
testOutput("--sbt-cache")("--sbt-cache", "./cachePath"): (out: List[String]) =>
|
testOutput("--sbt-cache")("--sbt-cache", "./cachePath"): (out: List[String]) =>
|
||||||
assert(out.contains[String]("-Dsbt.global.localcache=./cachePath"))
|
assert(out.contains[String]("-Dsbt.global.localcache=./cachePath"))
|
||||||
|
|
||||||
|
// Test for issue #7179: sbtopts files priority
|
||||||
|
testOutput(
|
||||||
|
"project .sbtopts overrides dist sbtopts",
|
||||||
|
distSbtoptsContents = "-Dsbt.test.config=dist-default",
|
||||||
|
sbtOptsFileContents = "-Dsbt.test.config=project-local"
|
||||||
|
)("-d", "-v"): (out: List[String]) =>
|
||||||
|
if (isWindows) cancel("Test not supported on windows")
|
||||||
|
else
|
||||||
|
// Find the command line section
|
||||||
|
val cmdLineStart = out.indexWhere(_.contains("Executing command line"))
|
||||||
|
assert(cmdLineStart >= 0, "Command line section not found")
|
||||||
|
|
||||||
|
val cmdLine = out.drop(cmdLineStart + 1).takeWhile(!_.trim.isEmpty)
|
||||||
|
val distIndex = cmdLine.indexWhere(_.contains("Dsbt.test.config=dist-default"))
|
||||||
|
val projectIndex = cmdLine.indexWhere(_.contains("Dsbt.test.config=project-local"))
|
||||||
|
|
||||||
|
assert(distIndex >= 0, "Dist config not found in command line")
|
||||||
|
assert(projectIndex >= 0, "Project config not found in command line")
|
||||||
|
assert(
|
||||||
|
projectIndex > distIndex,
|
||||||
|
s"Project config should appear after dist config. distIndex=$distIndex, projectIndex=$projectIndex"
|
||||||
|
)
|
||||||
|
|
||||||
|
testOutput(
|
||||||
|
"project .sbtopts overrides machine sbtopts",
|
||||||
|
machineSbtoptsContents = "-Dsbt.test.config=machine-config",
|
||||||
|
sbtOptsFileContents = "-Dsbt.test.config=project-local"
|
||||||
|
)("-d", "-v"): (out: List[String]) =>
|
||||||
|
if (isWindows) cancel("Test not supported on windows")
|
||||||
|
else
|
||||||
|
// Find the command line section
|
||||||
|
val cmdLineStart = out.indexWhere(_.contains("Executing command line"))
|
||||||
|
assert(cmdLineStart >= 0, "Command line section not found")
|
||||||
|
|
||||||
|
val cmdLine = out.drop(cmdLineStart + 1).takeWhile(!_.trim.isEmpty)
|
||||||
|
val machineIndex = cmdLine.indexWhere(_.contains("Dsbt.test.config=machine-config"))
|
||||||
|
val projectIndex = cmdLine.indexWhere(_.contains("Dsbt.test.config=project-local"))
|
||||||
|
|
||||||
|
assert(machineIndex >= 0, "Machine config not found in command line")
|
||||||
|
assert(projectIndex >= 0, "Project config not found in command line")
|
||||||
|
assert(
|
||||||
|
projectIndex > machineIndex,
|
||||||
|
s"Project config should appear after machine config. machineIndex=$machineIndex, projectIndex=$projectIndex"
|
||||||
|
)
|
||||||
|
|
||||||
|
testOutput(
|
||||||
|
"project .sbtopts overrides both dist and machine sbtopts",
|
||||||
|
distSbtoptsContents = "-Dsbt.test.config=dist-default",
|
||||||
|
machineSbtoptsContents = "-Dsbt.test.config=machine-config",
|
||||||
|
sbtOptsFileContents = "-Dsbt.test.config=project-local"
|
||||||
|
)("-d", "-v"): (out: List[String]) =>
|
||||||
|
if (isWindows) cancel("Test not supported on windows")
|
||||||
|
else
|
||||||
|
// Find the command line section
|
||||||
|
val cmdLineStart = out.indexWhere(_.contains("Executing command line"))
|
||||||
|
assert(cmdLineStart >= 0, "Command line section not found")
|
||||||
|
|
||||||
|
val cmdLine = out.drop(cmdLineStart + 1).takeWhile(!_.trim.isEmpty)
|
||||||
|
val distIndex = cmdLine.indexWhere(_.contains("Dsbt.test.config=dist-default"))
|
||||||
|
val machineIndex = cmdLine.indexWhere(_.contains("Dsbt.test.config=machine-config"))
|
||||||
|
val projectIndex = cmdLine.indexWhere(_.contains("Dsbt.test.config=project-local"))
|
||||||
|
|
||||||
|
// When machine sbtopts exists, the script only loads machine (not dist) due to if-else structure
|
||||||
|
// So dist should NOT be present, but machine and project should be
|
||||||
|
assert(distIndex < 0, "Dist config should NOT be present when machine config exists")
|
||||||
|
assert(machineIndex >= 0, "Machine config not found in command line")
|
||||||
|
assert(projectIndex >= 0, "Project config not found in command line")
|
||||||
|
assert(
|
||||||
|
machineIndex < projectIndex,
|
||||||
|
s"Machine config should appear before project config. machineIndex=$machineIndex, projectIndex=$projectIndex"
|
||||||
|
)
|
||||||
|
|
||||||
end RunnerScriptTest
|
end RunnerScriptTest
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,9 @@ trait ShellScriptUtil extends BasicTestSuite {
|
||||||
val isWindows: Boolean =
|
val isWindows: Boolean =
|
||||||
sys.props("os.name").toLowerCase(java.util.Locale.ENGLISH).contains("windows")
|
sys.props("os.name").toLowerCase(java.util.Locale.ENGLISH).contains("windows")
|
||||||
|
|
||||||
private val javaBinDir = new File("launcher-package/integration-test/bin").getAbsolutePath
|
protected val javaBinDir = new File("launcher-package/integration-test/bin").getAbsolutePath
|
||||||
|
|
||||||
private def retry[A1](f: () => A1, maxAttempt: Int = 10): A1 =
|
protected def retry[A1](f: () => A1, maxAttempt: Int = 10): A1 =
|
||||||
try {
|
try {
|
||||||
f()
|
f()
|
||||||
} catch {
|
} catch {
|
||||||
|
|
@ -33,12 +33,18 @@ trait ShellScriptUtil extends BasicTestSuite {
|
||||||
javaOpts: String = "",
|
javaOpts: String = "",
|
||||||
sbtOpts: String = "",
|
sbtOpts: String = "",
|
||||||
sbtOptsFileContents: String = "",
|
sbtOptsFileContents: String = "",
|
||||||
javaToolOptions: String = ""
|
javaToolOptions: String = "",
|
||||||
|
distSbtoptsContents: String = "",
|
||||||
|
machineSbtoptsContents: String = ""
|
||||||
)(args: String*)(f: List[String] => Any) =
|
)(args: String*)(f: List[String] => Any) =
|
||||||
test(name) {
|
test(name) {
|
||||||
val workingDirectory = Files.createTempDirectory("sbt-launcher-package-test").toFile
|
val workingDirectory = Files.createTempDirectory("sbt-launcher-package-test").toFile
|
||||||
retry(() => IO.copyDirectory(new File("launcher-package/citest"), workingDirectory))
|
retry(() => IO.copyDirectory(new File("launcher-package/citest"), workingDirectory))
|
||||||
|
|
||||||
|
var sbtHome: Option[File] = None
|
||||||
|
var configHome: Option[File] = None
|
||||||
|
var tempSbtHome: Option[File] = None
|
||||||
|
var testSbtScript: File = sbtScript
|
||||||
try
|
try
|
||||||
val sbtOptsFile = new File(workingDirectory, ".sbtopts")
|
val sbtOptsFile = new File(workingDirectory, ".sbtopts")
|
||||||
sbtOptsFile.createNewFile()
|
sbtOptsFile.createNewFile()
|
||||||
|
|
@ -48,24 +54,77 @@ trait ShellScriptUtil extends BasicTestSuite {
|
||||||
} finally {
|
} finally {
|
||||||
writer.close()
|
writer.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
val originalSbtHome = sbtScript.getParentFile.getParentFile
|
||||||
|
val tempSbtHomeDir = Files.createTempDirectory("sbt-home-test").toFile
|
||||||
|
tempSbtHome = Some(tempSbtHomeDir)
|
||||||
|
// Copy the entire sbt home directory structure
|
||||||
|
retry(() => IO.copyDirectory(originalSbtHome, tempSbtHomeDir))
|
||||||
|
// 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)
|
||||||
|
// Store reference for cleanup
|
||||||
|
sbtHome = Some(tempSbtHomeDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure no machine sbtopts exists when testing dist-only (unless explicitly provided)
|
||||||
|
// The script only loads dist if machine doesn't exist
|
||||||
|
if (distSbtoptsContents.nonEmpty && machineSbtoptsContents.isEmpty && configHome.isEmpty) {
|
||||||
|
// Set XDG_CONFIG_HOME to a temp directory without sbtopts to prevent default machine sbtopts from being found
|
||||||
|
val emptyConfigHome = Files.createTempDirectory("empty-config-home").toFile
|
||||||
|
envVars("XDG_CONFIG_HOME") = emptyConfigHome.getAbsolutePath
|
||||||
|
// Also unset SBT_ETC_FILE if it exists
|
||||||
|
sys.env.get("SBT_ETC_FILE").foreach(_ => envVars("SBT_ETC_FILE") = "")
|
||||||
|
// Store for cleanup
|
||||||
|
configHome = Some(emptyConfigHome)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up machine sbtopts if provided
|
||||||
|
if (machineSbtoptsContents.nonEmpty) {
|
||||||
|
val configHomeDir = Files.createTempDirectory("config-home").toFile
|
||||||
|
configHome = Some(configHomeDir)
|
||||||
|
val machineSbtoptsDir = new File(configHomeDir, "sbt")
|
||||||
|
machineSbtoptsDir.mkdirs()
|
||||||
|
val machineSbtoptsFile = new File(machineSbtoptsDir, "sbtopts")
|
||||||
|
IO.write(machineSbtoptsFile, machineSbtoptsContents)
|
||||||
|
envVars("XDG_CONFIG_HOME") = configHomeDir.getAbsolutePath
|
||||||
|
}
|
||||||
|
|
||||||
val path = sys.env.getOrElse("PATH", sys.env("Path"))
|
val path = sys.env.getOrElse("PATH", sys.env("Path"))
|
||||||
|
envVars("JAVA_OPTS") = javaOpts
|
||||||
|
envVars("SBT_OPTS") = sbtOpts
|
||||||
|
envVars("JAVA_TOOL_OPTIONS") = javaToolOptions
|
||||||
|
if (isWindows)
|
||||||
|
envVars("JAVACMD") = new File(javaBinDir, "java").getAbsolutePath()
|
||||||
|
else
|
||||||
|
envVars("PATH") = javaBinDir + File.pathSeparator + path
|
||||||
|
|
||||||
val out = scala.sys.process
|
val out = scala.sys.process
|
||||||
.Process(
|
.Process(
|
||||||
Seq(sbtScript.getAbsolutePath) ++ args,
|
Seq(testSbtScript.getAbsolutePath) ++ args,
|
||||||
workingDirectory,
|
workingDirectory,
|
||||||
"JAVA_OPTS" -> javaOpts,
|
envVars.toSeq*
|
||||||
"SBT_OPTS" -> sbtOpts,
|
|
||||||
"JAVA_TOOL_OPTIONS" -> javaToolOptions,
|
|
||||||
if (isWindows)
|
|
||||||
"JAVACMD" -> new File(javaBinDir, "java").getAbsolutePath()
|
|
||||||
else
|
|
||||||
"PATH" -> (javaBinDir + File.pathSeparator + path)
|
|
||||||
)
|
)
|
||||||
.!!
|
.!!
|
||||||
.linesIterator
|
.linesIterator
|
||||||
.toList
|
.toList
|
||||||
f(out)
|
f(out)
|
||||||
()
|
()
|
||||||
finally IO.delete(workingDirectory)
|
finally
|
||||||
|
IO.delete(workingDirectory)
|
||||||
|
// Clean up temporary sbt home directory if we created one
|
||||||
|
tempSbtHome.foreach(IO.delete)
|
||||||
|
configHome.foreach(IO.delete)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
9
sbt
9
sbt
|
|
@ -850,14 +850,15 @@ original_args=("$@")
|
||||||
|
|
||||||
# Pull in the machine-wide settings configuration.
|
# Pull in the machine-wide settings configuration.
|
||||||
if [[ -f "$machine_sbt_opts_file" ]]; then
|
if [[ -f "$machine_sbt_opts_file" ]]; then
|
||||||
set -- $(loadConfigFile "$machine_sbt_opts_file") "$@"
|
set -- "$@" $(loadConfigFile "$machine_sbt_opts_file")
|
||||||
else
|
else
|
||||||
# Otherwise pull in the default settings configuration.
|
# Otherwise pull in the default settings configuration.
|
||||||
[[ -f "$dist_sbt_opts_file" ]] && set -- $(loadConfigFile "$dist_sbt_opts_file") "$@"
|
[[ -f "$dist_sbt_opts_file" ]] && set -- "$@" $(loadConfigFile "$dist_sbt_opts_file")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Pull in the project-level config file, if it exists.
|
# Pull in the project-level config file, if it exists (highest priority, overrides machine/dist).
|
||||||
[[ -f "$sbt_opts_file" ]] && set -- $(loadConfigFile "$sbt_opts_file") "$@"
|
# Append so it appears last in command line and wins for duplicate properties.
|
||||||
|
[[ -f "$sbt_opts_file" ]] && set -- "$@" $(loadConfigFile "$sbt_opts_file")
|
||||||
|
|
||||||
# Pull in the project-level java config, if it exists.
|
# Pull in the project-level java config, if it exists.
|
||||||
[[ -f ".jvmopts" ]] && export JAVA_OPTS="$JAVA_OPTS $(loadConfigFile .jvmopts)"
|
[[ -f ".jvmopts" ]] && export JAVA_OPTS="$JAVA_OPTS $(loadConfigFile .jvmopts)"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue