Skip to content

Commit

Permalink
fix: Support envvar injection for Gradle tool window task (#117)
Browse files Browse the repository at this point in the history
## Description

The gradle task can be ran on many routes.

This PR fixes the envvar cannot be loaded when Gradle Task is ran by
Gradle Tool window.

- Closes: #116


---

Used a little hack: insert envvars to runConfiguration before execution,
and add terminated litener to the execution, remove the envvars from it.

Co-authored-by: Cooper Oh <cooper@chequer.io>
  • Loading branch information
134130 and cooper-oh authored Dec 26, 2024
1 parent 8a0a96d commit d682610
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,58 +6,86 @@ import com.github.l34130.mise.core.notification.MiseNotificationServiceUtils
import com.github.l34130.mise.core.run.MiseRunConfigurationSettingsEditor
import com.github.l34130.mise.core.setting.MiseSettings
import com.intellij.execution.Executor
import com.intellij.execution.application.ApplicationConfiguration
import com.intellij.execution.runners.ExecutionEnvironment
import com.intellij.execution.runners.ProgramRunner
import com.intellij.openapi.components.service
import com.intellij.openapi.externalSystem.util.ExternalSystemUtil
import com.intellij.openapi.project.Project
import com.intellij.task.ExecuteRunConfigurationTask
import org.jetbrains.plugins.gradle.execution.build.GradleExecutionEnvironmentProvider
import org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration
import org.jetbrains.plugins.gradle.util.GradleConstants

class GradleEnvironmentProvider : GradleExecutionEnvironmentProvider {
override fun isApplicable(task: ExecuteRunConfigurationTask?): Boolean = task?.runProfile is ApplicationConfiguration
override fun isApplicable(task: ExecuteRunConfigurationTask?): Boolean = task?.runProfile is GradleRunConfiguration

override fun createExecutionEnvironment(
project: Project,
task: ExecuteRunConfigurationTask,
executor: Executor,
): ExecutionEnvironment? {
val environment =
GradleExecutionEnvironmentProvider.EP_NAME.extensions
.firstOrNull { provider ->
provider != this && provider.isApplicable(task)
}?.createExecutionEnvironment(project, task, executor)

if (environment?.runProfile !is GradleRunConfiguration) {
return environment
}
val gradleRunConfiguration = environment.runProfile as GradleRunConfiguration
val runProfile = task.runProfile as GradleRunConfiguration
var environment: ExecutionEnvironment? = delegateProvider(task)?.createExecutionEnvironment(project, task, executor)

val projectState = project.service<MiseSettings>().state
val runConfigState = MiseRunConfigurationSettingsEditor.getMiseRunConfigurationState(gradleRunConfiguration)
// No registered environment provider. Gradle Task is in this case.
if (environment == null) {
environment =
ExternalSystemUtil.createExecutionEnvironment(
runProfile.project,
GradleConstants.SYSTEM_ID,
runProfile.settings.clone(),
executor.id,
)
}

val (workDir, configEnvironment) = when {
projectState.useMiseDirEnv -> project.basePath to projectState.miseConfigEnvironment
runConfigState?.useMiseDirEnv == true -> {
val sourceConfig = task.runProfile as ApplicationConfiguration
sourceConfig.project.basePath to runConfigState.miseConfigEnvironment
// Code Coverage runs maybe in this case.
if (environment == null) {
val runner = ProgramRunner.getRunner(executor.id, task.runProfile)
val taskSettings = task.settings
if (runner != null && taskSettings != null) {
environment =
ExecutionEnvironment(executor, runner, taskSettings, project)
}

else -> return environment
}

val miseEnvVars = MiseCommandLineHelper.getEnvVars(workDir, configEnvironment)
.fold(
onSuccess = { envVars -> envVars },
onFailure = {
if (it !is MiseCommandLineNotFoundException) {
MiseNotificationServiceUtils.notifyException("Failed to load environment variables", it)
if (environment?.runProfile is GradleRunConfiguration) {
val runProfile = task.runProfile as GradleRunConfiguration
val projectState = project.service<MiseSettings>().state
val runConfigState = MiseRunConfigurationSettingsEditor.getMiseRunConfigurationState(runProfile)

val (workDir, configEnvironment) =
when {
projectState.useMiseDirEnv -> project.basePath to projectState.miseConfigEnvironment
runConfigState?.useMiseDirEnv == true -> {
(runProfile.projectPathOnTarget ?: runProfile.project.basePath) to
runConfigState.miseConfigEnvironment
}
emptyMap()
},
)

gradleRunConfiguration.settings.env = miseEnvVars + gradleRunConfiguration.settings.env
else -> return environment
}

val miseEnvVars =
MiseCommandLineHelper
.getEnvVars(workDir, configEnvironment)
.fold(
onSuccess = { envVars -> envVars },
onFailure = {
if (it !is MiseCommandLineNotFoundException) {
MiseNotificationServiceUtils.notifyException("Failed to load environment variables", it)
}
emptyMap()
},
)

val settings = (environment.runProfile as GradleRunConfiguration).settings
settings.env = settings.env + miseEnvVars
}

return environment
}

private fun delegateProvider(task: ExecuteRunConfigurationTask): GradleExecutionEnvironmentProvider? {
val extensions = GradleExecutionEnvironmentProvider.EP_NAME.extensions
return extensions.firstOrNull { it !== this && it.isApplicable(task) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,24 @@ import com.github.l34130.mise.core.notification.MiseNotificationServiceUtils
import com.github.l34130.mise.core.run.MiseRunConfigurationSettingsEditor
import com.github.l34130.mise.core.setting.MiseSettings
import com.intellij.execution.RunConfigurationExtension
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.configurations.JavaParameters
import com.intellij.execution.configurations.RunConfigurationBase
import com.intellij.execution.configurations.RunnerSettings
import com.intellij.execution.process.ProcessEvent
import com.intellij.execution.process.ProcessHandler
import com.intellij.execution.process.ProcessListener
import com.intellij.openapi.components.service
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration
import com.intellij.openapi.options.SettingsEditor
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import org.jdom.Element
import java.util.concurrent.ConcurrentHashMap

class IdeaRunConfigurationExtension : RunConfigurationExtension() {
// Used for cleanup the configuration after the execution has ended.
private val runningProcessEnvs = ConcurrentHashMap<Project, Map<String, String>>()

override fun getEditorTitle(): String = MiseRunConfigurationSettingsEditor.EDITOR_TITLE

override fun <P : RunConfigurationBase<*>> createEditor(configuration: P): SettingsEditor<P> =
Expand All @@ -41,40 +50,68 @@ class IdeaRunConfigurationExtension : RunConfigurationExtension() {
params: JavaParameters,
runnerSettings: RunnerSettings?,
) {
val sourceEnv =
GeneralCommandLine()
.withEnvironment(params.env)
.withParentEnvironmentType(
if (params.isPassParentEnvs) {
GeneralCommandLine.ParentEnvironmentType.CONSOLE
} else {
GeneralCommandLine.ParentEnvironmentType.NONE
},
).effectiveEnvironment
params.env.putAll(sourceEnv)

val project = configuration.project
val projectState = project.service<MiseSettings>().state
val runConfigState = MiseRunConfigurationSettingsEditor.getMiseRunConfigurationState(configuration)

val (workDir, configEnvironment) = when {
projectState.useMiseDirEnv -> project.basePath to projectState.miseConfigEnvironment
runConfigState?.useMiseDirEnv == true -> (params.workingDirectory ?: project.basePath) to runConfigState.miseConfigEnvironment
else -> return
val (workDir, configEnvironment) =
when {
projectState.useMiseDirEnv -> project.basePath to projectState.miseConfigEnvironment
runConfigState?.useMiseDirEnv == true ->
(params.workingDirectory ?: project.basePath) to
runConfigState.miseConfigEnvironment
else -> return
}

val envVars =
MiseCommandLineHelper
.getEnvVars(workDir, configEnvironment)
.fold(
onSuccess = { envVars -> envVars },
onFailure = {
if (it !is MiseCommandLineNotFoundException) {
MiseNotificationServiceUtils.notifyException("Failed to load environment variables", it)
}
emptyMap()
},
)

params.env = params.env + envVars

// Gradle support (and external system configuration)
// When user double-clicks the Task in the Gradle tool window.
if (configuration is ExternalSystemRunConfiguration) {
runningProcessEnvs[configuration.project] = configuration.settings.env.toMap()
configuration.settings.env = configuration.settings.env + envVars
}
}

val envVars = MiseCommandLineHelper.getEnvVars(workDir, configEnvironment)
.fold(
onSuccess = { envVars -> envVars },
onFailure = {
if (it !is MiseCommandLineNotFoundException) {
MiseNotificationServiceUtils.notifyException("Failed to load environment variables", it)
override fun attachToProcess(
configuration: RunConfigurationBase<*>,
handler: ProcessHandler,
runnerSettings: RunnerSettings?,
) {
if (configuration is ExternalSystemRunConfiguration) {
val envsToRestore = runningProcessEnvs.remove(configuration.project) ?: return

handler.addProcessListener(
object : ProcessListener {
override fun processTerminated(event: ProcessEvent) {
configuration.settings.env.apply {
clear()
putAll(envsToRestore)
}
}
emptyMap()

override fun startNotified(event: ProcessEvent) {}

override fun onTextAvailable(
event: ProcessEvent,
outputType: Key<*>,
) {}
},
)

params.env.putAll(envVars)
}
}

override fun isApplicableFor(configuration: RunConfigurationBase<*>): Boolean = true
Expand Down

0 comments on commit d682610

Please sign in to comment.