Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execute commands in a Bash shell #8

Merged
merged 26 commits into from
Dec 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0ccd1ee
Add ShellCommand
phatblat Dec 12, 2017
8897038
Ignore only gradle build dirs
phatblat Dec 13, 2017
40cc118
Add GradleExec interface
phatblat Dec 13, 2017
6d8f021
Update Spek run config
phatblat Dec 13, 2017
0dbf79c
Add gradle run configs
phatblat Dec 13, 2017
8f898fb
Remove inheritance from Exec
phatblat Dec 14, 2017
3b263a3
Apply java plugin to example project
phatblat Dec 14, 2017
b8d600c
Wire up shell command in task action
phatblat Dec 14, 2017
34eb1ab
Make executable, args and command properties pass through to commandLine
phatblat Dec 16, 2017
4ef706b
Remove workingDir null check
phatblat Dec 16, 2017
e9680ca
Fix Spec run config
phatblat Dec 16, 2017
7958e73
Add LogOutputStream
phatblat Dec 17, 2017
7faf657
Pass output streams through to shell command
phatblat Dec 17, 2017
a5dae34
Add settings file to buildSrc project
Dec 23, 2017
bf2c80d
Merge branch 'master' into shellcmd
Dec 23, 2017
75c26ec
Add class doc comments
Dec 24, 2017
22360cb
Remove unnecessary plugin apply
Dec 24, 2017
8426387
Make systemPath private
Dec 24, 2017
2c2a638
Fix output and remove Exec compatibility
Dec 24, 2017
f3e4903
Merge branch 'master' into shellcmd
Dec 24, 2017
4976630
Ignore .idea/markdown-exported-files.xml
phatblat Dec 24, 2017
8b23ca8
Add more example tasks
Dec 24, 2017
de32536
Throw exception when command is empty string
Dec 24, 2017
31b00b7
Change errorOutput to use error log level
Dec 24, 2017
ac7e67a
Add group to tasks
phatblat Dec 24, 2017
a24c6e0
Check off feature on readme
phatblat Dec 24, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
.idea/inspectionProfiles/
.idea/kotlinc.xml
.idea/libraries/
.idea/markdown-exported-files.xml
.idea/markdown-navigator.xml
.idea/markdown-navigator/
.idea/misc.xml
Expand Down
21 changes: 21 additions & 0 deletions .idea/runConfigurations/SimpleExec__build_.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions .idea/runConfigurations/SimpleExec__test_.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions .idea/runConfigurations/Spek_Tests.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ A simpler extension point than `Exec` for ad-hoc Gradle tasks that run shell com

- [x] Specify entire command line in one string (instead of `List<CharSequence>`).
- [x] Append/prepend to the current `PATH`.
- [ ] Execute a command pipe or conditional command chain.
- [X] Execute a command pipe or conditional command chain.
- [ ] Easy access to `stdout` and `stderr`.

## Example `build.gradle`
Expand Down
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ repositories {
dependencies {
compile(gradleKotlinDsl())
compile(kotlin("stdlib", kotlinVersion))
compile("org.apache.commons:commons-exec:1.3")

// Speck
compile("org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion")
Expand Down
1 change: 1 addition & 0 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ buildscript {
}

apply plugin: 'kotlin'
apply plugin: 'java'

dependencies {
compile gradleApi()
Expand Down
Empty file added buildSrc/settings.gradle
Empty file.
39 changes: 29 additions & 10 deletions example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,45 @@
*/

buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'at.phatbl:simple-exec:+'
}
repositories.jcenter()
dependencies.classpath 'at.phatbl:simple-exec:+'
}

apply plugin: 'simple-exec'

import at.phatbl.simple_exec.SimpleExec

task helloWorld(type: SimpleExec) {
group "Example"
command "echo Hello World!"
}

task successfulCommand(type: SimpleExec) {
group "Example"
command "true"
}

task failingCommand(type: SimpleExec) {
group "Example"
command "false"
}

task emptyCommand(type: SimpleExec) {
group "Example"
command ""
}

task errorLogging(type: SimpleExec) {
group "Example"
command "cat missingfile"
}

task customPath(type: SimpleExec) {
group "Example"
prePath = "/Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home/bin"
prePath = "/Library/Java/JavaVirtualMachines/jdk-9.0.1.jdk/Contents/Home/bin"
postPath = ""
command "java -version"
command 'env | grep PATH= && java -version'
}

task lolCowFortune(type: SimpleExec) {
group "Example"
command "fortune | cowsay | lolcat"
}
102 changes: 102 additions & 0 deletions src/main/kotlin/at.phatbl.simple_exec/GradleExec.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package at.phatbl.simple_exec

import org.gradle.api.tasks.AbstractExecTask
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.process.ExecResult
import org.gradle.process.ExecSpec
import org.gradle.process.ProcessForkOptions
import org.gradle.process.internal.ExecAction
import java.io.File
import java.io.InputStream
import java.io.OutputStream

/**
* Interface for compatibility with the Gradle Exec task.
*/
interface GradleExec <T: Any> { //<T: AbstractExecTask>: ExecSpec {
fun commandLine(vararg arguments: Any): T

fun commandLine(args: Iterable<*>): T

fun args(vararg args: Any): T

fun args(args: Iterable<*>): T

fun setArgs(arguments: List<String>): T

fun setArgs(arguments: Iterable<*>): T

@Optional
@Input
fun getArgs(): List<String>

@Internal
fun getCommandLine(): List<String>

fun setCommandLine(args: List<String>)

fun setCommandLine(args: Iterable<*>)

fun setCommandLine(vararg args: Any)

@Optional
@Input
fun getExecutable(): String

fun setExecutable(executable: String)

fun setExecutable(executable: Any)

fun executable(executable: Any): T

@Internal
// TODO:LPTR Should be a content-less @InputDirectory
fun getWorkingDir(): File

fun setWorkingDir(dir: File)

fun setWorkingDir(dir: Any)

fun workingDir(dir: Any): T

@Internal
fun getEnvironment(): Map<String, Any>

fun setEnvironment(environmentVariables: Map<String, *>)

fun environment(name: String, value: Any): T

fun environment(environmentVariables: Map<String, *>): T

fun copyTo(target: ProcessForkOptions): T

fun setStandardInput(inputStream: InputStream): T

@Internal
fun getStandardInput(): InputStream

fun setStandardOutput(outputStream: OutputStream): T

@Internal
fun getStandardOutput(): OutputStream

fun setErrorOutput(outputStream: OutputStream): T

@Internal
fun getErrorOutput(): OutputStream

fun setIgnoreExitValue(ignoreExitValue: Boolean): T

@Input
fun isIgnoreExitValue(): Boolean

/**
* Returns the result for the command run by this task. Returns `null` if this task has not been executed yet.
*
* @return The result. Returns `null` if this task has not been executed yet.
*/
@Internal
fun getExecResult(): ExecResult
}
85 changes: 85 additions & 0 deletions src/main/kotlin/at.phatbl.simple_exec/ShellCommand.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package at.phatbl.simple_exec

import java.io.*
import java.util.concurrent.TimeUnit

/**
* Wrapper for running several commands inside a Bash shell.
*/
data class ShellCommand(
val baseDir: File,
val command: String
) {
lateinit var process: Process

var standardOutput: OutputStream? = null
var errorOutput: OutputStream? = null
// val standardInput: InputStream

val stdout: String
get() = stream2String(process.inputStream)

val stderr: String
get() = stream2String(process.errorStream)

var exitValue: Int = -999

val succeeded: Boolean
get() = exitValue == 0

val failed: Boolean
get() = !succeeded

/**
* Runs the command.
*/
fun start() {
baseDir.mkdir()
val pb = ProcessBuilder("bash", "-c", "cd $baseDir && $command")
process = pb.start()

if (standardOutput != null) {
copy(input = process.inputStream, output = standardOutput!!)
}
if (errorOutput != null) {
copy(input = process.errorStream, output = errorOutput!!)
}

// 20m
process.waitFor(1200, TimeUnit.SECONDS)

exitValue = process.exitValue()
}

/**
* Utility function which converts an input stream into a string.
*/
private fun stream2String(stream: InputStream): String {
val reader = BufferedReader(InputStreamReader(stream))
val builder = StringBuilder()
val lineSeparator = System.getProperty("line.separator")
reader.forEachLine { line ->
builder.append(line)
builder.append(lineSeparator)
}
return builder.toString()
}

private val BUFFER_SIZE = 2 * 1024 * 1024

@Throws(IOException::class)
private fun copy(input: InputStream, output: OutputStream) {
try {
val buffer = ByteArray(BUFFER_SIZE)
var bytesRead = input.read(buffer)
while (bytesRead != -1) {
output.write(buffer, 0, bytesRead)
bytesRead = input.read(buffer)
}
//If needed, close streams.
} finally {
input.close()
output.close()
}
}
}
Loading