From c09b426a9df86151355cb774a15df45891731ab3 Mon Sep 17 00:00:00 2001 From: bugale Date: Sun, 14 Jun 2026 11:58:58 +0300 Subject: [PATCH] Fix Windows container PATH dropped after a $GITHUB_PATH write On Windows, docker inspect .Config.Env rarely contains the container's PATH (servercore/nanoserver and most images don't declare it), so ParsePathFromConfigEnv returns empty. The later docker exec -e PATH= override then replaces the container PATH with only the prepended dirs -- System32 and PowerShell vanish and the next step's shell can't launch (hcs::CreateProcess 0x2, exit 126/127). PATH segments were also joined with a POSIX ':' separator. Read the container's real PATH at startup when .Config.Env has none, and join with Path.PathSeparator. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Runner.Worker/ContainerOperationProvider.cs | 8 ++++++++ src/Runner.Worker/Handlers/StepHost.cs | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Runner.Worker/ContainerOperationProvider.cs b/src/Runner.Worker/ContainerOperationProvider.cs index 3b061e8f8b1..600313db205 100644 --- a/src/Runner.Worker/ContainerOperationProvider.cs +++ b/src/Runner.Worker/ContainerOperationProvider.cs @@ -285,6 +285,14 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta var configEnvFormat = "--format \"{{range .Config.Env}}{{println .}}{{end}}\""; var containerEnv = await _dockerManager.DockerInspect(executionContext, container.ContainerId, configEnvFormat); container.ContainerRuntimePath = DockerUtil.ParsePathFromConfigEnv(containerEnv); + if (string.IsNullOrEmpty(container.ContainerRuntimePath) && Constants.Runner.Platform == Constants.OSPlatform.Windows) + { + // Windows images usually don't declare PATH in .Config.Env, so read the container's + // real PATH; otherwise a GITHUB_PATH write clobbers it via `docker exec -e PATH=`. + var runtimePath = new List(); + await _dockerManager.DockerExec(executionContext, container.ContainerId, string.Empty, "cmd /c echo %PATH%", runtimePath); + container.ContainerRuntimePath = string.Join("", runtimePath).Trim(); + } executionContext.JobContext.Container["id"] = new StringContextData(container.ContainerId); } executionContext.Output("##[endgroup]"); diff --git a/src/Runner.Worker/Handlers/StepHost.cs b/src/Runner.Worker/Handlers/StepHost.cs index 91f3154626c..afecfc9503d 100644 --- a/src/Runner.Worker/Handlers/StepHost.cs +++ b/src/Runner.Worker/Handlers/StepHost.cs @@ -250,7 +250,7 @@ await containerHookManager.RunScriptStepAsync(context, if (!string.IsNullOrEmpty(PrependPath)) { // Prepend tool paths to container's PATH - var fullPath = !string.IsNullOrEmpty(Container.ContainerRuntimePath) ? $"{PrependPath}:{Container.ContainerRuntimePath}" : PrependPath; + var fullPath = !string.IsNullOrEmpty(Container.ContainerRuntimePath) ? $"{PrependPath}{System.IO.Path.PathSeparator}{Container.ContainerRuntimePath}" : PrependPath; dockerCommandArgs.Add($"-e PATH=\"{fullPath}\""); }