Skip to content

Commit 0854386

Browse files
Merge branch 'master' into users/rishabhmalikMS/NodehandlerStrategies
2 parents 68d2621 + 23826db commit 0854386

File tree

9 files changed

+188
-20
lines changed

9 files changed

+188
-20
lines changed

.vsts.ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extends:
6161
publishArtifacts: ${{ ne(variables['Build.Reason'], 'PullRequest') }}
6262
buildAlternatePackage: false
6363
testProxyAgent: ${{ parameters.testProxyAgent }}
64+
targetFramework: 'net8.0'
6465
win_x64: ${{ parameters.win_x64 }}
6566
win_x86: ${{ parameters.win_x86 }}
6667
win_arm64: ${{ parameters.win_arm64 }}

src/Agent.Plugins/GitSourceProvider.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,35 @@ public async Task GetSourceAsync(
618618
}
619619
}
620620

621+
string agentWorkFolder = executionContext.Variables.GetValueOrDefault("agent.workfolder")?.Value;
622+
if (!string.IsNullOrEmpty(agentWorkFolder)
623+
&& File.Exists(Path.Combine(agentWorkFolder, ".autoManagedVhd"))
624+
&& !AgentKnobs.DisableAutoManagedVhdShallowOverride.GetValue(executionContext).AsBoolean())
625+
{
626+
// The existing working directory comes from an AutoManagedVHD (indicated by the
627+
// .autoManagedVhd marker file placed in the agent work folder).
628+
// An AutoManagedVHD always contains a full, non-shallow clone of the repository.
629+
// Some pipelines enable shallow fetch parameters (e.g., fetchDepth > 0). However,
630+
// Git cannot convert an existing full clone into a shallow one in-place.
631+
//
632+
// Technical reason:
633+
// A full clone already has complete commit history and object reachability.
634+
// When a fetch is issued with a non-zero --depth against a full clone, Git
635+
// does *not* rewrite the local history to match the requested shallow boundary.
636+
// Instead, to honor the depth constraint, Git falls back to creating a brand-new
637+
// shallow clone in an empty directory.
638+
//
639+
// That behavior causes the agent to discard the VHD-provided clone and re-clone
640+
// from scratch—defeating the whole purpose of using AutoManagedVHDs for fast sync.
641+
//
642+
// To avoid this, force a normal full fetch by disabling shallow behavior:
643+
// clean = false → preserve the VHD clone
644+
// fetchDepth = 0 → perform a standard "sync" fetch
645+
clean = false;
646+
fetchDepth = 0;
647+
executionContext.Output($"Detected an Auto Managed VHD at {targetPath}. Setting clean to false and fetchDepth to 0.");
648+
}
649+
621650
// When repo.clean is selected for a git repo, execute git clean -ffdx and git reset --hard HEAD on the current repo.
622651
// This will help us save the time to reclone the entire repo.
623652
// If any git commands exit with non-zero return code or any exception happened during git.exe invoke, fall back to delete the repo folder.

src/Agent.Sdk/Knob/AgentKnobs.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,13 @@ public class AgentKnobs
231231
new EnvironmentKnobSource("VSTS_FETCHBYCOMMITFORFULLCLONE"),
232232
new BuiltInDefaultKnobSource("false"));
233233

234+
public static readonly Knob DisableAutoManagedVhdShallowOverride = new Knob(
235+
nameof(DisableAutoManagedVhdShallowOverride),
236+
"If true, the agent will NOT override shallow-fetch settings when an AutoManagedVHD full clone is detected.",
237+
new RuntimeKnobSource("VSTS.DisableAutoManagedVhdShallowOverride"),
238+
new EnvironmentKnobSource("VSTS_DISABLEAUTOMANAGEDVHD_SHALLOW_OVERRIDE"),
239+
new BuiltInDefaultKnobSource("false"));
240+
234241
// Agent logging
235242
public static readonly Knob AgentPerflog = new Knob(
236243
nameof(AgentPerflog),
@@ -738,8 +745,9 @@ public class AgentKnobs
738745
public static readonly Knob UseNode24ToStartContainer = new Knob(
739746
nameof(UseNode24ToStartContainer),
740747
"If true, try to start container job using Node24, then fallback to Node20, then Node16.",
741-
new RuntimeKnobSource("AZP_AGENT_USE_NODE24_TO_START_CONTAINER"),
742748
new PipelineFeatureSource("UseNode24ToStartContainer"),
749+
new RuntimeKnobSource("AZP_AGENT_USE_NODE24_TO_START_CONTAINER"),
750+
new EnvironmentKnobSource("AZP_AGENT_USE_NODE24_TO_START_CONTAINER"),
743751
new BuiltInDefaultKnobSource("false"));
744752

745753
public static readonly Knob EnableNewMaskerAndRegexes = new Knob(

src/Agent.Worker/ContainerOperationProvider.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -648,21 +648,30 @@ string useDoubleQuotes(string value)
648648
}
649649
else if (logLine.Contains(labelContainerStartupUsingNode20))
650650
{
651-
executionContext.Debug("Using Node 20 for container startup.");
651+
string warningMsg = useNode24ToStartContainer
652+
? "Cannot run Node 24 in container. Falling back to Node 20 for container startup."
653+
: "Using Node 20 for container startup.";
654+
executionContext.Warning(warningMsg);
652655
containerStartupCompleted = true;
653656
container.ResultNodePath = node20ContainerPath;
654657
break;
655658
}
656659
else if (logLine.Contains(labelContainerStartupUsingNode16))
657660
{
658-
executionContext.Warning("Can not run Node 20 in container. Falling back to Node 16 for container startup.");
661+
string warningMsg = useNode24ToStartContainer
662+
? "Cannot run Node 24 and Node 20 in container. Falling back to Node 16 for container startup."
663+
: "Cannot run Node 20 in container. Falling back to Node 16 for container startup.";
664+
executionContext.Warning(warningMsg);
659665
containerStartupCompleted = true;
660666
container.ResultNodePath = node16ContainerPath;
661667
break;
662668
}
663669
else if (logLine.Contains(labelContainerStartupFailed))
664670
{
665-
executionContext.Error("Can not run both Node 20 and Node 16 in container. Container startup failed.");
671+
string errorMsg = useNode24ToStartContainer
672+
? "Cannot run Node 24, Node 20, and Node 16 in container. Container startup failed."
673+
: "Cannot run both Node 20 and Node 16 in container. Container startup failed.";
674+
executionContext.Error(errorMsg);
666675
containerStartupCompleted = true;
667676
break;
668677
}

src/Agent.Worker/ContainerOperationProviderEnhanced.cs

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -648,8 +648,10 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta
648648
trace.Info($"Configured {container.MountVolumes.Count} volume mounts: {string.Join(", ", mountSummary)}");
649649

650650
bool useNode20ToStartContainer = AgentKnobs.UseNode20ToStartContainer.GetValue(executionContext).AsBoolean();
651+
bool useNode24ToStartContainer = AgentKnobs.UseNode24ToStartContainer.GetValue(executionContext).AsBoolean();
651652
bool useAgentNode = false;
652653

654+
string labelContainerStartupUsingNode24 = "container-startup-using-node-24";
653655
string labelContainerStartupUsingNode20 = "container-startup-using-node-20";
654656
string labelContainerStartupUsingNode16 = "container-startup-using-node-16";
655657
string labelContainerStartupFailed = "container-startup-failed";
@@ -662,6 +664,7 @@ string containerNodePath(string nodeFolder)
662664
string nodeContainerPath = containerNodePath(NodeHandler.NodeFolder);
663665
string node16ContainerPath = containerNodePath(NodeHandler.Node16Folder);
664666
string node20ContainerPath = containerNodePath(NodeHandler.Node20_1Folder);
667+
string node24ContainerPath = containerNodePath(NodeHandler.Node24Folder);
665668

666669
if (container.IsJobContainer)
667670
{
@@ -697,9 +700,22 @@ string useDoubleQuotes(string value)
697700
else
698701
{
699702
useAgentNode = true;
700-
trace.Info($"Using agent-provided Node.js. Node20 enabled: {useNode20ToStartContainer}");
701-
trace.Info($"Node paths - Default: {nodeContainerPath}, Node16: {node16ContainerPath}, Node20: {node20ContainerPath}");
702-
string sleepCommand = useNode20ToStartContainer ? $"'{node20ContainerPath}' --version && echo '{labelContainerStartupUsingNode20}' && {nodeSetInterval(node20ContainerPath)} || '{node16ContainerPath}' --version && echo '{labelContainerStartupUsingNode16}' && {nodeSetInterval(node16ContainerPath)} || echo '{labelContainerStartupFailed}'" : nodeSetInterval(nodeContainerPath);
703+
trace.Info($"Using agent-provided Node.js. Node20 enabled: {useNode20ToStartContainer}, Node24 enabled: {useNode24ToStartContainer}");
704+
trace.Info($"Node paths - Default: {nodeContainerPath}, Node16: {node16ContainerPath}, Node20: {node20ContainerPath}, Node24: {node24ContainerPath}");
705+
string sleepCommand;
706+
707+
if (useNode24ToStartContainer)
708+
{
709+
sleepCommand = $"'{node24ContainerPath}' --version && echo '{labelContainerStartupUsingNode24}' && {nodeSetInterval(node24ContainerPath)} || '{node20ContainerPath}' --version && echo '{labelContainerStartupUsingNode20}' && {nodeSetInterval(node20ContainerPath)} || '{node16ContainerPath}' --version && echo '{labelContainerStartupUsingNode16}' && {nodeSetInterval(node16ContainerPath)} || echo '{labelContainerStartupFailed}'";
710+
}
711+
else if (useNode20ToStartContainer)
712+
{
713+
sleepCommand = $"'{node20ContainerPath}' --version && echo '{labelContainerStartupUsingNode20}' && {nodeSetInterval(node20ContainerPath)} || '{node16ContainerPath}' --version && echo '{labelContainerStartupUsingNode16}' && {nodeSetInterval(node16ContainerPath)} || echo '{labelContainerStartupFailed}'";
714+
}
715+
else
716+
{
717+
sleepCommand = nodeSetInterval(nodeContainerPath);
718+
}
703719
container.ContainerCommand = PlatformUtil.RunningOnWindows ? $"cmd.exe /c call {useDoubleQuotes(sleepCommand)}" : $"bash -c \"{sleepCommand}\"";
704720
container.ResultNodePath = nodeContainerPath;
705721
}
@@ -762,7 +778,7 @@ string useDoubleQuotes(string value)
762778

763779
executionContext.Warning($"Docker container {container.ContainerId} is not in running state.");
764780
}
765-
else if (useAgentNode && useNode20ToStartContainer)
781+
else if (useAgentNode && (useNode20ToStartContainer || useNode24ToStartContainer))
766782
{
767783
bool containerStartupCompleted = false;
768784
int containerStartupTimeoutInMilliseconds = 10000;
@@ -777,23 +793,39 @@ string useDoubleQuotes(string value)
777793

778794
foreach (string logLine in containerLogs)
779795
{
780-
if (logLine.Contains(labelContainerStartupUsingNode20))
796+
if (logLine.Contains(labelContainerStartupUsingNode24))
781797
{
782-
executionContext.Debug("Using Node 20 for container startup.");
798+
executionContext.Debug("Using Node 24 for container startup.");
799+
containerStartupCompleted = true;
800+
container.ResultNodePath = node24ContainerPath;
801+
break;
802+
}
803+
else if (logLine.Contains(labelContainerStartupUsingNode20))
804+
{
805+
string warningMsg = useNode24ToStartContainer
806+
? "Cannot run Node 24 in container. Falling back to Node 20 for container startup."
807+
: "Using Node 20 for container startup.";
808+
executionContext.Warning(warningMsg);
783809
containerStartupCompleted = true;
784810
container.ResultNodePath = node20ContainerPath;
785811
break;
786812
}
787813
else if (logLine.Contains(labelContainerStartupUsingNode16))
788814
{
789-
executionContext.Warning("Can not run Node 20 in container. Falling back to Node 16 for container startup.");
815+
string warningMsg = useNode24ToStartContainer
816+
? "Cannot run Node 24 and Node 20 in container. Falling back to Node 16 for container startup."
817+
: "Cannot run Node 20 in container. Falling back to Node 16 for container startup.";
818+
executionContext.Warning(warningMsg);
790819
containerStartupCompleted = true;
791820
container.ResultNodePath = node16ContainerPath;
792821
break;
793822
}
794823
else if (logLine.Contains(labelContainerStartupFailed))
795824
{
796-
executionContext.Error("Can not run both Node 20 and Node 16 in container. Container startup failed.");
825+
string errorMsg = useNode24ToStartContainer
826+
? "Cannot run Node 24, Node 20, and Node 16 in container. Container startup failed."
827+
: "Cannot run both Node 20 and Node 16 in container. Container startup failed.";
828+
executionContext.Error(errorMsg);
797829
containerStartupCompleted = true;
798830
break;
799831
}
@@ -1114,8 +1146,30 @@ string useDoubleQuotes(string value)
11141146
if (PlatformUtil.RunningOnLinux)
11151147
{
11161148
bool useNode20InUnsupportedSystem = AgentKnobs.UseNode20InUnsupportedSystem.GetValue(executionContext).AsBoolean();
1149+
bool useNode24InUnsupportedSystem = AgentKnobs.UseNode24InUnsupportedSystem.GetValue(executionContext).AsBoolean();
1150+
1151+
if (!useNode24InUnsupportedSystem)
1152+
{
1153+
var node24 = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node24Folder, "bin", $"node{IOUtil.ExeExtension}"));
1154+
1155+
string node24TestCmd = $"bash -c \"{node24} -v\"";
1156+
List<string> node24VersionOutput = await DockerExec(executionContext, container.ContainerId, node24TestCmd, noExceptionOnError: true);
1157+
1158+
container.NeedsNode20Redirect = WorkerUtilities.IsCommandResultGlibcError(executionContext, node24VersionOutput, out string node24InfoLine);
11171159

1118-
if (!useNode20InUnsupportedSystem)
1160+
if (container.NeedsNode20Redirect)
1161+
{
1162+
PublishTelemetry(
1163+
executionContext,
1164+
new Dictionary<string, string>
1165+
{
1166+
{ "ContainerNode24to20Fallback", container.NeedsNode20Redirect.ToString() }
1167+
}
1168+
);
1169+
}
1170+
}
1171+
1172+
if (!useNode20InUnsupportedSystem && (useNode24InUnsupportedSystem || container.NeedsNode20Redirect))
11191173
{
11201174
var node20 = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node20_1Folder, "bin", $"node{IOUtil.ExeExtension}"));
11211175

@@ -1130,12 +1184,24 @@ string useDoubleQuotes(string value)
11301184
executionContext,
11311185
new Dictionary<string, string>
11321186
{
1133-
{ "ContainerNode20to16Fallback", container.NeedsNode16Redirect.ToString() }
1187+
{ "ContainerNode20to16Fallback", container.NeedsNode16Redirect.ToString() }
11341188
}
11351189
);
11361190
}
11371191
}
11381192

1193+
if (!container.NeedsNode20Redirect)
1194+
{
1195+
container.ResultNodePath = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node24Folder, "bin", $"node{IOUtil.ExeExtension}"));
1196+
}
1197+
else if (!container.NeedsNode16Redirect)
1198+
{
1199+
container.ResultNodePath = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node20_1Folder, "bin", $"node{IOUtil.ExeExtension}"));
1200+
}
1201+
else
1202+
{
1203+
container.ResultNodePath = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node16Folder, "bin", $"node{IOUtil.ExeExtension}"));
1204+
}
11391205
}
11401206

11411207
if (!string.IsNullOrEmpty(containerUserName))

src/Misc/layoutbin/powershell/Add-MSBuildCapabilities.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ Get-MSBuildCapabilities -MajorVersion 16
6565

6666
Get-MSBuildCapabilities -MajorVersion 17
6767

68+
Get-MSBuildCapabilities -MajorVersion 18
69+
6870
# Add 64-bit.
6971
$latest = $null
7072
$null = Add-CapabilityFromRegistry -Name "MSBuild_2.0_x64" -Hive 'LocalMachine' -View 'Registry64' -KeyName $keyName20 -ValueName 'MSBuildToolsPath' -Value ([ref]$latest)
@@ -85,3 +87,5 @@ if ($vs15 -and $vs15.installationPath) {
8587
Get-MSBuildCapabilities -MajorVersion 16 -Add_x64
8688

8789
Get-MSBuildCapabilities -MajorVersion 17 -Add_x64
90+
91+
Get-MSBuildCapabilities -MajorVersion 18 -Add_x64

src/Misc/layoutbin/powershell/Add-VisualStudioCapabilities.ps1

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ function Add-TestCapability {
3030
function Get-VSCapabilities {
3131
param (
3232
[Parameter(Mandatory = $true)]
33-
[ValidateSet(15, 16, 17)]
33+
[ValidateSet(15, 16, 17, 18)]
3434
[int]$MajorVersion,
3535

3636
[Parameter(Mandatory = $true)]
@@ -87,6 +87,7 @@ $keyName14 = 'Software\Microsoft\VisualStudio\14.0'
8787
$keyName15 = 'Software\Microsoft\VisualStudio\15.0'
8888
$keyName16 = 'Software\Microsoft\VisualStudio\16.0'
8989
$keyName17 = 'Software\Microsoft\VisualStudio\17.0'
90+
$keyName18 = 'Software\Microsoft\VisualStudio\18.0'
9091

9192
# Add the capabilities.
9293
$latestVS = $null
@@ -111,3 +112,5 @@ Get-VSCapabilities -MajorVersion 15 -keyName $keyName15
111112
Get-VSCapabilities -MajorVersion 16 -keyName $keyName16
112113

113114
Get-VSCapabilities -MajorVersion 17 -keyName $keyName17
115+
116+
Get-VSCapabilities -MajorVersion 18 -keyName $keyName18

0 commit comments

Comments
 (0)