mirror of
https://github.com/slhaf/Partner.git
synced 2026-05-12 08:43:02 +08:00
feat(partnerctl): add command execution helpers with captured and inherited IO
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
package work.slhaf.partner.ctl.support
|
||||
|
||||
import java.io.InputStream
|
||||
import java.nio.charset.Charset
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Result of a completed external command.
|
||||
*/
|
||||
data class CommandResult(
|
||||
val command: List<String>,
|
||||
val exitCode: Int,
|
||||
val stdout: String,
|
||||
val stderr: String,
|
||||
) {
|
||||
val success: Boolean get() = exitCode == 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a command and captures stdout/stderr separately.
|
||||
*/
|
||||
fun runCommand(
|
||||
command: List<String>,
|
||||
workingDirectory: Path? = null,
|
||||
environment: Map<String, String> = emptyMap(),
|
||||
timeoutSeconds: Long? = null,
|
||||
charset: Charset = StandardCharsets.UTF_8,
|
||||
): CommandResult {
|
||||
require(command.isNotEmpty()) { "command must not be empty" }
|
||||
|
||||
val process = ProcessBuilder(command)
|
||||
.apply {
|
||||
if (workingDirectory != null) directory(workingDirectory.toFile())
|
||||
environment().putAll(environment)
|
||||
}
|
||||
.start()
|
||||
|
||||
val executor = Executors.newFixedThreadPool(2)
|
||||
try {
|
||||
val stdout = executor.submit<String> { process.inputStream.readText(charset) }
|
||||
val stderr = executor.submit<String> { process.errorStream.readText(charset) }
|
||||
|
||||
val finished = if (timeoutSeconds == null) {
|
||||
process.waitFor()
|
||||
true
|
||||
} else {
|
||||
process.waitFor(timeoutSeconds, TimeUnit.SECONDS)
|
||||
}
|
||||
|
||||
if (!finished) {
|
||||
process.destroyForcibly()
|
||||
process.waitFor()
|
||||
}
|
||||
|
||||
return CommandResult(
|
||||
command = command,
|
||||
exitCode = if (finished) process.exitValue() else -1,
|
||||
stdout = stdout.get(),
|
||||
stderr = stderr.get(),
|
||||
)
|
||||
} finally {
|
||||
executor.shutdownNow()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a command with stdout/stderr/stdin inherited from current process.
|
||||
*/
|
||||
fun inheritCommand(
|
||||
command: List<String>,
|
||||
workingDirectory: Path? = null,
|
||||
environment: Map<String, String> = emptyMap(),
|
||||
): Int {
|
||||
require(command.isNotEmpty()) { "command must not be empty" }
|
||||
|
||||
val process = ProcessBuilder(command)
|
||||
.apply {
|
||||
if (workingDirectory != null) directory(workingDirectory.toFile())
|
||||
environment().putAll(environment)
|
||||
inheritIO()
|
||||
}
|
||||
.start()
|
||||
|
||||
return process.waitFor()
|
||||
}
|
||||
|
||||
private fun InputStream.readText(charset: Charset): String {
|
||||
return bufferedReader(charset).use { it.readText() }
|
||||
}
|
||||
Reference in New Issue
Block a user