Skip to content

Commit

Permalink
Add ShellSession
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudshiftchris committed Apr 5, 2024
1 parent 760c628 commit 4ec4583
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 10 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ Co-routine, DSL-friendly process launching from Kotlin.
}
```

## Shell Session

For executing multiple commands in a single shell session, use the `shellSession` function:
```kotlin
shellSession {
mkdir("foo")
cd("foo")
exec("git", "init")
}
```

## Key Features

* Non-zero exit values are considered a failure by default (configure `failOnNonZeroExit`) and throw an exception;
Expand Down
36 changes: 33 additions & 3 deletions api/kprocess.api
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ public abstract class io/cloudshiftdev/kprocess/KProcessException : java/lang/Ru
}

public final class io/cloudshiftdev/kprocess/KProcessKt {
public static final fun exec (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun execToFile (Ljava/io/File;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun execToList (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun exec ([Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun execToFile (Ljava/io/File;[Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun execToList ([Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract class io/cloudshiftdev/kprocess/OutputConsumer {
Expand Down Expand Up @@ -104,3 +104,33 @@ public final class io/cloudshiftdev/kprocess/ProcessStartException : io/cloudshi
public fun <init> (Ljava/lang/String;)V
}

public abstract interface class io/cloudshiftdev/kprocess/session/ShellSession {
public abstract fun cd (Ljava/io/File;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun cd (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun changeDirectory (Ljava/io/File;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun chmod ([Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun currentDirectory (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun cwd (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun exec ([Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun mkdir ([Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public abstract fun pwd (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class io/cloudshiftdev/kprocess/session/ShellSession$DefaultImpls {
public static fun cd (Lio/cloudshiftdev/kprocess/session/ShellSession;Ljava/io/File;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static fun cd (Lio/cloudshiftdev/kprocess/session/ShellSession;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static fun cwd (Lio/cloudshiftdev/kprocess/session/ShellSession;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun exec$default (Lio/cloudshiftdev/kprocess/session/ShellSession;[Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static fun pwd (Lio/cloudshiftdev/kprocess/session/ShellSession;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class io/cloudshiftdev/kprocess/session/ShellSessionKt {
public static final fun shellSession (Ljava/io/File;Ljava/util/Map;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun shellSession$default (Ljava/io/File;Ljava/util/Map;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public final class io/cloudshiftdev/kprocess/session/ZipKt {
public static final fun unzip (Lio/cloudshiftdev/kprocess/session/ShellSession;[Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun zip (Lio/cloudshiftdev/kprocess/session/ShellSession;[Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version=0.17.1
version=0.18.0
group=io.cloudshiftdev.kprocess

org.gradle.vfs.watch=true
Expand Down
27 changes: 21 additions & 6 deletions src/main/kotlin/io/cloudshiftdev/kprocess/KProcess.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,32 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext

public suspend fun <O> exec(spec: (ExecSpec<O>.() -> Unit)): ExecResult<O> =
execute(ExecSpecImpl<O>().apply(spec))
public suspend fun <O> exec(
vararg commandAndArgs: String,
spec: (ExecSpec<O>.() -> Unit)
): ExecResult<O> =
ExecSpecImpl<O>()
.apply {
commandLine(commandAndArgs.toList())
spec()
}
.let { execute(it) }

public suspend fun execToList(spec: (ExecSpec<List<String>>.() -> Unit)): ExecResult<List<String>> =
exec {
public suspend fun execToList(
vararg commandAndArgs: String,
spec: (ExecSpec<List<String>>.() -> Unit)
): ExecResult<List<String>> =
exec(*commandAndArgs) {
apply(spec)
outputConsumer(OutputConsumer.lines { it.toList() })
}

public suspend fun execToFile(file: File, spec: (ExecSpec<Unit>.() -> Unit)): ExecResult<Unit> =
exec {
public suspend fun execToFile(
file: File,
vararg commandAndArgs: String,
spec: (ExecSpec<Unit>.() -> Unit)
): ExecResult<Unit> =
exec(*commandAndArgs) {
apply(spec)
outputConsumer(OutputConsumer.file(file))
}
Expand Down
70 changes: 70 additions & 0 deletions src/main/kotlin/io/cloudshiftdev/kprocess/session/ShellSession.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package io.cloudshiftdev.kprocess.session

import io.cloudshiftdev.kprocess.ExecResult
import io.cloudshiftdev.kprocess.ExecSpec
import java.io.File

public interface ShellSession {
public suspend fun changeDirectory(dir: File)

public suspend fun cd(dir: File): Unit = changeDirectory(dir)

public suspend fun cd(dir: String): Unit = changeDirectory(File(dir))

public suspend fun currentDirectory(): File

public suspend fun cwd(): File = currentDirectory()

public suspend fun pwd(): File = currentDirectory()

public suspend fun mkdir(vararg args: String)

public suspend fun chmod(vararg args: String)

public suspend fun <O> exec(
vararg commandAndArgs: String,
spec: (ExecSpec<O>.() -> Unit) = {}
): ExecResult<O>
}

public suspend fun shellSession(
workingDir: File? = null,
environment: Map<String, String> = emptyMap(),
session: suspend ShellSession.() -> Unit
) {
ShellSessionImpl(workingDir, environment).session()
}

private class ShellSessionImpl(initialWorkingDir: File?, environment: Map<String, String>) :
ShellSession {
private var workingDir: File = initialWorkingDir ?: File(System.getProperty("user.dir"))
private val environment: MutableMap<String, String> = environment.toMutableMap()

override suspend fun changeDirectory(dir: File) {
workingDir = resolveDir(dir)
}

override suspend fun currentDirectory(): File = workingDir

override suspend fun mkdir(vararg args: String) {
exec<Unit>("mkdir", *args) { failOnNonZeroExit(false) }
}

override suspend fun chmod(vararg args: String) {
exec<Unit>("chmod", *args)
}

override suspend fun <O> exec(
vararg commandAndArgs: String,
spec: ExecSpec<O>.() -> Unit
): ExecResult<O> {
return io.cloudshiftdev.kprocess.exec(*commandAndArgs) {
workingDir(workingDir)
environment(environment)
apply(spec)
}
}

private fun resolveDir(dir: File): File =
if (dir.isAbsolute) dir else workingDir.resolve(dir).normalize()
}
9 changes: 9 additions & 0 deletions src/main/kotlin/io/cloudshiftdev/kprocess/session/Zip.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.cloudshiftdev.kprocess.session

public suspend fun ShellSession.zip(vararg args: String) {
exec<Unit>("zip", *args)
}

public suspend fun ShellSession.unzip(vararg args: String) {
exec<Unit>("unzip", *args)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.cloudshiftdev.kprocess.session

import io.kotest.core.spec.style.FunSpec
import io.kotest.engine.spec.tempdir
import org.junit.jupiter.api.Assertions.*

class ShellSessionImplTest : FunSpec() {
init {
test("shell session works") {
val tmpDir = tempdir()
shellSession(workingDir = tmpDir) {
mkdir("test")
cd("test")
val dir = pwd()
assertEquals(tmpDir.resolve("test"), dir)
}
}
}
}

0 comments on commit 4ec4583

Please sign in to comment.