Skip to content

Commit d7e0704

Browse files
authored
Support Node 20 handler (#4346)
* add node 20 handler * update unit tests
1 parent 4978a18 commit d7e0704

File tree

6 files changed

+85
-14
lines changed

6 files changed

+85
-14
lines changed

src/Agent.Sdk/Knob/AgentKnobs.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,13 @@ public class AgentKnobs
152152
new EnvironmentKnobSource("AGENT_USE_NODE16"),
153153
new BuiltInDefaultKnobSource("false"));
154154

155+
public static readonly Knob UseNode20 = new Knob(
156+
nameof(UseNode20),
157+
"Forces the agent to use Node 20 handler for all Node-based tasks",
158+
new RuntimeKnobSource("AGENT_USE_NODE20"),
159+
new EnvironmentKnobSource("AGENT_USE_NODE20"),
160+
new BuiltInDefaultKnobSource("false"));
161+
155162
// Agent logging
156163
public static readonly Knob AgentPerflog = new Knob(
157164
nameof(AgentPerflog),

src/Agent.Worker/Handlers/NodeHandler.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.Services.Agent.Worker.Handlers
1818
[ServiceLocator(Default = typeof(NodeHandler))]
1919
public interface INodeHandler : IHandler
2020
{
21-
// Data can be of these three types: NodeHandlerData, Node10HandlerData and Node16HandlerData
21+
// Data can be of these four types: NodeHandlerData, Node10HandlerData, Node16HandlerData and Node20HandlerData
2222
BaseNodeHandlerData Data { get; set; }
2323
}
2424

@@ -56,10 +56,11 @@ public sealed class NodeHandler : Handler, INodeHandler
5656
private const string nodeFolder = "node";
5757
private const string node10Folder = "node10";
5858
private const string node16Folder = "node16";
59+
private const string node20Folder = "node20";
5960
private const string nodeLTS = node16Folder;
6061
private const string useNodeKnobLtsKey = "LTS";
6162
private const string useNodeKnobUpgradeKey = "UPGRADE";
62-
private string[] possibleNodeFolders = { nodeFolder, node10Folder, node16Folder };
63+
private string[] possibleNodeFolders = { nodeFolder, node10Folder, node16Folder, node20Folder };
6364
private static Regex _vstsTaskLibVersionNeedsFix = new Regex("^[0-2]\\.[0-9]+", RegexOptions.Compiled | RegexOptions.IgnoreCase);
6465
private static string[] _extensionsNode6 ={
6566
"if (process.versions.node && process.versions.node.match(/^5\\./)) {",
@@ -215,10 +216,12 @@ public async Task RunAsync()
215216

216217
public string GetNodeLocation()
217218
{
218-
bool useNode16 = AgentKnobs.UseNode16.GetValue(ExecutionContext).AsBoolean();
219219
bool useNode10 = AgentKnobs.UseNode10.GetValue(ExecutionContext).AsBoolean();
220+
bool useNode16 = AgentKnobs.UseNode16.GetValue(ExecutionContext).AsBoolean();
221+
bool useNode20 = AgentKnobs.UseNode20.GetValue(ExecutionContext).AsBoolean();
220222
bool taskHasNode10Data = Data is Node10HandlerData;
221223
bool taskHasNode16Data = Data is Node16HandlerData;
224+
bool taskHasNode20Data = Data is Node20HandlerData;
222225
string useNodeKnob = AgentKnobs.UseNode.GetValue(ExecutionContext).AsString();
223226

224227
string nodeFolder = NodeHandler.nodeFolder;
@@ -227,6 +230,11 @@ public string GetNodeLocation()
227230
Trace.Info($"Detected RedHat 6, using node 10 as execution handler, instead node16");
228231
nodeFolder = NodeHandler.node10Folder;
229232
}
233+
else if (taskHasNode20Data)
234+
{
235+
Trace.Info($"Task.json has node20 handler data: {taskHasNode20Data}");
236+
nodeFolder = NodeHandler.node20Folder;
237+
}
230238
else if (taskHasNode16Data)
231239
{
232240
Trace.Info($"Task.json has node16 handler data: {taskHasNode16Data}");
@@ -238,6 +246,12 @@ public string GetNodeLocation()
238246
nodeFolder = NodeHandler.node10Folder;
239247
}
240248

249+
if (useNode20)
250+
{
251+
Trace.Info($"Found UseNode20 knob, using node20 for node tasks {useNode20}");
252+
nodeFolder = NodeHandler.node20Folder;
253+
}
254+
241255
if (useNode16)
242256
{
243257
Trace.Info($"Found UseNode16 knob, using node16 for node tasks {useNode16}");

src/Agent.Worker/TaskManager.cs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,7 @@ public sealed class ExecutionData
381381
private NodeHandlerData _node;
382382
private Node10HandlerData _node10;
383383
private Node16HandlerData _node16;
384+
private Node20HandlerData _node20;
384385
private PowerShellHandlerData _powerShell;
385386
private PowerShell3HandlerData _powerShell3;
386387
private PowerShellExeHandlerData _powerShellExe;
@@ -450,6 +451,20 @@ public Node16HandlerData Node16
450451
}
451452
}
452453

454+
public Node20HandlerData Node20
455+
{
456+
get
457+
{
458+
return _node20;
459+
}
460+
461+
set
462+
{
463+
_node20 = value;
464+
Add(value);
465+
}
466+
}
467+
453468
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
454469
public PowerShellHandlerData PowerShell
455470
{
@@ -624,21 +639,25 @@ public string WorkingDirectory
624639

625640
public sealed class NodeHandlerData : BaseNodeHandlerData
626641
{
627-
public override int Priority => 3;
642+
public override int Priority => 4;
628643
}
629644

630645
public sealed class Node10HandlerData : BaseNodeHandlerData
631646
{
632-
public override int Priority => 2;
647+
public override int Priority => 3;
633648
}
634649
public sealed class Node16HandlerData : BaseNodeHandlerData
650+
{
651+
public override int Priority => 2;
652+
}
653+
public sealed class Node20HandlerData : BaseNodeHandlerData
635654
{
636655
public override int Priority => 1;
637656
}
638657

639658
public sealed class PowerShell3HandlerData : HandlerData
640659
{
641-
public override int Priority => 4;
660+
public override int Priority => 5;
642661
}
643662

644663
public sealed class PowerShellHandlerData : HandlerData
@@ -656,7 +675,7 @@ public string ArgumentFormat
656675
}
657676
}
658677

659-
public override int Priority => 5;
678+
public override int Priority => 6;
660679

661680
public string WorkingDirectory
662681
{
@@ -687,7 +706,7 @@ public string ArgumentFormat
687706
}
688707
}
689708

690-
public override int Priority => 6;
709+
public override int Priority => 7;
691710

692711
public string WorkingDirectory
693712
{
@@ -744,7 +763,7 @@ public string InlineScript
744763
}
745764
}
746765

747-
public override int Priority => 6;
766+
public override int Priority => 7;
748767

749768
public string ScriptType
750769
{
@@ -801,7 +820,7 @@ public string ModifyEnvironment
801820
}
802821
}
803822

804-
public override int Priority => 7;
823+
public override int Priority => 8;
805824

806825
public string WorkingDirectory
807826
{

src/Misc/externals.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ NODE_URL=https://nodejs.org/dist
1111
NODE_VERSION="6.17.1"
1212
NODE10_VERSION="10.24.1"
1313
NODE16_VERSION="16.20.0"
14+
NODE20_VERSION="20.3.1"
1415
MINGIT_VERSION="2.39.1"
1516
LFS_VERSION="3.3.0"
1617

@@ -160,6 +161,8 @@ if [[ "$PACKAGERUNTIME" == "win-x64" ]]; then
160161
acquireExternalTool "$NODE_URL/v${NODE10_VERSION}/win-x64/node.lib" node10/bin
161162
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/win-x64/node.exe" node16/bin
162163
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/win-x64/node.lib" node16/bin
164+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/win-x64/node.exe" node20/bin
165+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/win-x64/node.lib" node20/bin
163166
acquireExternalTool "https://dist.nuget.org/win-x86-commandline/v3.4.4/nuget.exe" nuget
164167
fi
165168

@@ -178,6 +181,8 @@ if [[ "$PACKAGERUNTIME" == "win-x86" ]]; then
178181
acquireExternalTool "$NODE_URL/v${NODE10_VERSION}/win-x86/node.lib" node10/bin
179182
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/win-x86/node.exe" node16/bin
180183
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/win-x86/node.lib" node16/bin
184+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/win-x86/node.exe" node20/bin
185+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/win-x86/node.lib" node20/bin
181186
acquireExternalTool "https://dist.nuget.org/win-x86-commandline/v3.4.4/nuget.exe" nuget
182187
fi
183188

@@ -188,6 +193,7 @@ if [[ "$PACKAGERUNTIME" == "osx-x64" ]]; then
188193
fi
189194
acquireExternalTool "$NODE_URL/v${NODE10_VERSION}/node-v${NODE10_VERSION}-darwin-x64.tar.gz" node10 fix_nested_dir
190195
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-darwin-x64.tar.gz" node16 fix_nested_dir
196+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-darwin-x64.tar.gz" node20 fix_nested_dir
191197
fi
192198

193199

@@ -197,6 +203,7 @@ if [[ "$PACKAGERUNTIME" == "osx-arm64" ]]; then
197203
fi
198204
acquireExternalTool "$NODE_URL/v${NODE10_VERSION}/node-v${NODE10_VERSION}-darwin-x64.tar.gz" node10 fix_nested_dir
199205
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-darwin-arm64.tar.gz" node16 fix_nested_dir
206+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-darwin-arm64.tar.gz" node20 fix_nested_dir
200207
fi
201208

202209
# Download the external tools common across OSX and Linux PACKAGERUNTIMEs.
@@ -211,6 +218,7 @@ if [[ "$PACKAGERUNTIME" == "linux-x64" || "$PACKAGERUNTIME" == "rhel.7.2-x64" ]]
211218
fi
212219
acquireExternalTool "$NODE_URL/v${NODE10_VERSION}/node-v${NODE10_VERSION}-linux-x64.tar.gz" node10 fix_nested_dir
213220
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-x64.tar.gz" node16 fix_nested_dir
221+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-x64.tar.gz" node20 fix_nested_dir
214222
fi
215223

216224
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
@@ -219,6 +227,7 @@ if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
219227
fi
220228
acquireExternalTool "$NODE_URL/v${NODE10_VERSION}/node-v${NODE10_VERSION}-linux-armv7l.tar.gz" node10 fix_nested_dir
221229
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-armv7l.tar.gz" node16 fix_nested_dir
230+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-armv7l.tar.gz" node20 fix_nested_dir
222231
fi
223232

224233
if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
@@ -227,6 +236,7 @@ if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
227236
fi
228237
acquireExternalTool "$NODE_URL/v${NODE10_VERSION}/node-v${NODE10_VERSION}-linux-arm64.tar.gz" node10 fix_nested_dir
229238
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-arm64.tar.gz" node16 fix_nested_dir
239+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-arm64.tar.gz" node20 fix_nested_dir
230240
fi
231241

232242
if [[ "$PACKAGERUNTIME" != "win-x64" && "$PACKAGERUNTIME" != "win-x86" ]]; then
@@ -244,6 +254,11 @@ if [[ "$PACKAGERUNTIME" != "win-x64" && "$PACKAGERUNTIME" != "win-x86" ]]; then
244254
rm "$LAYOUT_DIR/externals/node16/bin/npm"
245255
rm "$LAYOUT_DIR/externals/node16/bin/npx"
246256
rm "$LAYOUT_DIR/externals/node16/bin/corepack"
257+
258+
rm -rf "$LAYOUT_DIR/externals/node20/lib"
259+
rm "$LAYOUT_DIR/externals/node20/bin/npm"
260+
rm "$LAYOUT_DIR/externals/node20/bin/npx"
261+
rm "$LAYOUT_DIR/externals/node20/bin/corepack"
247262
fi
248263

249264
if [[ "$PACKAGERUNTIME" != "win-x64" && "$PACKAGERUNTIME" != "win-x86" ]]; then

src/Test/L0/NodeHandlerL0.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public void UseNodeForNodeHandlerEnvVarNotSet()
5656
[Theory]
5757
[InlineData("node10")]
5858
[InlineData("node16")]
59+
[InlineData("node20")]
5960
[Trait("Level", "L0")]
6061
[Trait("Category", "Common")]
6162
public void UseNewNodeForNewNodeHandler(string nodeVersion)
@@ -69,7 +70,13 @@ public void UseNewNodeForNewNodeHandler(string nodeVersion)
6970

7071
nodeHandler.Initialize(thc);
7172
nodeHandler.ExecutionContext = CreateTestExecutionContext(thc);
72-
nodeHandler.Data = nodeVersion == "node16" ? (BaseNodeHandlerData)new Node16HandlerData() : (BaseNodeHandlerData)new Node10HandlerData();
73+
nodeHandler.Data = nodeVersion switch
74+
{
75+
"node10" => new Node10HandlerData(),
76+
"node16" => new Node16HandlerData(),
77+
"node20" => new Node20HandlerData(),
78+
_ => throw new Exception("Invalid node version"),
79+
};
7380

7481
string actualLocation = nodeHandler.GetNodeLocation();
7582
// We should fall back to node10 for node16 tasks, since RHEL 6 is not capable with Node16.

src/Test/L0/Worker/TaskManagerL0.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,10 @@ public void LoadsDefinition()
458458
""target"": ""Some Node16 target"",
459459
""extraNodeArg"": ""Extra node16 arg value""
460460
},
461+
""Node20"": {
462+
""target"": ""Some Node20 target"",
463+
""extraNodeArg"": ""Extra node20 arg value""
464+
},
461465
""Process"": {
462466
""target"": ""Some process target"",
463467
""argumentFormat"": ""Some process argument format"",
@@ -509,12 +513,12 @@ public void LoadsDefinition()
509513
if (TestUtil.IsWindows())
510514
{
511515
// Process handler should only be deserialized on Windows.
512-
Assert.Equal(4, definition.Data.Execution.All.Count);
516+
Assert.Equal(5, definition.Data.Execution.All.Count);
513517
}
514518
else
515519
{
516520
// Only the Node handlers should be deserialized on non-Windows.
517-
Assert.Equal(3, definition.Data.Execution.All.Count);
521+
Assert.Equal(4, definition.Data.Execution.All.Count);
518522
}
519523

520524
// Node handler should always be deserialized.
@@ -532,11 +536,16 @@ public void LoadsDefinition()
532536
Assert.Equal(definition.Data.Execution.Node16, definition.Data.Execution.All[2]);
533537
Assert.Equal("Some Node16 target", definition.Data.Execution.Node16.Target);
534538

539+
// Node20 handler should always be deserialized.
540+
Assert.NotNull(definition.Data.Execution.Node20); // execution.Node20
541+
Assert.Equal(definition.Data.Execution.Node20, definition.Data.Execution.All[3]);
542+
Assert.Equal("Some Node20 target", definition.Data.Execution.Node20.Target);
543+
535544
if (TestUtil.IsWindows())
536545
{
537546
// Process handler should only be deserialized on Windows.
538547
Assert.NotNull(definition.Data.Execution.Process); // execution.Process
539-
Assert.Equal(definition.Data.Execution.Process, definition.Data.Execution.All[3]);
548+
Assert.Equal(definition.Data.Execution.Process, definition.Data.Execution.All[4]);
540549
Assert.Equal("Some process argument format", definition.Data.Execution.Process.ArgumentFormat);
541550
Assert.NotNull(definition.Data.Execution.Process.Platforms);
542551
Assert.Equal(1, definition.Data.Execution.Process.Platforms.Length);

0 commit comments

Comments
 (0)