From a64703cf2f0e72392c4f89a7285ed7af55b7c205 Mon Sep 17 00:00:00 2001 From: Infendro Date: Sat, 13 Dec 2025 01:51:47 +0100 Subject: [PATCH 1/6] use common plugin --- build.gradle.kts | 41 ++++++++++----------------------------- gradle/libs.versions.toml | 6 ++++-- settings.gradle.kts | 5 +++++ 3 files changed, 19 insertions(+), 33 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 17c2c1b..7656cd8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,11 +1,11 @@ +import com.infendro.plugin.infendro +import com.infendro.plugin.token + group = "com.infendro" version = "1.0.0" -repositories { - mavenCentral() -} - plugins { + alias(libs.plugins.infendro) alias(libs.plugins.multiplatform) id("maven-publish") } @@ -17,6 +17,10 @@ kotlin { } linuxX64() + repositories { + mavenCentral() + } + sourceSets { jsMain.dependencies { implementation(libs.node) @@ -24,31 +28,6 @@ kotlin { } } -publishing { - repositories { - maven { - url = uri("https://git.infendro.com/api/packages/Infendro/maven") - authentication.create("header", HttpHeaderAuthentication::class) - credentials(HttpHeaderCredentials::class) { - val token = secret("git.token") ?: throw GradleException() - - name = "Authorization" - value = "token $token" - } - } - } -} - -fun secret( - key: String, -): String? { - val property = key - val environment = key - .uppercase() - .replace(".", "__") - - val prop = properties[property] as String? - val env = System.getenv(environment) - - return prop ?: env +publishing.repositories { + infendro(token) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6e75867..66207cb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,10 @@ [versions] -kotlin = "2.1.20" -node = "2025.6.9-22.13.10" +infendro = "1.0.0" +kotlin = "2.2.21" +node = "2025.12.5-24.7.1" [plugins] +infendro = { id = "com.infendro.plugin", version.ref = "infendro" } multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } [libraries] diff --git a/settings.gradle.kts b/settings.gradle.kts index f4f9dde..4d0e67e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1 +1,6 @@ rootProject.name = "shell" + +pluginManagement.repositories { + maven("https://git.infendro.com/api/packages/Infendro/maven") + mavenCentral() +} -- 2.49.1 From 9336409590820d364510362ac85fd40f6bb9d2a6 Mon Sep 17 00:00:00 2001 From: Infendro Date: Sat, 13 Dec 2025 01:54:35 +0100 Subject: [PATCH 2/6] fix workflow --- .gitea/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitea/workflows/main.yaml b/.gitea/workflows/main.yaml index dd78fce..4885477 100644 --- a/.gitea/workflows/main.yaml +++ b/.gitea/workflows/main.yaml @@ -4,7 +4,7 @@ on: - main env: - GIT__TOKEN: ${{ secrets.TOKEN }} + INFENDRO__TOKEN: ${{ secrets.TOKEN }} jobs: publish: -- 2.49.1 From c1cbd9655883d919e9810a7818b36f28dc6e77ae Mon Sep 17 00:00:00 2001 From: Infendro Date: Sun, 14 Dec 2025 00:07:46 +0100 Subject: [PATCH 3/6] small refactor --- build.gradle.kts | 4 ++++ src/jsMain/kotlin/com/infendro/shell/Shell.js.kt | 6 +++--- src/jvmMain/kotlin/com/infendro/shell/Shell.jvm.kt | 14 +++++--------- .../kotlin/com/infendro/shell/Shell.native.kt | 3 --- 4 files changed, 12 insertions(+), 15 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7656cd8..a035e3e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -26,6 +26,10 @@ kotlin { implementation(libs.node) } } + + compilerOptions { + freeCompilerArgs.add("-opt-in=kotlinx.cinterop.ExperimentalForeignApi") + } } publishing.repositories { diff --git a/src/jsMain/kotlin/com/infendro/shell/Shell.js.kt b/src/jsMain/kotlin/com/infendro/shell/Shell.js.kt index 290477b..d5b7826 100644 --- a/src/jsMain/kotlin/com/infendro/shell/Shell.js.kt +++ b/src/jsMain/kotlin/com/infendro/shell/Shell.js.kt @@ -1,9 +1,9 @@ package com.infendro.shell -import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine import node.childProcess.ExecOptions import node.childProcess.exec +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine internal actual suspend fun execute( command: Array, @@ -13,7 +13,7 @@ internal actual suspend fun execute( val options = ExecOptions.invoke(cwd = directory) exec(command, options) { error, stdout, stderr -> val code = if (error == null) 0 else error.code!!.toInt() - val value = if (error == null) stdout else stderr + val value = (if (error == null) stdout else stderr) as String continuation.resume(Shell.Result(code, value)) } } diff --git a/src/jvmMain/kotlin/com/infendro/shell/Shell.jvm.kt b/src/jvmMain/kotlin/com/infendro/shell/Shell.jvm.kt index c713ebc..e47b3d6 100644 --- a/src/jvmMain/kotlin/com/infendro/shell/Shell.jvm.kt +++ b/src/jvmMain/kotlin/com/infendro/shell/Shell.jvm.kt @@ -6,16 +6,12 @@ internal actual suspend fun execute( command: Array, directory: String?, ): Shell.Result { - val process = ProcessBuilder(*command) - .also { - if (directory != null) { - it.directory(File(directory)) - } + val process = ProcessBuilder(*command).also { + if (directory != null) { + it.directory(File(directory)) } - .start() + }.start() val code = process.waitFor() - val value = process - .let { if (code == 0) it.inputReader() else it.errorReader() } - .readText() + val value = (if (code == 0) process.inputReader() else process.errorReader()).readText() return Shell.Result(code, value) } diff --git a/src/nativeMain/kotlin/com/infendro/shell/Shell.native.kt b/src/nativeMain/kotlin/com/infendro/shell/Shell.native.kt index 315cf82..c3371d3 100644 --- a/src/nativeMain/kotlin/com/infendro/shell/Shell.native.kt +++ b/src/nativeMain/kotlin/com/infendro/shell/Shell.native.kt @@ -1,12 +1,10 @@ package com.infendro.shell import kotlinx.cinterop.CPointer -import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.refTo import kotlinx.cinterop.toKString import platform.posix.* -@ExperimentalForeignApi internal actual suspend fun execute( command: Array, directory: String?, @@ -21,7 +19,6 @@ internal actual suspend fun execute( return Shell.Result(code, value) } -@ExperimentalForeignApi private fun CPointer.readLines(): List { val lines = mutableListOf() val buffer = ByteArray(4096) -- 2.49.1 From fd54489747dadbfd077b8421d9128ee5a6089ae3 Mon Sep 17 00:00:00 2001 From: Infendro Date: Sun, 14 Dec 2025 00:08:23 +0100 Subject: [PATCH 4/6] update README --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5342a15..08719f5 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,15 @@ -# Kotlin Shell +# shell-kt -This library enables the execution of shell commands in Kotlin Multiplatform. +`shell-kt` is a Kotlin Multiplatform library that enables the execution of shell commands. -## Features +## Targets -* Execute commands on the shell. -* Multiplatform support - * JVM - * JavaScript - * Native (Linux) +| Target | Status | +|------------------|---------------| +| JVM | Supported | +| JavaScript | Supported | +| Native (Linux) | Supported | +| Native (Windows) | Not Supported | ## Installation @@ -29,7 +30,7 @@ dependencies { ```kotlin import com.infendro.shell.Shell -fun main() { +suspend fun main() { // the command to execute val command = arrayOf("echo", "Hello World!") -- 2.49.1 From c961342462a37faa7c6066fe18c59161fa87ef24 Mon Sep 17 00:00:00 2001 From: Infendro Date: Tue, 13 Jan 2026 18:01:22 +0100 Subject: [PATCH 5/6] improve ci --- .gitea/workflows/ci.yaml | 47 ++++++++++++++++++++++++++++++++++++++ .gitea/workflows/main.yaml | 36 ----------------------------- build.gradle.kts | 2 +- 3 files changed, 48 insertions(+), 37 deletions(-) create mode 100644 .gitea/workflows/ci.yaml delete mode 100644 .gitea/workflows/main.yaml diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml new file mode 100644 index 0000000..a63d994 --- /dev/null +++ b/.gitea/workflows/ci.yaml @@ -0,0 +1,47 @@ +on: + push: + branches: [ main ] + +jobs: + test: + runs-on: linux + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Java + uses: actions/setup-java@v4 + with: + java-version: '24' + distribution: 'temurin' + + - name: Cache Gradle + uses: actions/cache@v4 + with: + key: gradle-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: gradle- + path: | + ~/.gradle/caches/ + ~/.gradle/wrapper/ + + - name: Cache Konan + uses: actions/cache@v4 + with: + key: konan-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: konan- + path: | + ~/.konan/ + + - name: Cache Node + uses: actions/cache@v4 + with: + key: node-${{ hashFiles('**/libs.versions.toml') }} + restore-keys: node- + path: | + ~/.gradle/nodejs + ~/.gradle/yarn + + - name: Publish + run: ./gradlew publish + env: + INFENDRO__TOKEN: ${{ secrets.TOKEN }} diff --git a/.gitea/workflows/main.yaml b/.gitea/workflows/main.yaml deleted file mode 100644 index 4885477..0000000 --- a/.gitea/workflows/main.yaml +++ /dev/null @@ -1,36 +0,0 @@ -on: - push: - branches: - - main - -env: - INFENDRO__TOKEN: ${{ secrets.TOKEN }} - -jobs: - publish: - runs-on: linux - steps: - - uses: actions/checkout@v4 - - - uses: actions/cache/restore@v4 - with: - key: gradle - path: | - ~/.gradle/caches/ - ~/.gradle/wrapper/ - ~/.konan/ - - - uses: actions/setup-java@v4 - with: - java-version: '24' - distribution: 'temurin' - - - run: ./gradlew publish - - - uses: actions/cache/save@v4 - with: - key: gradle - path: | - ~/.gradle/caches/ - ~/.gradle/wrapper/ - ~/.konan/ diff --git a/build.gradle.kts b/build.gradle.kts index a035e3e..5e42033 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -12,10 +12,10 @@ plugins { kotlin { jvm() + linuxX64() js { nodejs() } - linuxX64() repositories { mavenCentral() -- 2.49.1 From 2679f688d52f65c092a591f79512d1c0dc10d645 Mon Sep 17 00:00:00 2001 From: Infendro Date: Sun, 15 Feb 2026 14:06:35 +0100 Subject: [PATCH 6/6] split ci --- .gitea/workflows/{ci.yaml => publish.yaml} | 8 ++--- README.md | 19 ++---------- build.gradle.kts | 10 ++++--- gradle/libs.versions.toml | 6 ++-- .../kotlin/com/infendro/shell/Shell.kt | 30 +++++++++---------- .../kotlin/com/infendro/shell/Shell.js.kt | 16 +++++----- .../kotlin/com/infendro/shell/Shell.jvm.kt | 15 +++++----- .../kotlin/com/infendro/shell/Shell.native.kt | 16 +++++----- 8 files changed, 53 insertions(+), 67 deletions(-) rename .gitea/workflows/{ci.yaml => publish.yaml} (90%) diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/publish.yaml similarity index 90% rename from .gitea/workflows/ci.yaml rename to .gitea/workflows/publish.yaml index a63d994..dad1306 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/publish.yaml @@ -3,7 +3,7 @@ on: branches: [ main ] jobs: - test: + publish: runs-on: linux steps: - name: Checkout @@ -19,7 +19,7 @@ jobs: uses: actions/cache@v4 with: key: gradle-${{ hashFiles('**/libs.versions.toml') }} - restore-keys: gradle- + restore-keys: gradle path: | ~/.gradle/caches/ ~/.gradle/wrapper/ @@ -28,7 +28,7 @@ jobs: uses: actions/cache@v4 with: key: konan-${{ hashFiles('**/libs.versions.toml') }} - restore-keys: konan- + restore-keys: konan path: | ~/.konan/ @@ -36,7 +36,7 @@ jobs: uses: actions/cache@v4 with: key: node-${{ hashFiles('**/libs.versions.toml') }} - restore-keys: node- + restore-keys: node path: | ~/.gradle/nodejs ~/.gradle/yarn diff --git a/README.md b/README.md index 08719f5..72154c0 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,6 @@ `shell-kt` is a Kotlin Multiplatform library that enables the execution of shell commands. -## Targets - -| Target | Status | -|------------------|---------------| -| JVM | Supported | -| JavaScript | Supported | -| Native (Linux) | Supported | -| Native (Windows) | Not Supported | - ## Installation Add the following to your `build.gradle.kts`. @@ -21,21 +12,15 @@ repositories { } dependencies { - implementation("com.infendro:shell:1.0.0") + implementation("com.infendro:shell:1.1.0") } ``` ## Usage ```kotlin -import com.infendro.shell.Shell - suspend fun main() { - // the command to execute - val command = arrayOf("echo", "Hello World!") - - // execute the command - val result = Shell.run(command) + val result = execute("echo", "Hello World!") println(result.code) // 0 println(result.value) // Hello World! diff --git a/build.gradle.kts b/build.gradle.kts index 5e42033..ce92cbb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ import com.infendro.plugin.infendro import com.infendro.plugin.token group = "com.infendro" -version = "1.0.0" +version = "1.1.0" plugins { alias(libs.plugins.infendro) @@ -13,9 +13,11 @@ plugins { kotlin { jvm() linuxX64() - js { - nodejs() - } + linuxArm64() + mingwX64() + macosX64() + macosArm64() + js { nodejs() } repositories { mavenCentral() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 66207cb..717ea07 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -infendro = "1.0.0" -kotlin = "2.2.21" -node = "2025.12.5-24.7.1" +infendro = "1.1.0" +kotlin = "2.3.0" +node = "2026.2.11-24.10.4" [plugins] infendro = { id = "com.infendro.plugin", version.ref = "infendro" } diff --git a/src/commonMain/kotlin/com/infendro/shell/Shell.kt b/src/commonMain/kotlin/com/infendro/shell/Shell.kt index 5c5badf..6c99885 100644 --- a/src/commonMain/kotlin/com/infendro/shell/Shell.kt +++ b/src/commonMain/kotlin/com/infendro/shell/Shell.kt @@ -1,20 +1,20 @@ package com.infendro.shell -object Shell { - suspend fun run( - command: Array, - directory: String? = null, - ): Result { - return execute(command, directory) - } +data class Result( + val code: Int, + val value: String, +) { + val success: Boolean + get() = code == 0 - class Result( - val code: Int, - val value: String, - ) + internal companion object { + fun from(code: Int, value: String) = Result(code, value) + + fun from(code: Int, success: () -> String, error: () -> String) = when (code) { + 0 -> Result(code, success()) + else -> Result(code, error()) + } + } } -internal expect suspend fun execute( - command: Array, - directory: String?, -): Shell.Result +expect suspend fun execute(vararg command: String, directory: String? = null): Result diff --git a/src/jsMain/kotlin/com/infendro/shell/Shell.js.kt b/src/jsMain/kotlin/com/infendro/shell/Shell.js.kt index d5b7826..9ad0fc9 100644 --- a/src/jsMain/kotlin/com/infendro/shell/Shell.js.kt +++ b/src/jsMain/kotlin/com/infendro/shell/Shell.js.kt @@ -5,15 +5,13 @@ import node.childProcess.exec import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -internal actual suspend fun execute( - command: Array, - directory: String?, -): Shell.Result = suspendCoroutine { continuation -> - val command = command.joinToString(" ") { "\"$it\"" } - val options = ExecOptions.invoke(cwd = directory) +actual suspend fun execute(vararg command: String, directory: String?): Result = suspendCoroutine { continuation -> + val command = command.joinToString(" ") { """"$it"""" } + val options = ExecOptions(cwd = directory) exec(command, options) { error, stdout, stderr -> - val code = if (error == null) 0 else error.code!!.toInt() - val value = (if (error == null) stdout else stderr) as String - continuation.resume(Shell.Result(code, value)) + val code = error?.code?.toInt() ?: 0 + + val result = Result.from(code, { stdout as String }, { stderr as String }) + continuation.resume(result) } } diff --git a/src/jvmMain/kotlin/com/infendro/shell/Shell.jvm.kt b/src/jvmMain/kotlin/com/infendro/shell/Shell.jvm.kt index e47b3d6..d74f7de 100644 --- a/src/jvmMain/kotlin/com/infendro/shell/Shell.jvm.kt +++ b/src/jvmMain/kotlin/com/infendro/shell/Shell.jvm.kt @@ -1,17 +1,18 @@ package com.infendro.shell import java.io.File +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine -internal actual suspend fun execute( - command: Array, - directory: String?, -): Shell.Result { +actual suspend fun execute(vararg command: String, directory: String?): Result = suspendCoroutine { continuation -> val process = ProcessBuilder(*command).also { if (directory != null) { - it.directory(File(directory)) + val dir = File(directory) + it.directory(dir) } }.start() val code = process.waitFor() - val value = (if (code == 0) process.inputReader() else process.errorReader()).readText() - return Shell.Result(code, value) + + val result = Result.from(code, { process.inputReader().readText() }, { process.errorReader().readText() }) + continuation.resume(result) } diff --git a/src/nativeMain/kotlin/com/infendro/shell/Shell.native.kt b/src/nativeMain/kotlin/com/infendro/shell/Shell.native.kt index c3371d3..f05bb42 100644 --- a/src/nativeMain/kotlin/com/infendro/shell/Shell.native.kt +++ b/src/nativeMain/kotlin/com/infendro/shell/Shell.native.kt @@ -4,27 +4,27 @@ import kotlinx.cinterop.CPointer import kotlinx.cinterop.refTo import kotlinx.cinterop.toKString import platform.posix.* +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine -internal actual suspend fun execute( - command: Array, - directory: String?, -): Shell.Result { - val command = command.joinToString(" ") { "\"$it\"" } +actual suspend fun execute(vararg command: String, directory: String?): Result = suspendCoroutine { continuation -> + val command = command.joinToString(" ") { """"$it"""" } if (directory != null) { chdir(directory) } val stream = popen(command, "r")!! val value = stream.readLines().joinToString("") val code = pclose(stream) - return Shell.Result(code, value) + + val result = Result.from(code, value) + continuation.resume(result) } private fun CPointer.readLines(): List { val lines = mutableListOf() val buffer = ByteArray(4096) while (true) { - val line = fgets(buffer.refTo(0), buffer.size, this) - if (line == null) break + val line = fgets(buffer.refTo(0), buffer.size, this) ?: break lines.add(line.toKString()) } return lines -- 2.49.1