From 056e76eb9f0efaadded59baafd4c4a17f5925076 Mon Sep 17 00:00:00 2001
From: 0xnm <0xnm@users.noreply.github.com>
Date: Wed, 16 Oct 2024 23:01:59 +0200
Subject: [PATCH] Add test reporter for Kotlin/JS
---
.../kotlinlib/web/3-hello-kotlinjs/build.mill | 2 +-
.../kotlinlib/KotlinWorkerManagerImpl.scala | 2 +-
.../mill/kotlinlib/js/KotlinJSModule.scala | 223 ++++++++++++++----
.../contrib/ktfmt/KtfmtModuleTests.scala | 2 +-
.../js/KotlinJSKotestModuleTests.scala | 38 ++-
...KotlinJSKotlinTestPackageModuleTests.scala | 36 ++-
.../mill/kotlinlib/js/KotlinJSLinkTests.scala | 2 -
main/util/src/mill/util/Jvm.scala | 13 +-
scalalib/src/mill/scalalib/JavaModule.scala | 2 +-
.../scalalib/publish/SonatypeHelpers.scala | 2 +-
.../src/mill/scalalib/AssemblyTestUtils.scala | 4 +-
.../scalanativelib/ScalaNativeModule.scala | 3 +-
12 files changed, 250 insertions(+), 79 deletions(-)
diff --git a/example/kotlinlib/web/3-hello-kotlinjs/build.mill b/example/kotlinlib/web/3-hello-kotlinjs/build.mill
index 800fb8db491..bf284a6fd79 100644
--- a/example/kotlinlib/web/3-hello-kotlinjs/build.mill
+++ b/example/kotlinlib/web/3-hello-kotlinjs/build.mill
@@ -33,7 +33,7 @@ Compiling 1 Kotlin sources to .../out/test/compile.dest/classes...
Linking IR to .../out/test/linkBinary.dest/binaries
produce executable: .../out/test/linkBinary.dest/binaries
...
-error: ...AssertionFailedError: expected:<"
Hello World Wrong
"> but was:<"Hello World
...
+error: ... expected:<"Hello World Wrong
"> but was:<"Hello World
...
...
> cat out/test/linkBinary.dest/binaries/test.js # Generated javascript on disk
diff --git a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala
index 5cfc77c10d6..d55e600e3c4 100644
--- a/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala
+++ b/kotlinlib/src/mill/kotlinlib/KotlinWorkerManagerImpl.scala
@@ -1,7 +1,7 @@
/*
* Original code copied from https://github.com/lefou/mill-kotlin
* Original code published under the Apache License Version 2
- * Original Copyright 2020-20 24 Tobias Roeser
+ * Original Copyright 2020-2024 Tobias Roeser
*/
package mill.kotlinlib
diff --git a/kotlinlib/src/mill/kotlinlib/js/KotlinJSModule.scala b/kotlinlib/src/mill/kotlinlib/js/KotlinJSModule.scala
index f0a46df5070..0e8934a2788 100644
--- a/kotlinlib/src/mill/kotlinlib/js/KotlinJSModule.scala
+++ b/kotlinlib/src/mill/kotlinlib/js/KotlinJSModule.scala
@@ -10,10 +10,12 @@ import mill.scalalib.api.CompilationResult
import mill.testrunner.TestResult
import mill.util.Jvm
import mill.{Agg, Args, T}
+import sbt.testing.Status
import upickle.default.{macroRW, ReadWriter => RW}
-import java.io.File
+import java.io.{File, FileNotFoundException}
import java.util.zip.ZipFile
+import scala.xml.XML
/**
* This module is very experimental. Don't use it, it is still under the development, APIs can change.
@@ -105,54 +107,69 @@ trait KotlinJSModule extends KotlinModule { outer =>
Task.Command { run(args)() }
override def run(args: Task[Args] = Task.Anon(Args())): Command[Unit] = Task.Command {
- val binaryKind = kotlinJSBinaryKind()
- if (binaryKind.isEmpty || binaryKind.get != BinaryKind.Executable) {
- T.log.error("Run action is only allowed for the executable binary")
+ runJsBinary(
+ args = args(),
+ binaryKind = kotlinJSBinaryKind(),
+ moduleKind = moduleKind(),
+ binaryDir = linkBinary().classes.path,
+ runTarget = kotlinJSRunTarget(),
+ envArgs = T.env,
+ workingDir = T.dest
+ ).getOrThrow
+ ()
+ }
+
+ override def runMainLocal(
+ @arg(positional = true) mainClass: String,
+ args: String*
+ ): Command[Unit] = Task.Command[Unit] {
+ mill.api.Result.Failure("runMain is not supported in Kotlin/JS.")
+ }
+
+ override def runMain(@arg(positional = true) mainClass: String, args: String*): Command[Unit] =
+ Task.Command[Unit] {
+ mill.api.Result.Failure("runMain is not supported in Kotlin/JS.")
}
- val moduleKind = this.moduleKind()
+ protected[js] def runJsBinary(
+ args: Args = Args(),
+ binaryKind: Option[BinaryKind],
+ moduleKind: ModuleKind,
+ binaryDir: os.Path,
+ runTarget: Option[RunTarget],
+ envArgs: Map[String, String] = Map.empty[String, String],
+ workingDir: os.Path
+ )(implicit ctx: mill.api.Ctx): Result[Int] = {
+ if (binaryKind.isEmpty || binaryKind.get != BinaryKind.Executable) {
+ return Result.Failure("Run action is only allowed for the executable binary")
+ }
- val linkResult = linkBinary().classes
if (
moduleKind == ModuleKind.NoModule &&
- linkResult.path.toIO.listFiles().count(_.getName.endsWith(".js")) > 1
+ binaryDir.toIO.listFiles().count(_.getName.endsWith(".js")) > 1
) {
T.log.info("No module type is selected for the executable, but multiple .js files found in the output folder." +
" This will probably lead to the dependency resolution failure.")
}
- kotlinJSRunTarget() match {
- case Some(RunTarget.Node) => {
- val testBinaryPath = (linkResult.path / s"${moduleName()}.${moduleKind.extension}")
+ runTarget match {
+ case Some(RunTarget.Node) =>
+ val binaryPath = (binaryDir / s"${moduleName()}.${moduleKind.extension}")
.toIO.getAbsolutePath
Jvm.runSubprocess(
commandArgs = Seq(
"node"
- ) ++ args().value ++ Seq(testBinaryPath),
- envArgs = T.env,
- workingDir = T.dest
+ ) ++ args.value ++ Seq(binaryPath),
+ envArgs = envArgs,
+ workingDir = workingDir
)
- }
case Some(x) =>
- T.log.error(s"Run target $x is not supported")
+ Result.Failure(s"Run target $x is not supported")
case None =>
- throw new IllegalArgumentException("Executable binary should have a run target selected.")
+ Result.Failure("Executable binary should have a run target selected.")
}
-
- }
-
- override def runMainLocal(
- @arg(positional = true) mainClass: String,
- args: String*
- ): Command[Unit] = Task.Command[Unit] {
- mill.api.Result.Failure("runMain is not supported in Kotlin/JS.")
}
- override def runMain(@arg(positional = true) mainClass: String, args: String*): Command[Unit] =
- Task.Command[Unit] {
- mill.api.Result.Failure("runMain is not supported in Kotlin/JS.")
- }
-
/**
* The actual Kotlin compile task (used by [[compile]] and [[kotlincHelp]]).
*/
@@ -386,8 +403,8 @@ trait KotlinJSModule extends KotlinModule { outer =>
}
}
- private def moduleName() = fullModuleNameSegments().last
- private def fullModuleName() = fullModuleNameSegments().mkString("-")
+ protected[js] def moduleName(): String = fullModuleNameSegments().last
+ protected[js] def fullModuleName(): String = fullModuleNameSegments().mkString("-")
// **NOTE**: This logic may (and probably is) be incomplete
private def isKotlinJsLibrary(path: os.Path)(implicit ctx: mill.api.Ctx): Boolean = {
@@ -422,6 +439,8 @@ trait KotlinJSModule extends KotlinModule { outer =>
*/
trait KotlinJSTests extends KotlinTests with KotlinJSModule {
+ private val defaultXmlReportName = "test-report.xml"
+
// region private
// TODO may be optimized if there is a single folder for all modules
@@ -439,7 +458,7 @@ trait KotlinJSModule extends KotlinModule { outer =>
commandArgs = Seq("npm", "install", "mocha@10.2.0"),
envArgs = T.env,
workingDir = workingDir
- )
+ ).getOrThrow
PathRef(workingDir / "node_modules" / "mocha" / "bin" / "mocha.js")
}
@@ -448,8 +467,8 @@ trait KotlinJSModule extends KotlinModule { outer =>
Jvm.runSubprocess(
commandArgs = Seq("npm", "install", "source-map-support@0.5.21"),
envArgs = T.env,
- workingDir = nodeModulesDir().path
- )
+ workingDir = workingDir
+ ).getOrThrow
PathRef(workingDir / "node_modules" / "source-map-support" / "register.js")
}
@@ -457,7 +476,9 @@ trait KotlinJSModule extends KotlinModule { outer =>
override def testFramework = ""
- override def kotlinJSBinaryKind: T[Option[BinaryKind]] = Some(BinaryKind.Executable)
+ override def kotlinJSRunTarget: T[Option[RunTarget]] = outer.kotlinJSRunTarget()
+
+ override def moduleKind: T[ModuleKind] = ModuleKind.PlainModule
override def splitPerModule = false
@@ -470,20 +491,136 @@ trait KotlinJSModule extends KotlinModule { outer =>
args: Task[Seq[String]],
globSelectors: Task[Seq[String]]
): Task[(String, Seq[TestResult])] = Task.Anon {
- // This is a terrible hack, but it works
- run(Task.Anon {
- Args(args() ++ Seq(
+ runJsBinary(
+ // TODO add runner to be able to use test selector
+ args = Args(args() ++ Seq(
// TODO this is valid only for the NodeJS target. Once browser support is
// added, need to have different argument handling
"--require",
sourceMapSupportModule().path.toString(),
- mochaModule().path.toString()
- ))
- })()
- ("", Seq.empty[TestResult])
+ mochaModule().path.toString(),
+ "--reporter",
+ "xunit",
+ "--reporter-option",
+ s"output=${testReportXml().getOrElse(defaultXmlReportName)}"
+ )),
+ binaryKind = Some(BinaryKind.Executable),
+ moduleKind = moduleKind(),
+ binaryDir = linkBinary().classes.path,
+ runTarget = kotlinJSRunTarget(),
+ envArgs = T.env,
+ workingDir = T.dest
+ )
+
+ // we don't care about the result returned above (because node will return exit code = 1 when tests fail), what
+ // matters is if test results file exists
+ val xmlReportName = testReportXml().getOrElse(defaultXmlReportName)
+ val xmlReportPath = T.dest / xmlReportName
+ val testResults = parseTestResults(xmlReportPath)
+ val totalCount = testResults.length
+ val passedCount = testResults.count(_.status == Status.Success.name())
+ val failedCount = testResults.count(_.status == Status.Failure.name())
+ val skippedCount = testResults.count(_.status == Status.Skipped.name())
+ val doneMessage =
+ s"""
+ |Tests: $totalCount, Passed: $passedCount, Failed: $failedCount, Skipped: $skippedCount
+ |
+ |Full report is available at $xmlReportPath
+ |""".stripMargin
+
+ if (failedCount != 0) {
+ val failedTests = testResults
+ .filter(_.status == Status.Failure.name())
+ .map(result =>
+ if (result.exceptionName.isEmpty && result.exceptionMsg.isEmpty) {
+ s"${result.fullyQualifiedName} - ${result.selector}"
+ } else {
+ s"${result.fullyQualifiedName} - ${result.selector}: ${result.exceptionName.getOrElse("<>")}:" +
+ s" ${result.exceptionMsg.getOrElse("<>")}"
+ }
+ )
+ val failureMessage =
+ s"""
+ |Tests failed:
+ |
+ |${failedTests.mkString("\n")}
+ |
+ |""".stripMargin
+ Result.Failure(failureMessage, Some((doneMessage, testResults)))
+ } else {
+ Result.Success((doneMessage, testResults))
+ }
+ }
+
+ private def parseTestResults(path: os.Path): Seq[TestResult] = {
+ if (!os.exists(path)) {
+ throw new FileNotFoundException(s"Test results file $path wasn't found")
+ }
+ val xml = XML.loadFile(path.toIO)
+ (xml \ "testcase")
+ .map { node =>
+ val (testStatus, exceptionName, exceptionMessage, exceptionTrace) =
+ if (node.child.exists(_.label == "failure")) {
+ val content = (node \ "failure")
+ .head
+ .child
+ .filter(_.isAtom)
+ .text
+ val lines = content.split("\n")
+ val exceptionMessage = lines.head
+ val exceptionType = lines(1).splitAt(lines(1).indexOf(":"))._1
+ val trace = parseTrace(lines.drop(2))
+ (Status.Failure, Some(exceptionType), Some(exceptionMessage), Some(trace))
+ } else if (node.child.exists(_.label == "skipped")) {
+ (Status.Skipped, None, None, None)
+ } else {
+ (Status.Success, None, None, None)
+ }
+
+ TestResult(
+ fullyQualifiedName = node \@ "classname",
+ selector = node \@ "name",
+ // probably in milliseconds?
+ duration = ((node \@ "time").toDouble * 1000).toLong,
+ status = testStatus.name(),
+ exceptionName = exceptionName,
+ exceptionMsg = exceptionMessage,
+ exceptionTrace = exceptionTrace
+ )
+ }
}
- override def kotlinJSRunTarget: T[Option[RunTarget]] = Some(RunTarget.Node)
+ private def parseTrace(trace: Seq[String]): Seq[StackTraceElement] = {
+ trace.map { line =>
+ // there are some lines with methods like this: $interceptCOROUTINE$97.l [as test_1], no idea what is this.
+ val strippedLine = line.trim.stripPrefix("at ")
+ val (symbol, location) = strippedLine.splitAt(strippedLine.lastIndexOf("("))
+ // symbol can be like that HelloTests$_init_$lambda$slambda_wolooq_1.protoOf.doResume_5yljmg_k$
+ // assume that everything past first dot is a method name, and everything before - some synthetic class name
+ // this may be completely wrong though, but at least location will be right
+ val (declaringClass, method) = if (symbol.contains(".")) {
+ symbol.splitAt(symbol.indexOf("."))
+ } else {
+ ("", symbol)
+ }
+ // can be what we expect in case if line is pure-JVM:
+ // src/internal/JSDispatcher.kt:127:25
+ // but can also be something like:
+ // node:internal/process/task_queues:77:11
+ // drop closing ), then after split drop position on the line
+ val locationElements = location.dropRight(1).split(":").dropRight(1)
+ if (locationElements.length >= 2) {
+ new StackTraceElement(
+ declaringClass,
+ method,
+ locationElements(locationElements.length - 2),
+ locationElements.last.toInt
+ )
+ } else {
+ new StackTraceElement(declaringClass, method, "", 0)
+ }
+ }
+ }
}
/**
diff --git a/kotlinlib/test/src/mill/kotlinlib/contrib/ktfmt/KtfmtModuleTests.scala b/kotlinlib/test/src/mill/kotlinlib/contrib/ktfmt/KtfmtModuleTests.scala
index 75fbad0890b..b1a8afe2c62 100644
--- a/kotlinlib/test/src/mill/kotlinlib/contrib/ktfmt/KtfmtModuleTests.scala
+++ b/kotlinlib/test/src/mill/kotlinlib/contrib/ktfmt/KtfmtModuleTests.scala
@@ -1,6 +1,6 @@
package mill.kotlinlib.ktfmt
-import mill.{PathRef, T, Task, api}
+import mill.{PathRef, T, api}
import mill.kotlinlib.KotlinModule
import mill.main.Tasks
import mill.testkit.{TestBaseModule, UnitTester}
diff --git a/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSKotestModuleTests.scala b/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSKotestModuleTests.scala
index b9327e55576..77d00778edf 100644
--- a/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSKotestModuleTests.scala
+++ b/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSKotestModuleTests.scala
@@ -1,9 +1,11 @@
package mill
package kotlinlib.js
+import mill.api.Result
import mill.eval.EvaluatorPaths
import mill.testkit.{TestBaseModule, UnitTester}
-import utest.{assert, TestSuite, Tests, test}
+import sbt.testing.Status
+import utest.{TestSuite, Tests, assert, test}
object KotlinJSKotestModuleTests extends TestSuite {
@@ -19,6 +21,7 @@ object KotlinJSKotestModuleTests extends TestSuite {
object foo extends KotlinJSModule {
def kotlinVersion = KotlinJSKotestModuleTests.kotlinVersion
+ override def kotlinJSRunTarget = Some(RunTarget.Node)
override def moduleDeps = Seq(module.bar)
object test extends KotlinJSModule with KotestTests {
@@ -36,19 +39,30 @@ object KotlinJSKotestModuleTests extends TestSuite {
val eval = testEval()
val command = module.foo.test.test()
- val Left(_) = eval.apply(command)
+ val Left(Result.Failure(failureMessage, Some((doneMessage, testResults)))) =
+ eval.apply(command)
+
+ val xmlReport =
+ EvaluatorPaths.resolveDestPaths(eval.outPath, command).dest / "test-report.xml"
- // temporary, because we are running run() task, it won't be test.log, but run.log
- val log =
- os.read(EvaluatorPaths.resolveDestPaths(eval.outPath, command).log / ".." / "run.log")
assert(
- log.contains(
- "AssertionFailedError: expected:<\"Not hello, world\"> but was:<\"Hello, world\">"
- ),
- log.contains("1 passing"),
- log.contains("1 failing"),
- // verify that source map is applied, otherwise all stack entries will point to .js
- log.contains("HelloKotestTests.kt:")
+ os.exists(xmlReport),
+ os.read(xmlReport).contains("HelloKotestTests.kt:"),
+ failureMessage == s"""
+ |Tests failed:
+ |
+ |HelloTests - failure: AssertionFailedError: expected:<\"Not hello, world\"> but was:<\"Hello, world\">
+ |
+ |""".stripMargin,
+ doneMessage == s"""
+ |Tests: 2, Passed: 1, Failed: 1, Skipped: 0
+ |
+ |Full report is available at $xmlReport
+ |""".stripMargin,
+ testResults.length == 2,
+ testResults.count(result =>
+ result.status == Status.Failure.name() && result.exceptionTrace.getOrElse(Seq.empty).isEmpty
+ ) == 0
)
}
}
diff --git a/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSKotlinTestPackageModuleTests.scala b/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSKotlinTestPackageModuleTests.scala
index fdd9b2039a0..84c9295b8f8 100644
--- a/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSKotlinTestPackageModuleTests.scala
+++ b/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSKotlinTestPackageModuleTests.scala
@@ -2,9 +2,11 @@ package mill
package kotlinlib
package js
+import mill.api.Result
import mill.eval.EvaluatorPaths
import mill.testkit.{TestBaseModule, UnitTester}
-import utest.{assert, TestSuite, Tests, test}
+import sbt.testing.Status
+import utest.{TestSuite, Tests, assert, test}
object KotlinJSKotlinTestPackageModuleTests extends TestSuite {
@@ -20,6 +22,7 @@ object KotlinJSKotlinTestPackageModuleTests extends TestSuite {
object foo extends KotlinJSModule {
def kotlinVersion = KotlinJSKotlinTestPackageModuleTests.kotlinVersion
+ override def kotlinJSRunTarget = Some(RunTarget.Node)
override def moduleDeps = Seq(module.bar)
object test extends KotlinJSModule with KotlinTestPackageTests {
@@ -37,17 +40,30 @@ object KotlinJSKotlinTestPackageModuleTests extends TestSuite {
val eval = testEval()
val command = module.foo.test.test()
- val Left(_) = eval.apply(command)
+ val Left(Result.Failure(failureMessage, Some((doneMessage, testResults)))) =
+ eval.apply(command)
+
+ val xmlReport =
+ EvaluatorPaths.resolveDestPaths(eval.outPath, command).dest / "test-report.xml"
- // temporary, because we are running run() task, it won't be test.log, but run.log
- val log =
- os.read(EvaluatorPaths.resolveDestPaths(eval.outPath, command).log / ".." / "run.log")
assert(
- log.contains("AssertionError: Expected , actual ."),
- log.contains("1 passing"),
- log.contains("1 failing"),
- // verify that source map is applied, otherwise all stack entries will point to .js
- log.contains("HelloKotlinTestPackageTests.kt:")
+ os.exists(xmlReport),
+ os.read(xmlReport).contains("HelloKotlinTestPackageTests.kt:"),
+ failureMessage == s"""
+ |Tests failed:
+ |
+ |foo HelloTests - failure: AssertionError: Expected , actual .
+ |
+ |""".stripMargin,
+ doneMessage == s"""
+ |Tests: 2, Passed: 1, Failed: 1, Skipped: 0
+ |
+ |Full report is available at $xmlReport
+ |""".stripMargin,
+ testResults.length == 2,
+ testResults.count(result =>
+ result.status == Status.Failure.name() && result.exceptionTrace.getOrElse(Seq.empty).isEmpty
+ ) == 0
)
}
}
diff --git a/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSLinkTests.scala b/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSLinkTests.scala
index 928a3fcb49e..c0ad36ac29d 100644
--- a/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSLinkTests.scala
+++ b/kotlinlib/test/src/mill/kotlinlib/js/KotlinJSLinkTests.scala
@@ -4,8 +4,6 @@ import mill.testkit.{TestBaseModule, UnitTester}
import mill.{Cross, T}
import utest.{TestSuite, Tests, assert, test}
-import scala.util.Random
-
object KotlinJSLinkTests extends TestSuite {
private val kotlinVersion = "1.9.25"
diff --git a/main/util/src/mill/util/Jvm.scala b/main/util/src/mill/util/Jvm.scala
index aaff3514375..c23aebc1839 100644
--- a/main/util/src/mill/util/Jvm.scala
+++ b/main/util/src/mill/util/Jvm.scala
@@ -204,17 +204,19 @@ object Jvm extends CoursierSupport {
if (backgroundOutputs.nonEmpty)
spawnSubprocessWithBackgroundOutputs(args, envArgs, workingDir, backgroundOutputs)
else
- runSubprocess(args, envArgs, workingDir)
+ runSubprocess(args, envArgs, workingDir).getOrThrow
}
/**
* Runs a generic subprocess and waits for it to terminate.
+ *
+ * @return Result with exit code.
*/
def runSubprocess(
commandArgs: Seq[String],
envArgs: Map[String, String],
workingDir: os.Path
- ): Unit = {
+ ): Result[Int] = {
val process = spawnSubprocessWithBackgroundOutputs(
commandArgs,
envArgs,
@@ -239,8 +241,11 @@ object Jvm extends CoursierSupport {
} finally {
Runtime.getRuntime().removeShutdownHook(shutdownHook)
}
- if (process.exitCode() == 0) ()
- else throw new Exception("Interactive Subprocess Failed (exit code " + process.exitCode() + ")")
+ if (process.exitCode() == 0) Result.Success(process.exitCode())
+ else Result.Failure(
+ "Interactive Subprocess Failed (exit code " + process.exitCode() + ")",
+ Some(process.exitCode())
+ )
}
/**
diff --git a/scalalib/src/mill/scalalib/JavaModule.scala b/scalalib/src/mill/scalalib/JavaModule.scala
index e7da0fb7760..fa831d36320 100644
--- a/scalalib/src/mill/scalalib/JavaModule.scala
+++ b/scalalib/src/mill/scalalib/JavaModule.scala
@@ -759,7 +759,7 @@ trait JavaModule
commandArgs = Seq(Jvm.jdkTool("javadoc")) ++ cmdArgs,
envArgs = Map(),
workingDir = T.dest
- )
+ ).getOrThrow
}
Jvm.createJar(Agg(javadocDir))(outDir)
diff --git a/scalalib/src/mill/scalalib/publish/SonatypeHelpers.scala b/scalalib/src/mill/scalalib/publish/SonatypeHelpers.scala
index a5534b4340d..2b4d1d46edd 100644
--- a/scalalib/src/mill/scalalib/publish/SonatypeHelpers.scala
+++ b/scalalib/src/mill/scalalib/publish/SonatypeHelpers.scala
@@ -54,7 +54,7 @@ object SonatypeHelpers {
val fileName = file.toString
val command = "gpg" +: args :+ fileName
- Jvm.runSubprocess(command, env, workspace)
+ Jvm.runSubprocess(command, env, workspace).getOrThrow
os.Path(fileName + ".asc")
}
diff --git a/scalalib/test/src/mill/scalalib/AssemblyTestUtils.scala b/scalalib/test/src/mill/scalalib/AssemblyTestUtils.scala
index c6f1ecd16f2..dbcd9e8b9c3 100644
--- a/scalalib/test/src/mill/scalalib/AssemblyTestUtils.scala
+++ b/scalalib/test/src/mill/scalalib/AssemblyTestUtils.scala
@@ -54,13 +54,13 @@ trait AssemblyTestUtils {
commandArgs = Seq(Jvm.javaExe, "-jar", file.toString(), "--text", "tutu"),
envArgs = Map.empty[String, String],
workingDir = wd
- )
+ ).getOrThrow
if (checkExe) {
Jvm.runSubprocess(
commandArgs = Seq(file.toString(), "--text", "tutu"),
envArgs = Map.empty[String, String],
workingDir = wd
- )
+ ).getOrThrow
}
}
}
diff --git a/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala b/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala
index 44c18789178..e433cfacf83 100644
--- a/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala
+++ b/scalanativelib/src/mill/scalanativelib/ScalaNativeModule.scala
@@ -278,7 +278,8 @@ trait ScalaNativeModule extends ScalaModule { outer =>
commandArgs = Vector(nativeLink().toString) ++ args().value,
envArgs = forkEnv(),
workingDir = forkWorkingDir()
- )
+ ).getOrThrow
+ ()
}
@internal