From c3a9fe8834b14c22c4feb5bd097abd964a009d59 Mon Sep 17 00:00:00 2001 From: Mihail Buryakov Date: Fri, 30 May 2025 01:05:23 +0300 Subject: [PATCH] [eel-vcs] using eel external cli api: add a helper method in the base class GitOrigin-RevId: 2bb99e819515fa8099fc6b7e7f3c9de4d3bf7269 --- .../external-process-auth-helper/BUILD.bazel | 4 ++ .../external-process-auth-helper/api-dump.txt | 1 + ...lij.platform.externalProcessAuthHelper.iml | 2 + .../ExternalProcessHandlerService.kt | 72 ++++++++++++++++++- 4 files changed, 76 insertions(+), 3 deletions(-) diff --git a/platform/external-process-auth-helper/BUILD.bazel b/platform/external-process-auth-helper/BUILD.bazel index 6e307804284a..7dee50f69f24 100644 --- a/platform/external-process-auth-helper/BUILD.bazel +++ b/platform/external-process-auth-helper/BUILD.bazel @@ -25,6 +25,8 @@ jvm_library( "//platform/platform-util-netty:ide-util-netty", "@lib//:netty-codec-http", "@lib//:netty-buffer", + "//platform/eel", + "//platform/eel-provider", ], exports = ["//platform/built-in-server:builtInServer-impl"], runtime_deps = [":external-process-auth-helper_resources"] @@ -50,6 +52,8 @@ jvm_library( "@lib//:netty-codec-http", "@lib//:netty-buffer", "@lib//:junit5", + "//platform/eel", + "//platform/eel-provider", ], runtime_deps = [":external-process-auth-helper_resources"] ) diff --git a/platform/external-process-auth-helper/api-dump.txt b/platform/external-process-auth-helper/api-dump.txt index 7dcea5d3d347..33546ae5beb0 100644 --- a/platform/external-process-auth-helper/api-dump.txt +++ b/platform/external-process-auth-helper/api-dump.txt @@ -15,6 +15,7 @@ f:com.intellij.externalProcessAuthHelper.ExternalProcessAuthHelperBundle - s:messagePointer(java.lang.String,java.lang.Object[]):java.util.function.Supplier a:com.intellij.externalProcessAuthHelper.ExternalProcessHandlerService - (java.lang.String,java.lang.Class):V +- (java.lang.String,java.lang.Class,externalApp.ExternalCli,java.util.List,kotlinx.coroutines.CoroutineScope):V - f:getCallbackScriptPath(java.lang.String,com.intellij.externalProcessAuthHelper.ScriptGenerator,Z):java.io.File - f:getIdePort():I - pa:handleRequest(externalApp.ExternalAppHandler,java.lang.String):java.lang.String diff --git a/platform/external-process-auth-helper/intellij.platform.externalProcessAuthHelper.iml b/platform/external-process-auth-helper/intellij.platform.externalProcessAuthHelper.iml index 2abe54068741..f7919143c055 100644 --- a/platform/external-process-auth-helper/intellij.platform.externalProcessAuthHelper.iml +++ b/platform/external-process-auth-helper/intellij.platform.externalProcessAuthHelper.iml @@ -22,5 +22,7 @@ + + \ No newline at end of file diff --git a/platform/external-process-auth-helper/src/externalProcessAuthHelper/ExternalProcessHandlerService.kt b/platform/external-process-auth-helper/src/externalProcessAuthHelper/ExternalProcessHandlerService.kt index be4cd45b38cb..d1d7a2f1af1c 100644 --- a/platform/external-process-auth-helper/src/externalProcessAuthHelper/ExternalProcessHandlerService.kt +++ b/platform/external-process-auth-helper/src/externalProcessAuthHelper/ExternalProcessHandlerService.kt @@ -6,23 +6,41 @@ import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.progress.EmptyProgressIndicator import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.progress.runBlockingMaybeCancellable import com.intellij.openapi.util.Computable import com.intellij.openapi.util.Disposer +import com.intellij.platform.eel.EelApi +import com.intellij.platform.eel.EelExecApi +import com.intellij.platform.eel.provider.asNioPath +import com.intellij.platform.eel.provider.utils.asOutputStream +import com.intellij.util.cancelOnDispose import com.intellij.util.concurrency.AppExecutorUtil import com.intellij.util.io.readUtf8 import externalApp.ExternalApp +import externalApp.ExternalAppEntry import externalApp.ExternalAppHandler +import externalApp.ExternalCli import io.netty.channel.ChannelHandlerContext -import io.netty.handler.codec.http.* +import io.netty.handler.codec.http.FullHttpRequest +import io.netty.handler.codec.http.HttpMethod +import io.netty.handler.codec.http.HttpResponseStatus +import io.netty.handler.codec.http.QueryStringDecoder +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import org.jetbrains.annotations.ApiStatus import org.jetbrains.annotations.NonNls -import org.jetbrains.ide.* +import org.jetbrains.ide.BuiltInServerManager +import org.jetbrains.ide.RestService import org.jetbrains.io.response import java.io.File import java.io.IOException +import java.io.PrintStream import java.nio.charset.StandardCharsets +import java.nio.file.Path import java.util.* import java.util.concurrent.CompletableFuture import java.util.concurrent.ConcurrentHashMap +import kotlin.io.path.pathString /** * The provider of external application scripts called by Git when a remote operation needs communication with the user. @@ -37,9 +55,16 @@ import java.util.concurrent.ConcurrentHashMap */ abstract class ExternalProcessHandlerService( private val scriptNamePrefix: @NonNls String, - private val scriptMainClass: Class + private val scriptMainClass: Class, + private val scriptBody: ExternalCli?, + private val requiredEnvVariables: List, + private val coroutineScope: CoroutineScope? ) { + @Deprecated("Use constructor with scriptMainInstance") + constructor(scriptNamePrefix: String, scriptMainClass: Class) : + this(scriptNamePrefix, scriptMainClass, null, emptyList(), null) + private val scriptPaths = HashMap<@NonNls String, File>() private val SCRIPT_FILE_LOCK = Any() @@ -69,6 +94,47 @@ abstract class ExternalProcessHandlerService( } } + /** + * Get file to the script service + * + * @return path to the script + */ + @Throws(IOException::class) + @ApiStatus.Internal + fun getCallbackScriptPath(eelApi: EelApi, useBatchFile: Boolean, disposable: Disposable): Path { + if (scriptBody == null || coroutineScope == null) throw UnsupportedOperationException("Handler ${this.javaClass} doesn't support eel external cli.") + return runBlockingMaybeCancellable { + val script = eelApi.exec.createExternalCli(object : EelExecApi.LocalExternalCliOptions { + override val mainClass = scriptMainClass + override val useBatchFile = useBatchFile + override val filePrefix = scriptNamePrefix + override val envVariablesToCapture = requiredEnvVariables + }) + coroutineScope.launch { + script.consumeInvocations { process -> + process.toExternalAppEntry().use { externalAppEntry -> + scriptBody.entryPoint(externalAppEntry) + } + } + }.cancelOnDispose(disposable) + script.path.asNioPath() + } + } + + private class EelExternalAppEntry(val process: EelExecApi.ExternalCliProcess) : ExternalAppEntry, AutoCloseable { + val myStderr: PrintStream = process.stderr.asOutputStream().let(::PrintStream) + override fun getArgs(): Array = process.args.toTypedArray() + override fun getEnvironment(): Map = process.environment + override fun getWorkingDirectory(): String = process.workingDir.asNioPath().pathString + override fun getStderr(): PrintStream = myStderr + + override fun close() { + stderr.close() + } + } + + private fun EelExecApi.ExternalCliProcess.toExternalAppEntry() = EelExternalAppEntry(this) + fun registerHandler(handler: T, disposable: Disposable): UUID { val key: UUID = UUID.randomUUID() handlers[key] = handler