Cancelling a build only killed the running build process; the synchronous preparation phases — most notably a multi-minute Docker image build, but also the Gradle-volume preparation — ran to completion and kept the concurrency slot occupied, so the next queued build stayed PENDING for a long time. Build runners now report every auxiliary process through an onAuxProcess sink (GitCommandRunner gained an onProcess hook), and the executor registers them like the build process, so cancellation terminates whatever is currently running. Measured on vm4006: cancel to next-build-running is ~3s in the normal case; the unit test covers the aux-phase case. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
67 lines
2.3 KiB
Kotlin
67 lines
2.3 KiB
Kotlin
package de.hoennig.gittally.build
|
|
|
|
import de.hoennig.gittally.config.BranchConfig
|
|
import org.springframework.context.annotation.Primary
|
|
import org.springframework.stereotype.Component
|
|
import java.nio.file.Path
|
|
|
|
/**
|
|
* Starts a single build or clean command and hands the [Process] back to the caller,
|
|
* which owns log streaming and process-tree termination.
|
|
* [repoDir] and [branchConfig] let implementations derive per-repository resources
|
|
* and per-branch settings (step 11: Docker runtime).
|
|
* Implementations report every auxiliary process they run before the build command
|
|
* (e.g. a Docker image build) via [onAuxProcess], so a cancellation can terminate
|
|
* long preparation phases instead of blocking the build slot until they finish.
|
|
*/
|
|
interface BuildRunner {
|
|
fun start(
|
|
command: String,
|
|
workingDir: Path,
|
|
environment: Map<String, String>,
|
|
repoDir: Path = workingDir,
|
|
branchConfig: BranchConfig = BranchConfig(),
|
|
onAuxProcess: (Process) -> Unit = {},
|
|
): Process
|
|
}
|
|
|
|
@Component
|
|
class ProcessBuildRunner : BuildRunner {
|
|
override fun start(
|
|
command: String,
|
|
workingDir: Path,
|
|
environment: Map<String, String>,
|
|
repoDir: Path,
|
|
branchConfig: BranchConfig,
|
|
onAuxProcess: (Process) -> Unit,
|
|
): Process {
|
|
val processBuilder = ProcessBuilder("bash", "-c", command)
|
|
processBuilder.directory(workingDir.toFile())
|
|
processBuilder.environment().putAll(environment)
|
|
return processBuilder.start()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Selects the runtime per branch: Docker when `branches.<name>.docker.enabled`,
|
|
* native shell execution otherwise (the unchanged default).
|
|
*/
|
|
@Primary
|
|
@Component
|
|
class DispatchingBuildRunner(
|
|
private val processBuildRunner: ProcessBuildRunner,
|
|
private val dockerBuildRunner: DockerBuildRunner,
|
|
) : BuildRunner {
|
|
override fun start(
|
|
command: String,
|
|
workingDir: Path,
|
|
environment: Map<String, String>,
|
|
repoDir: Path,
|
|
branchConfig: BranchConfig,
|
|
onAuxProcess: (Process) -> Unit,
|
|
): Process {
|
|
val runner = if (branchConfig.docker.enabled) dockerBuildRunner else processBuildRunner
|
|
return runner.start(command, workingDir, environment, repoDir, branchConfig, onAuxProcess)
|
|
}
|
|
}
|