KT-57650 run pod install --repo-update if repo is out of date

This commit is contained in:
Andrey Yastrebov
2023-11-27 14:23:30 +01:00
committed by Space Team
parent da4c6dd443
commit 4cdb9301ed
2 changed files with 104 additions and 30 deletions
@@ -11,10 +11,10 @@ import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import org.gradle.work.DisableCachingByDefault
import org.jetbrains.kotlin.gradle.utils.CommandFallback
import org.jetbrains.kotlin.gradle.utils.onlyIfCompat
import org.jetbrains.kotlin.gradle.utils.runCommand
import org.jetbrains.kotlin.gradle.utils.runCommandWithFallback
import java.io.File
import java.io.IOException
/**
* The task takes the path to the Podfile and calls `pod install`
@@ -45,19 +45,7 @@ abstract class AbstractPodInstallTask : CocoapodsTask() {
@TaskAction
open fun doPodInstall() {
// env is used here to work around the JVM PATH caching when spawning a child process with custom environment, i.e. LC_ALL
// The caching causes the ProcessBuilder to ignore changes in the PATH that may occur on incremental runs of the Gradle daemon
// KT-60394
val podInstallCommand = listOf("env", "pod", "install")
runCommand(podInstallCommand,
logger,
errorHandler = { retCode, output, process -> sharedHandleError(podInstallCommand, retCode, output, process) },
processConfiguration = {
directory(workingDir.get())
// CocoaPods requires to be run with Unicode external encoding
environment().putIfAbsent("LC_ALL", "en_US.UTF-8")
})
runPodInstall(false)
with(podsXcodeProjDirProvider.get()) {
check(exists() && isDirectory) {
@@ -66,6 +54,28 @@ abstract class AbstractPodInstallTask : CocoapodsTask() {
}
}
private fun runPodInstall(updateRepo: Boolean): String {
// env is used here to work around the JVM PATH caching when spawning a child process with custom environment, i.e. LC_ALL
// The caching causes the ProcessBuilder to ignore changes in the PATH that may occur on incremental runs of the Gradle daemon
// KT-60394
val podInstallCommand = listOfNotNull("env", "pod", "install", if (updateRepo) "--repo-update" else null)
return runCommandWithFallback(podInstallCommand,
logger,
fallback = { retCode, output, process ->
if (output.contains("out-of-date source repos which you can update with `pod repo update` or with `pod install --repo-update`") && updateRepo.not()) {
CommandFallback.Action(runPodInstall(true))
} else {
CommandFallback.Error(sharedHandleError(podInstallCommand, retCode, output, process))
}
},
processConfiguration = {
directory(workingDir.get())
// CocoaPods requires to be run with Unicode external encoding
environment().putIfAbsent("LC_ALL", "en_US.UTF-8")
})
}
private fun sharedHandleError(podInstallCommand: List<String>, retCode: Int, error: String, process: Process): String? {
return if (error.contains("No such file or directory")) {
val command = podInstallCommand.joinToString(" ")
@@ -6,15 +6,80 @@
package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.logging.Logger
import java.io.IOException
import kotlin.concurrent.thread
/**
* Executes a command and returns the input text.
*
* @param command the command and its arguments to be executed as a list of strings.
* @param logger an optional logger to log information about the command execution.
* @param errorHandler (Optional) A function that handles any errors that occur during the command execution.
* @param processConfiguration a function to configure the process before execution.
* @return The input text of the executed command.
*/
internal fun runCommand(
command: List<String>,
logger: Logger? = null,
errorHandler: ((retCode: Int, output: String, process: Process) -> String?)? = null,
processConfiguration: ProcessBuilder.() -> Unit = { }
processConfiguration: ProcessBuilder.() -> Unit = { },
): String {
val runResult = assembleAndRunProcess(command, logger, processConfiguration)
check(runResult.retCode == 0) {
errorHandler?.invoke(runResult.retCode, runResult.output, runResult.process) ?: createErrorMessage(command, runResult)
}
return runResult.inputText
}
/**
* Sealed class representing the fallback behavior for a command.
*/
sealed class CommandFallback {
data class Action(val fallback: String) : CommandFallback()
data class Error(val error: String?) : CommandFallback()
}
/**
* Executes the specified command with fallback behavior in case of non-zero return code.
*
* @param command the command and its arguments to be executed as a list of strings.
* @param logger an optional logger to log information about the command execution.
* @param fallback a function that provides the fallback behavior. It takes the return code, output, and process as parameters and returns a [CommandFallback] object.
* @param processConfiguration a function to configure the process before execution.
* @return the output of the command if the return code is 0, otherwise the fallback action or error.
*/
internal fun runCommandWithFallback(
command: List<String>,
logger: Logger? = null,
fallback: (retCode: Int, output: String, process: Process) -> CommandFallback,
processConfiguration: ProcessBuilder.() -> Unit = { },
): String {
val runResult = assembleAndRunProcess(command, logger, processConfiguration)
return if (runResult.retCode != 0) {
when (val fallbackOption = fallback(runResult.retCode, runResult.output, runResult.process)) {
is CommandFallback.Action -> fallbackOption.fallback
is CommandFallback.Error -> error(fallbackOption.error ?: createErrorMessage(command, runResult))
}
} else {
runResult.inputText
}
}
private data class RunProcessResult(
val inputText: String,
val errorText: String,
val retCode: Int,
val process: Process,
) {
val output: String get() = inputText.ifBlank { errorText }
}
private fun assembleAndRunProcess(
command: List<String>,
logger: Logger? = null,
processConfiguration: ProcessBuilder.() -> Unit = { },
): RunProcessResult {
val process = ProcessBuilder(command).apply {
this.processConfiguration()
}.start()
@@ -46,17 +111,16 @@ internal fun runCommand(
""".trimMargin()
)
check(retCode == 0) {
errorHandler?.invoke(retCode, inputText.ifBlank { errorText }, process)
?: """
|Executing of '${command.joinToString(" ")}' failed with code $retCode and message:
|
|$inputText
|
|$errorText
|
""".trimMargin()
}
return inputText
return RunProcessResult(inputText, errorText, retCode, process)
}
private fun createErrorMessage(command: List<String>, runResult: RunProcessResult): String {
return """
|Executing of '${command.joinToString(" ")}' failed with code ${runResult.retCode} and message:
|
|${runResult.inputText}
|
|${runResult.errorText}
|
""".trimMargin()
}