parent
a70ae91ce4
commit
928a08b842
@ -0,0 +1,22 @@
|
||||
package alfred.web.core
|
||||
|
||||
import alfred.web.core.build.BuildId
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Service
|
||||
import java.nio.file.Paths
|
||||
|
||||
@Service
|
||||
class AlfredHome(
|
||||
@Value("\${ALFRED_HOME}")
|
||||
private val home: String,
|
||||
) {
|
||||
|
||||
val homePath = Paths.get(home)
|
||||
|
||||
fun logsDir() =
|
||||
homePath.resolve("logs")
|
||||
|
||||
fun buildConfig(buildId: BuildId) =
|
||||
homePath.resolve("builds/${buildId}.properties")
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package alfred.web.core.process
|
||||
|
||||
import alfred.web.core.Handle
|
||||
import alfred.web.core.Handles
|
||||
import alfred.web.core.build.BuildContext
|
||||
import alfred.web.core.build.BuildId
|
||||
import alfred.web.core.build.Workspace
|
||||
import org.springframework.stereotype.Service
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Service
|
||||
class Git(
|
||||
val processes: Processes,
|
||||
val handles: Handles,
|
||||
) {
|
||||
|
||||
fun clone(ctx: BuildContext, ws: Workspace) {
|
||||
ctx.log("cloning ${ctx.config.gitRepo} into $ws")
|
||||
|
||||
val proc = processes.builder(ctx.config, ctx.logFile, "")
|
||||
.command("git", "clone", ctx.config.gitRepo, ".")
|
||||
.directory(ws.toFile())
|
||||
.start()
|
||||
handles.add(Handle(proc.toHandle(), ctx.buildId))
|
||||
|
||||
val cloneSuccess = proc.waitFor(ctx.config.gitCloneTimeout, TimeUnit.SECONDS)
|
||||
if (!cloneSuccess) {
|
||||
throw FailedToClone(ctx.buildId, ctx.config.gitRepo ?: "[no repo configured]")
|
||||
}
|
||||
}
|
||||
|
||||
fun checkout(ctx: BuildContext, ws: Workspace) {
|
||||
ctx.log("checkout rev ${ctx.rev}")
|
||||
|
||||
val proc = processes.builder(ctx.config, ctx.logFile, "")
|
||||
.command("git", "checkout", ctx.rev)
|
||||
.directory(ws.toFile())
|
||||
.start()
|
||||
handles.add(Handle(proc.toHandle(), ctx.buildId))
|
||||
|
||||
val checkoutSuccess = proc.waitFor(ctx.config.gitCloneTimeout, TimeUnit.SECONDS)
|
||||
if (!checkoutSuccess) {
|
||||
throw FailedToCheckout(ctx.buildId, ctx.config.gitRepo!!, ctx.rev)
|
||||
}
|
||||
}
|
||||
|
||||
sealed class GitException(msg: String): Exception(msg)
|
||||
|
||||
private class FailedToClone(
|
||||
buildId: BuildId,
|
||||
gitRepo: String
|
||||
) : GitException(
|
||||
"failed to clone $gitRepo for build id $buildId"
|
||||
)
|
||||
|
||||
private class FailedToCheckout(
|
||||
buildId: BuildId,
|
||||
gitRepo: String,
|
||||
rev: String
|
||||
) : GitException(
|
||||
"failed to checkout revision $rev on repo $gitRepo for build id $buildId"
|
||||
)
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package alfred.web.core.process
|
||||
|
||||
import alfred.web.core.Handle
|
||||
import alfred.web.core.Handles
|
||||
import alfred.web.core.build.BuildContext
|
||||
import alfred.web.core.build.Workspace
|
||||
import org.springframework.stereotype.Service
|
||||
import java.io.File
|
||||
|
||||
@Service
|
||||
class Script(
|
||||
val handles: Handles,
|
||||
val processes: Processes
|
||||
) {
|
||||
|
||||
fun execute(ctx: BuildContext, ws: Workspace, scriptFile: File): Process {
|
||||
ctx.log("Running build file: ${scriptFile.name}")
|
||||
|
||||
val scriptProcess = processes.builder(ctx.config, ctx.logFile, ctx.rev)
|
||||
.command(scriptFile.absolutePath)
|
||||
.directory(ws.toFile())
|
||||
.start()
|
||||
handles.add(Handle(scriptProcess.toHandle(), ctx.buildId))
|
||||
|
||||
return scriptProcess
|
||||
}
|
||||
|
||||
fun exists(scriptFile: File) = scriptFile.exists()
|
||||
|
||||
fun onExit(process: Process, block: () -> Unit) {
|
||||
process.onExit().whenComplete { _, _ -> block() }
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package alfred.web.core
|
||||
|
||||
import io.mockk.justRun
|
||||
import io.mockk.mockk
|
||||
import org.springframework.context.ApplicationEventPublisher
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
fun <T : Any> eventPublisher(eventType: KClass<T>): ApplicationEventPublisher {
|
||||
val eventPublisher = mockk<ApplicationEventPublisher>()
|
||||
justRun { eventPublisher.publishEvent(any(eventType)) }
|
||||
return eventPublisher
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package alfred.web.core.runner
|
||||
|
||||
import alfred.web.core.AlfredHome
|
||||
import alfred.web.core.build.Builds
|
||||
import alfred.web.core.build.Workspaces
|
||||
import alfred.web.core.event.BuildFinished
|
||||
import alfred.web.core.eventPublisher
|
||||
import alfred.web.core.process.Git
|
||||
import alfred.web.core.process.ProcessEnvironment
|
||||
import alfred.web.core.process.Processes
|
||||
import alfred.web.core.process.Script
|
||||
import io.mockk.every
|
||||
import io.mockk.justRun
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
class GitRunnerTest {
|
||||
|
||||
@Test
|
||||
fun runs(@TempDir logsDir: Path) {
|
||||
// Given
|
||||
val eventPublisher = eventPublisher(BuildFinished::class)
|
||||
|
||||
val git = mockk<Git>()
|
||||
justRun { git.clone(any(), any()) }
|
||||
justRun { git.checkout(any(), any()) }
|
||||
|
||||
val script = mockk<Script>()
|
||||
every { script.execute(any(), any(), any()) } returns (mockk<Process>(relaxed = true))
|
||||
every { script.exists(any()) } returns (true)
|
||||
|
||||
val workspacesSpy = spyk(Workspaces())
|
||||
|
||||
val homeDir = Paths.get("").toAbsolutePath().resolve("src/test/resources/home-1")
|
||||
val home = spyk(AlfredHome(homeDir.toFile().absolutePath))
|
||||
every { home.logsDir() } returns logsDir
|
||||
|
||||
val runner = GitRunner(
|
||||
builds = Builds(home),
|
||||
processes = Processes(ProcessEnvironment()),
|
||||
workspaces = workspacesSpy,
|
||||
eventPublisher = eventPublisher,
|
||||
git = git,
|
||||
script = script
|
||||
)
|
||||
|
||||
// When
|
||||
runner.run(
|
||||
buildId = "simple-git",
|
||||
rev = "master"
|
||||
)
|
||||
|
||||
// Then
|
||||
verify(timeout = 1_000, exactly = 1) {
|
||||
git.clone(any(), any())
|
||||
git.checkout(any(), any())
|
||||
|
||||
listOf("pre.sh", "job.sh", "post.sh").forEach { scriptName ->
|
||||
script.exists(match { it.name == scriptName })
|
||||
script.execute(any(), any(), match { it.name == scriptName })
|
||||
}
|
||||
|
||||
eventPublisher.publishEvent(any(BuildFinished::class))
|
||||
workspacesSpy.cleanUp(any())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package alfred.web.core.runner
|
||||
|
||||
import alfred.web.core.AlfredHome
|
||||
import alfred.web.core.build.Builds
|
||||
import alfred.web.core.build.Workspaces
|
||||
import alfred.web.core.event.BuildFinished
|
||||
import alfred.web.core.eventPublisher
|
||||
import alfred.web.core.process.Script
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import org.springframework.test.web.client.ExpectedCount.once
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
|
||||
|
||||
class ScriptRunnerTest {
|
||||
|
||||
@Test
|
||||
fun runs(@TempDir logDir: Path) {
|
||||
// Given
|
||||
val home = spyk(
|
||||
AlfredHome(
|
||||
Paths.get("").toAbsolutePath().resolve("src/test/resources/home-1").toString()
|
||||
)
|
||||
)
|
||||
every { home.logsDir() } returns logDir
|
||||
|
||||
val eventPublisher = eventPublisher(BuildFinished::class)
|
||||
|
||||
val script = mockk<Script>()
|
||||
every { script.execute(any(), any(), any()) } returns mockk<Process>(relaxed = true)
|
||||
val onExit = slot<() -> Unit>()
|
||||
every { script.onExit(any(), capture(onExit)) } answers { onExit.captured() }
|
||||
|
||||
val workspaces = spyk(Workspaces())
|
||||
|
||||
val runner = ScriptRunner(
|
||||
builds = Builds(home),
|
||||
workspaces = workspaces,
|
||||
eventPublisher = eventPublisher,
|
||||
script = script
|
||||
)
|
||||
|
||||
// When
|
||||
runner.run(
|
||||
buildId = "simple-script",
|
||||
rev = null
|
||||
)
|
||||
|
||||
// Then
|
||||
verify(timeout = 1_000, exactly = 1) {
|
||||
script.execute(any(), any(), match { it.name == "some-script" })
|
||||
|
||||
workspaces.cleanUp(any())
|
||||
|
||||
eventPublisher.publishEvent(any(BuildFinished::class))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
user=alfred
|
||||
workspace=/tmp
|
||||
apikey=Mellon
|
||||
|
||||
git.repo.url=some.url
|
@ -0,0 +1,5 @@
|
||||
user=alfred
|
||||
workspace=/tmp
|
||||
apikey=Mellon
|
||||
|
||||
script=some-script
|
Loading…
Reference in new issue