diff --git a/lib-publishing-helpers/src/main/kotlin/lib_publisher_tools/clipboard/Clipboard.kt b/lib-publishing-helpers/src/main/kotlin/lib_publisher_tools/clipboard/Clipboard.kt new file mode 100644 index 0000000..b703386 --- /dev/null +++ b/lib-publishing-helpers/src/main/kotlin/lib_publisher_tools/clipboard/Clipboard.kt @@ -0,0 +1,24 @@ +package lib_publisher_tools.clipboard + +import lib_publisher_tools.process.execute + +fun String.copyToClipboard() { + val escapedText = this.escape(charsToEscape = "\"`$") + + val osName = System.getProperty("os.name").lowercase() + val isMacOs: Boolean = "mac" in osName + val isWindows: Boolean = "win" in osName + when { + isMacOs -> listOf("sh", "-c", "echo \"$escapedText\" | pbcopy") + isWindows -> listOf("cmd", "/C", "echo \"$escapedText\" | clip") //TODO: Test that + else -> listOf("sh", "-c", "echo \"$escapedText\" | xclip -sel clip") //TODO: Test that + }.execute() +} + +private fun String.escape(charsToEscape: String): String = buildString { + append(this@escape) + for (i in lastIndex downTo 0) { + val c = this[i] + if (c in charsToEscape) insert(i, '\\') + } +} diff --git a/lib-publishing-helpers/src/main/kotlin/lib_publisher_tools/process/RunCommand.kt b/lib-publishing-helpers/src/main/kotlin/lib_publisher_tools/process/RunCommand.kt index 20e34ed..1bb7df0 100644 --- a/lib-publishing-helpers/src/main/kotlin/lib_publisher_tools/process/RunCommand.kt +++ b/lib-publishing-helpers/src/main/kotlin/lib_publisher_tools/process/RunCommand.kt @@ -13,11 +13,56 @@ class NonZeroExitCodeException(val value: Int) : Exception("Non zero exit value: fun String.execute( workingDir: File = executionDir, timeout: Duration = 60.minutes -): String { - val proc = processBuilder( - rawCommand = this, - workingDir = workingDir - ).start() +): String = processBuilder( + rawCommand = this, + workingDir = workingDir +).execute(timeout) + +fun String.executeAndPrint( + workingDir: File = executionDir, + timeout: Duration = 60.minutes +) { + processBuilder(rawCommand = this, workingDir = workingDir).executeAndPrint(timeout) +} + +fun List.execute( + workingDir: File = executionDir, + timeout: Duration = 60.minutes +): String = processBuilder( + command = this, + workingDir = workingDir +).execute(timeout) + +fun List.executeAndPrint( + workingDir: File = executionDir, + timeout: Duration = 60.minutes +) { + processBuilder(command = this, workingDir = workingDir).executeAndPrint(timeout) +} + +private val rawCommandPattern = Pattern.compile("\"([^\"]*)\"|(\\S+)") + +private fun decodeRawCommand(rawCommand: String): List { + return rawCommandPattern.matcher(rawCommand).let { m -> + generateSequence { + when { + m.find() -> if (m.group(1) != null) m.group(1) else m.group(2) + else -> null + } + } + }.toList() +} + +private fun processBuilder( + rawCommand: String, + workingDir: File = executionDir +): ProcessBuilder = processBuilder( + command = decodeRawCommand(rawCommand), + workingDir = workingDir +) + +private fun ProcessBuilder.execute(timeout: Duration): String { + val proc = start() proc.waitFor(timeout.inWholeMilliseconds, TimeUnit.MILLISECONDS) return proc.inputStream.use { it.bufferedReader().readText() }.also { val exitValue = proc.exitValue() @@ -27,12 +72,8 @@ fun String.execute( } } -fun String.executeAndPrint( - workingDir: File = executionDir, - timeout: Duration = 60.minutes -) { - val proc = processBuilder(rawCommand = this, workingDir = workingDir) - .redirectInput(ProcessBuilder.Redirect.INHERIT) +private fun ProcessBuilder.executeAndPrint(timeout: Duration) { + val proc = redirectInput(ProcessBuilder.Redirect.INHERIT) .redirectOutput(ProcessBuilder.Redirect.INHERIT) .redirectError(ProcessBuilder.Redirect.INHERIT) .start() @@ -43,19 +84,10 @@ fun String.executeAndPrint( } } -private val rawCommandPattern = Pattern.compile("\"([^\"]*)\"|(\\S+)") - -private fun processBuilder(rawCommand: String, workingDir: File = executionDir): ProcessBuilder { - val command = rawCommandPattern.matcher(rawCommand).let { m -> - generateSequence { - when { - m.find() -> if (m.group(1) != null) m.group(1) else m.group(2) - else -> null - } - } - }.toList() - return ProcessBuilder(command) - .directory(workingDir) - .redirectOutput(ProcessBuilder.Redirect.PIPE) - .redirectError(ProcessBuilder.Redirect.PIPE) -} +private fun processBuilder( + command: List, + workingDir: File = executionDir +): ProcessBuilder = ProcessBuilder(command) + .directory(workingDir) + .redirectOutput(ProcessBuilder.Redirect.PIPE) + .redirectError(ProcessBuilder.Redirect.PIPE)