Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Agent.Sdk/Knob/AgentKnobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ public class AgentKnobs
new EnvironmentKnobSource("AGENT_USE_NODE16"),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob UseNode20 = new Knob(
nameof(UseNode20),
"Forces the agent to use Node 20 handler for all Node-based tasks",
new RuntimeKnobSource("AGENT_USE_NODE20"),
new EnvironmentKnobSource("AGENT_USE_NODE20"),
new BuiltInDefaultKnobSource("false"));

// Agent logging
public static readonly Knob AgentPerflog = new Knob(
nameof(AgentPerflog),
Expand Down
20 changes: 17 additions & 3 deletions src/Agent.Worker/Handlers/NodeHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace Microsoft.VisualStudio.Services.Agent.Worker.Handlers
[ServiceLocator(Default = typeof(NodeHandler))]
public interface INodeHandler : IHandler
{
// Data can be of these three types: NodeHandlerData, Node10HandlerData and Node16HandlerData
// Data can be of these four types: NodeHandlerData, Node10HandlerData, Node16HandlerData and Node20HandlerData
BaseNodeHandlerData Data { get; set; }
}

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

public string GetNodeLocation()
{
bool useNode16 = AgentKnobs.UseNode16.GetValue(ExecutionContext).AsBoolean();
bool useNode10 = AgentKnobs.UseNode10.GetValue(ExecutionContext).AsBoolean();
bool useNode16 = AgentKnobs.UseNode16.GetValue(ExecutionContext).AsBoolean();
bool useNode20 = AgentKnobs.UseNode20.GetValue(ExecutionContext).AsBoolean();
bool taskHasNode10Data = Data is Node10HandlerData;
bool taskHasNode16Data = Data is Node16HandlerData;
bool taskHasNode20Data = Data is Node20HandlerData;
string useNodeKnob = AgentKnobs.UseNode.GetValue(ExecutionContext).AsString();

string nodeFolder = NodeHandler.nodeFolder;
Expand All @@ -227,6 +230,11 @@ public string GetNodeLocation()
Trace.Info($"Detected RedHat 6, using node 10 as execution handler, instead node16");
nodeFolder = NodeHandler.node10Folder;
}
else if (taskHasNode20Data)
{
Trace.Info($"Task.json has node20 handler data: {taskHasNode20Data}");
nodeFolder = NodeHandler.node20Folder;
}
else if (taskHasNode16Data)
{
Trace.Info($"Task.json has node16 handler data: {taskHasNode16Data}");
Expand All @@ -238,6 +246,12 @@ public string GetNodeLocation()
nodeFolder = NodeHandler.node10Folder;
}

if (useNode20)
{
Trace.Info($"Found UseNode20 knob, using node20 for node tasks {useNode20}");
nodeFolder = NodeHandler.node20Folder;
}

if (useNode16)
{
Trace.Info($"Found UseNode16 knob, using node16 for node tasks {useNode16}");
Expand Down
33 changes: 26 additions & 7 deletions src/Agent.Worker/TaskManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ public sealed class ExecutionData
private NodeHandlerData _node;
private Node10HandlerData _node10;
private Node16HandlerData _node16;
private Node20HandlerData _node20;
private PowerShellHandlerData _powerShell;
private PowerShell3HandlerData _powerShell3;
private PowerShellExeHandlerData _powerShellExe;
Expand Down Expand Up @@ -450,6 +451,20 @@ public Node16HandlerData Node16
}
}

public Node20HandlerData Node20
{
get
{
return _node20;
}

set
{
_node20 = value;
Add(value);
}
}

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public PowerShellHandlerData PowerShell
{
Expand Down Expand Up @@ -624,21 +639,25 @@ public string WorkingDirectory

public sealed class NodeHandlerData : BaseNodeHandlerData
{
public override int Priority => 3;
public override int Priority => 4;
}

public sealed class Node10HandlerData : BaseNodeHandlerData
{
public override int Priority => 2;
public override int Priority => 3;
}
public sealed class Node16HandlerData : BaseNodeHandlerData
{
public override int Priority => 2;
}
public sealed class Node20HandlerData : BaseNodeHandlerData
{
public override int Priority => 1;
}

public sealed class PowerShell3HandlerData : HandlerData
{
public override int Priority => 4;
public override int Priority => 5;
}

public sealed class PowerShellHandlerData : HandlerData
Expand All @@ -656,7 +675,7 @@ public string ArgumentFormat
}
}

public override int Priority => 5;
public override int Priority => 6;

public string WorkingDirectory
{
Expand Down Expand Up @@ -687,7 +706,7 @@ public string ArgumentFormat
}
}

public override int Priority => 6;
public override int Priority => 7;

public string WorkingDirectory
{
Expand Down Expand Up @@ -744,7 +763,7 @@ public string InlineScript
}
}

public override int Priority => 6;
public override int Priority => 7;

public string ScriptType
{
Expand Down Expand Up @@ -801,7 +820,7 @@ public string ModifyEnvironment
}
}

public override int Priority => 7;
public override int Priority => 8;

public string WorkingDirectory
{
Expand Down
15 changes: 15 additions & 0 deletions src/Misc/externals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ NODE_URL=https://nodejs.org/dist
NODE_VERSION="6.17.1"
NODE10_VERSION="10.24.1"
NODE16_VERSION="16.20.0"
NODE20_VERSION="20.3.1"
MINGIT_VERSION="2.39.1"
LFS_VERSION="3.3.0"

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

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

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


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

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

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

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

if [[ "$PACKAGERUNTIME" != "win-x64" && "$PACKAGERUNTIME" != "win-x86" ]]; then
Expand All @@ -244,6 +254,11 @@ if [[ "$PACKAGERUNTIME" != "win-x64" && "$PACKAGERUNTIME" != "win-x86" ]]; then
rm "$LAYOUT_DIR/externals/node16/bin/npm"
rm "$LAYOUT_DIR/externals/node16/bin/npx"
rm "$LAYOUT_DIR/externals/node16/bin/corepack"

rm -rf "$LAYOUT_DIR/externals/node20/lib"
rm "$LAYOUT_DIR/externals/node20/bin/npm"
rm "$LAYOUT_DIR/externals/node20/bin/npx"
rm "$LAYOUT_DIR/externals/node20/bin/corepack"
fi

if [[ "$PACKAGERUNTIME" != "win-x64" && "$PACKAGERUNTIME" != "win-x86" ]]; then
Expand Down
9 changes: 8 additions & 1 deletion src/Test/L0/NodeHandlerL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public void UseNodeForNodeHandlerEnvVarNotSet()
[Theory]
[InlineData("node10")]
[InlineData("node16")]
[InlineData("node20")]
[Trait("Level", "L0")]
[Trait("Category", "Common")]
public void UseNewNodeForNewNodeHandler(string nodeVersion)
Expand All @@ -69,7 +70,13 @@ public void UseNewNodeForNewNodeHandler(string nodeVersion)

nodeHandler.Initialize(thc);
nodeHandler.ExecutionContext = CreateTestExecutionContext(thc);
nodeHandler.Data = nodeVersion == "node16" ? (BaseNodeHandlerData)new Node16HandlerData() : (BaseNodeHandlerData)new Node10HandlerData();
nodeHandler.Data = nodeVersion switch
{
"node10" => new Node10HandlerData(),
"node16" => new Node16HandlerData(),
"node20" => new Node20HandlerData(),
_ => throw new Exception("Invalid node version"),
};

string actualLocation = nodeHandler.GetNodeLocation();
// We should fall back to node10 for node16 tasks, since RHEL 6 is not capable with Node16.
Expand Down
15 changes: 12 additions & 3 deletions src/Test/L0/Worker/TaskManagerL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,10 @@ public void LoadsDefinition()
""target"": ""Some Node16 target"",
""extraNodeArg"": ""Extra node16 arg value""
},
""Node20"": {
""target"": ""Some Node20 target"",
""extraNodeArg"": ""Extra node20 arg value""
},
""Process"": {
""target"": ""Some process target"",
""argumentFormat"": ""Some process argument format"",
Expand Down Expand Up @@ -509,12 +513,12 @@ public void LoadsDefinition()
if (TestUtil.IsWindows())
{
// Process handler should only be deserialized on Windows.
Assert.Equal(4, definition.Data.Execution.All.Count);
Assert.Equal(5, definition.Data.Execution.All.Count);
}
else
{
// Only the Node handlers should be deserialized on non-Windows.
Assert.Equal(3, definition.Data.Execution.All.Count);
Assert.Equal(4, definition.Data.Execution.All.Count);
}

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

// Node20 handler should always be deserialized.
Assert.NotNull(definition.Data.Execution.Node20); // execution.Node20
Assert.Equal(definition.Data.Execution.Node20, definition.Data.Execution.All[3]);
Assert.Equal("Some Node20 target", definition.Data.Execution.Node20.Target);

if (TestUtil.IsWindows())
{
// Process handler should only be deserialized on Windows.
Assert.NotNull(definition.Data.Execution.Process); // execution.Process
Assert.Equal(definition.Data.Execution.Process, definition.Data.Execution.All[3]);
Assert.Equal(definition.Data.Execution.Process, definition.Data.Execution.All[4]);
Assert.Equal("Some process argument format", definition.Data.Execution.Process.ArgumentFormat);
Assert.NotNull(definition.Data.Execution.Process.Platforms);
Assert.Equal(1, definition.Data.Execution.Process.Platforms.Length);
Expand Down