[2.x] fix: Fixes client-side run status (#9081)

**Problem**
In sbt 2.x, if we execute a run task from the shell,
and if the program fails, it ends up taking down the entire shell
because client-side run rethrows, which is not desirable
for the sbt shell.

**Solution**
1. Omit printing out success for ClientJobParams, which is the runinfo.
2. Print out success or error on the client-side for shell usage case.
This commit is contained in:
eugene yokota 2026-04-13 00:41:35 -04:00 committed by Eugene Yokota
parent e64db7927c
commit e8757c85e2
2 changed files with 23 additions and 9 deletions

View File

@ -179,6 +179,7 @@ class NetworkClient(
def success(message: => String): Unit = ()
def log(level: Level.Value, message: => String): Unit = console.appendLog(level, message)
}
private val interactive = arguments.commandArguments.isEmpty
private[sbt] def connectOrStartServerAndConnect(
promptCompleteUsers: Boolean,
@ -706,8 +707,19 @@ class NetworkClient(
case (`clientJob`, Some(json)) =>
import sbt.internal.worker.codec.JsonProtocol.given
Converter.fromJson[ClientJobParams](json) match {
case Success(params) => clientSideRun(params).get; Vector.empty
case Failure(_) => Vector.empty
case Success(params) =>
clientSideRun(params) match
case Success(_) =>
if interactive then console.success("ok")
else ()
Vector.empty
case Failure(e) =>
if interactive then
Vector(
(Level.Error, e.getMessage)
)
else throw e
case Failure(_) => Vector.empty
}
case (`Shutdown`, Some(_)) => Vector.empty
case (msg, _) if msg.startsWith("build/") => Vector.empty
@ -915,9 +927,8 @@ class NetworkClient(
withSignalHandler(contHandler, Signals.CONT) {
interactiveThread.set(Thread.currentThread)
val cleaned = arguments.commandArguments
val userCommands = cleaned.takeWhile(_ != TerminateAction)
val interactive = cleaned.isEmpty
val exit = cleaned.nonEmpty && userCommands.isEmpty
val userCommands = arguments.commandArguments.takeWhile(_ != TerminateAction)
val exit = arguments.commandArguments.nonEmpty && userCommands.isEmpty
attachUUID.set(sendJson(attach, s"""{"interactive": $interactive}"""))
val handler: () => Unit = () => {
def exitAbruptly() = {

View File

@ -17,6 +17,7 @@ import sbt.internal.util.complete.Parser
import sbt.internal.util.complete.Parser.{ failure, seq, success }
import sbt.internal.util.*
import sbt.internal.client.NetworkClient
import sbt.internal.worker.ClientJobParams
import sbt.std.Transform.DummyTaskMap
import sbt.util.{ Logger, Show }
import scala.annotation.tailrec
@ -87,16 +88,18 @@ object Aggregation {
import complete.*
val log = state.log
val extracted = Project.extract(state)
val success = results match
case Result.Value(_) => true
case Result.Inc(_) => false
// omit success printing for client-side run
val (success, jobParams) = results match
case Result.Value(Seq(KeyValue(_, p: ClientJobParams))) => (true, true)
case Result.Value(_) => (true, false)
case Result.Inc(_) => (false, false)
val isPaused = currentChannel(state) match
case Some(channel) => channel.isPaused
case None => false
results.toEither.foreach { r =>
if show.taskValues then printSettings(r, show.print) else ()
}
if !isPaused && show.success && !state.get(suppressShow).getOrElse(false) then
if !isPaused && show.success && !state.get(suppressShow).getOrElse(false) && !jobParams then
printSuccess(start, stop, extracted, success, cacheSummary, log)
else ()