-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Try to automatically recover from toolchain install failures
Issue: #37
- Loading branch information
1 parent
a9b2d18
commit 91924a7
Showing
8 changed files
with
266 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
plugin-rust-agent/src/main/kotlin/jetbrains/buildServer/rust/CargoCommandBuildSession.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* | ||
* Copyright 2000-2017 JetBrains s.r.o. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* See LICENSE in the project root for license information. | ||
*/ | ||
|
||
package jetbrains.buildServer.rust | ||
|
||
import jetbrains.buildServer.agent.BuildFinishedStatus | ||
import jetbrains.buildServer.agent.BuildRunnerContext | ||
import jetbrains.buildServer.agent.runner.CommandExecution | ||
import jetbrains.buildServer.agent.runner.CommandLineBuildService | ||
import jetbrains.buildServer.agent.runner.MultiCommandBuildSession | ||
import jetbrains.buildServer.util.FileUtil | ||
import java.io.File | ||
import kotlin.coroutines.experimental.buildIterator | ||
|
||
/** | ||
* Cargo runner service. | ||
*/ | ||
class CargoCommandBuildSession(private val runnerContext: BuildRunnerContext) : MultiCommandBuildSession { | ||
|
||
private var buildSteps: Iterator<CommandExecution>? = null | ||
private var lastCommands = arrayListOf<CommandExecutionAdapter>() | ||
|
||
override fun sessionStarted() { | ||
buildSteps = getSteps() | ||
} | ||
|
||
override fun getNextCommand(): CommandExecution? { | ||
buildSteps?.let { | ||
if (it.hasNext()) { | ||
return it.next() | ||
} | ||
} | ||
|
||
return null | ||
} | ||
|
||
override fun sessionFinished(): BuildFinishedStatus? { | ||
return lastCommands.last().result | ||
} | ||
|
||
private fun getSteps() = buildIterator<CommandExecution> { | ||
runnerContext.runnerParameters[CargoConstants.PARAM_TOOLCHAIN]?.let { | ||
if (it.isNotBlank()) { | ||
val installToolchain = RustupBuildService("install") | ||
yield(addCommand(installToolchain)) | ||
|
||
// Rustup could fail to install toolchain | ||
// We could try to resolve it by execution uninstall of toolchain | ||
// and cleaning up temporary directories | ||
if (installToolchain.errors.isNotEmpty()) { | ||
val logger = runnerContext.build.buildLogger | ||
logger.message("Installation has failed, will remove toolchain '${installToolchain.version}' and try again") | ||
|
||
val uninstallToolchain = RustupBuildService("uninstall") | ||
yield(addCommand(uninstallToolchain)) | ||
|
||
val rustupCache = File(System.getProperty("user.home"), ".rustup") | ||
installToolchain.version.let { | ||
// Cleanup temp directories | ||
FileUtil.delete(File(rustupCache, CargoConstants.RUSTUP_DOWNLOADS_DIR)) | ||
FileUtil.delete(File(rustupCache, CargoConstants.RUSTUP_TMP_DIR)) | ||
|
||
// Remove toolchain files | ||
FileUtil.delete(File(rustupCache, "${CargoConstants.RUSTUP_TOOLCHAINS_DIR}/$it")) | ||
FileUtil.delete(File(rustupCache, "${CargoConstants.RUSTUP_HASHES_DIR}/$it")) | ||
} | ||
|
||
yield(addCommand(RustupBuildService("install"))) | ||
} | ||
} | ||
} | ||
|
||
yield(addCommand(CargoRunnerBuildService())) | ||
} | ||
|
||
private fun addCommand(buildService: CommandLineBuildService) = CommandExecutionAdapter(buildService.apply { | ||
this.initialize(runnerContext.build, runnerContext) | ||
}).apply { | ||
lastCommands.add(this) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
65 changes: 65 additions & 0 deletions
65
plugin-rust-agent/src/main/kotlin/jetbrains/buildServer/rust/CommandExecutionAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* Copyright 2000-2017 JetBrains s.r.o. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* See LICENSE in the project root for license information. | ||
*/ | ||
|
||
package jetbrains.buildServer.rust | ||
|
||
import jetbrains.buildServer.agent.BuildFinishedStatus | ||
import jetbrains.buildServer.agent.runner.* | ||
import java.io.File | ||
|
||
class CommandExecutionAdapter(private val buildService: CommandLineBuildService) : CommandExecution { | ||
|
||
private val processListeners by lazy { buildService.listeners } | ||
|
||
var result: BuildFinishedStatus? = null | ||
private set | ||
|
||
override fun processFinished(exitCode: Int) { | ||
buildService.afterProcessFinished() | ||
|
||
processListeners.forEach { | ||
it.processFinished(exitCode) | ||
} | ||
|
||
result = buildService.getRunResult(exitCode) | ||
if (result == BuildFinishedStatus.FINISHED_SUCCESS) { | ||
buildService.afterProcessSuccessfullyFinished() | ||
} | ||
} | ||
|
||
override fun processStarted(programCommandLine: String, workingDirectory: File) { | ||
processListeners.forEach { | ||
it.processStarted(programCommandLine, workingDirectory) | ||
} | ||
} | ||
|
||
override fun onStandardOutput(text: String) { | ||
processListeners.forEach { | ||
it.onStandardOutput(text) | ||
} | ||
} | ||
|
||
override fun onErrorOutput(text: String) { | ||
processListeners.forEach { | ||
it.onErrorOutput(text) | ||
} | ||
} | ||
|
||
override fun interruptRequested(): TerminationAction { | ||
return buildService.interrupt() | ||
} | ||
|
||
override fun makeProgramCommandLine(): ProgramCommandLine { | ||
return buildService.makeProgramCommandLine() | ||
} | ||
|
||
override fun isCommandLineLoggingEnabled() = buildService.isCommandLineLoggingEnabled | ||
|
||
override fun beforeProcessStarted() { | ||
buildService.beforeProcessStarted() | ||
} | ||
} |
79 changes: 79 additions & 0 deletions
79
plugin-rust-agent/src/main/kotlin/jetbrains/buildServer/rust/RustupBuildService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
/* | ||
* Copyright 2000-2017 JetBrains s.r.o. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). | ||
* See LICENSE in the project root for license information. | ||
*/ | ||
|
||
package jetbrains.buildServer.rust | ||
|
||
import jetbrains.buildServer.RunBuildException | ||
import jetbrains.buildServer.agent.ToolCannotBeFoundException | ||
import jetbrains.buildServer.agent.runner.BuildServiceAdapter | ||
import jetbrains.buildServer.agent.runner.ProcessListener | ||
import jetbrains.buildServer.agent.runner.ProcessListenerAdapter | ||
import jetbrains.buildServer.agent.runner.ProgramCommandLine | ||
import jetbrains.buildServer.rust.logging.BlockListener | ||
|
||
/** | ||
* Rustup runner service. | ||
*/ | ||
class RustupBuildService(private val action: String) : BuildServiceAdapter() { | ||
|
||
val errors = arrayListOf<String>() | ||
var foundVersion: String? = null | ||
|
||
val version: String | ||
get() = foundVersion ?: runnerParameters[CargoConstants.PARAM_TOOLCHAIN]!! | ||
|
||
override fun makeProgramCommandLine(): ProgramCommandLine { | ||
val toolchainVersion = runnerParameters[CargoConstants.PARAM_TOOLCHAIN]!!.trim() | ||
val rustupPath = getPath(CargoConstants.RUSTUP_CONFIG_NAME) | ||
|
||
return createProgramCommandline(rustupPath, arrayListOf("toolchain", action, toolchainVersion)) | ||
} | ||
|
||
private fun getPath(toolName: String): String { | ||
try { | ||
return getToolPath(toolName) | ||
} catch (e: ToolCannotBeFoundException) { | ||
val buildException = RunBuildException(e) | ||
buildException.isLogStacktrace = false | ||
throw buildException | ||
} | ||
} | ||
|
||
override fun isCommandLineLoggingEnabled() = false | ||
|
||
override fun getListeners(): MutableList<ProcessListener> { | ||
return arrayListOf<ProcessListener>().apply { | ||
val blockName = "$action toolchain: ${runnerParameters[CargoConstants.PARAM_TOOLCHAIN]}" | ||
this.add(BlockListener(blockName, logger)) | ||
this.add(object : ProcessListenerAdapter() { | ||
override fun onStandardOutput(text: String) { | ||
processOutput(text) | ||
} | ||
|
||
override fun onErrorOutput(text: String) { | ||
processOutput(text) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
fun processOutput(text: String) { | ||
if (text.startsWith("error:")) { | ||
errors.add(text) | ||
} | ||
|
||
toolchainVersion.matchEntire(text)?.let { | ||
foundVersion = it.groupValues.last() | ||
} | ||
|
||
logger.message(text) | ||
} | ||
|
||
companion object { | ||
val toolchainVersion = Regex("info: syncing channel updates for '([^']+)'") | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
plugin-rust-agent/src/main/kotlin/jetbrains/buildServer/rust/logging/BlockListener.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package jetbrains.buildServer.rust.logging | ||
|
||
import jetbrains.buildServer.agent.BuildProgressLogger | ||
import jetbrains.buildServer.agent.runner.ProcessListenerAdapter | ||
import java.io.File | ||
|
||
class BlockListener(private val blockName:String, | ||
private val logger: BuildProgressLogger) : ProcessListenerAdapter() { | ||
|
||
override fun processStarted(programCommandLine: String, workingDirectory: File) { | ||
logger.message("##teamcity[blockOpened name='$blockName']") | ||
} | ||
|
||
override fun processFinished(exitCode: Int) { | ||
logger.message("##teamcity[blockClosed name='$blockName']") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters