Fix #8344: Skip interactive prompt in batch mode when project loading fails

When project loading fails in batch mode, sbt was showing an interactive
prompt asking the user to choose between retry, quit, last, or ignore.
However, in batch mode there is no interactive terminal, causing the
process to hang waiting for input that will never come.

This fix checks if we're in batch mode (Prompt.Batch) and automatically
exits with failure (equivalent to 'q' quit option) without prompting the
user. This prevents infinite retry loops on persistent errors and allows
batch mode scripts to fail fast, which is appropriate for CI/CD environments.

The interactive behavior remains unchanged for non-batch mode.
This commit is contained in:
Francluob 2026-01-08 17:57:54 +01:00
parent a4ad73ddf6
commit 4976ec7dc7
1 changed files with 23 additions and 18 deletions

View File

@ -869,25 +869,30 @@ object BuiltinCommands {
@tailrec
private def doLoadFailed(s: State, loadArg: String): State = {
s.log.warn("Project loading failed: (r)etry, (q)uit, (l)ast, or (i)gnore? (default: r)")
val result: Int =
try
ITerminal.get.withRawInput(System.in.read) match {
case -1 => 'q'.toInt
case b => b
}
catch { case _: ClosedChannelException => 'q' }
def retry: State = loadProjectCommand(LoadProject, loadArg) :: s.clearGlobalLog
def ignoreMsg: String =
if (Project.isProjectLoaded(s)) "using previously loaded project" else "no project loaded"
// In batch mode, exit with failure to avoid infinite retry loops on persistent errors
if (ITerminal.console.prompt == Prompt.Batch) {
s.exit(ok = false)
} else {
s.log.warn("Project loading failed: (r)etry, (q)uit, (l)ast, or (i)gnore? (default: r)")
val result: Int =
try
ITerminal.get.withRawInput(System.in.read) match {
case -1 => 'q'.toInt
case b => b
}
catch { case _: ClosedChannelException => 'q' }
def retry: State = loadProjectCommand(LoadProject, loadArg) :: s.clearGlobalLog
def ignoreMsg: String =
if (Project.isProjectLoaded(s)) "using previously loaded project" else "no project loaded"
result.toChar match {
case '\n' | '\r' => retry
case 'r' | 'R' => retry
case 'q' | 'Q' => s.exit(ok = false)
case 'i' | 'I' => s.log.warn(s"Ignoring load failure: $ignoreMsg."); s
case 'l' | 'L' => LastCommand :: loadProjectCommand(LoadFailed, loadArg) :: s
case c => println(s"Invalid response: '$c'"); doLoadFailed(s, loadArg)
result.toChar match {
case '\n' | '\r' => retry
case 'r' | 'R' => retry
case 'q' | 'Q' => s.exit(ok = false)
case 'i' | 'I' => s.log.warn(s"Ignoring load failure: $ignoreMsg."); s
case 'l' | 'L' => LastCommand :: loadProjectCommand(LoadFailed, loadArg) :: s
case c => println(s"Invalid response: '$c'"); doLoadFailed(s, loadArg)
}
}
}