Skip to content

Commit 8206cf4

Browse files
authored
Add node20 to runner (#2732)
* . * fix hashes * fix linux-arm * linux-arm64 * osx-x64 * win-x64 * clean up node12 deprecation warning * Add node20 * Remove node12 from the BuildInVersions list * fix externals hash
1 parent 6680a3b commit 8206cf4

17 files changed

+212
-15
lines changed
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
209a3b07522d883b745945ec8b1bfa57c0bce769573c3e79206b62959686c0d3
1+
5bdddd32bab1e57af252b470579083049496e9e39b6e4f50de01232581f9a2d8
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1dd7654995ab54ce0d06519ad84c25029f0924f8313f0f5b7811c3631982a5e5
1+
54b3b3a72da93db0fa38708c759fceadddb70cacdd3620a079084a242126dd78
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
9a06b2be3c18a06d2e676774d8b6d2869854f23f982433e9346bc7bdcb08f362
1+
e7f2da271abb174285c3a757503538b3e9792e9d731b0382b6d1f21bb59a79ba
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4eb04e10e173b1f5d7cf8890878a3e178de162e5bf1a583067cf704b8162631a
1+
2481c5b0d06b2b5621635f2568b86a43b0e5b259fed1298167ba4f33d4c464c7
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
fc5efd1df9c75f01d7b6a43018ba49257bdc0d65e743ddd1ae753802a5ddb736
1+
85de7677165e65ec69b8a9e344c0811efa51b7fe5376a1aa083505c560ea6f57
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0064acac3fba3bd29851334c90f53adc13f88f2d23f5909f3bbec2b1c4ade9ef
1+
763d18de11c11fd299c0e75e98fefc8a0e6605ae0ad6aba3bbc110db2262ab41
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
e2fa2229ed5bc8857acdd264c2f3964454fd710c776efdfb5dc0b40b7a75065a
1+
16f3cc545dfe10e84df43746073fc64d3c44d1891782532805aeb2118869a55d

src/Misc/externals.sh

+11
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ PRECACHE=$2
55
NODE_URL=https://nodejs.org/dist
66
UNOFFICIAL_NODE_URL=https://unofficial-builds.nodejs.org/download/release
77
NODE16_VERSION="16.20.1"
8+
NODE20_VERSION="20.5.0"
89
# used only for win-arm64, remove node16 unofficial version when official version is available
910
NODE16_UNOFFICIAL_VERSION="16.20.0"
1011

@@ -140,6 +141,8 @@ function acquireExternalTool() {
140141
if [[ "$PACKAGERUNTIME" == "win-x64" || "$PACKAGERUNTIME" == "win-x86" ]]; then
141142
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/$PACKAGERUNTIME/node.exe" node16/bin
142143
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/$PACKAGERUNTIME/node.lib" node16/bin
144+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.exe" node20/bin
145+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.lib" node20/bin
143146
if [[ "$PRECACHE" != "" ]]; then
144147
acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere
145148
fi
@@ -150,6 +153,8 @@ if [[ "$PACKAGERUNTIME" == "win-arm64" ]]; then
150153
# todo: replace these with official release when available
151154
acquireExternalTool "$UNOFFICIAL_NODE_URL/v${NODE16_UNOFFICIAL_VERSION}/$PACKAGERUNTIME/node.exe" node16/bin
152155
acquireExternalTool "$UNOFFICIAL_NODE_URL/v${NODE16_UNOFFICIAL_VERSION}/$PACKAGERUNTIME/node.lib" node16/bin
156+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.exe" node20/bin
157+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/$PACKAGERUNTIME/node.lib" node20/bin
153158
if [[ "$PRECACHE" != "" ]]; then
154159
acquireExternalTool "https://github.com/microsoft/vswhere/releases/download/2.6.7/vswhere.exe" vswhere
155160
fi
@@ -158,23 +163,29 @@ fi
158163
# Download the external tools only for OSX.
159164
if [[ "$PACKAGERUNTIME" == "osx-x64" ]]; then
160165
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-darwin-x64.tar.gz" node16 fix_nested_dir
166+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-darwin-x64.tar.gz" node20 fix_nested_dir
161167
fi
162168

163169
if [[ "$PACKAGERUNTIME" == "osx-arm64" ]]; then
164170
# node.js v12 doesn't support macOS on arm64.
165171
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-darwin-arm64.tar.gz" node16 fix_nested_dir
172+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-darwin-arm64.tar.gz" node20 fix_nested_dir
166173
fi
167174

168175
# Download the external tools for Linux PACKAGERUNTIMEs.
169176
if [[ "$PACKAGERUNTIME" == "linux-x64" ]]; then
170177
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-x64.tar.gz" node16 fix_nested_dir
171178
acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/${NODE16_VERSION}/alpine/x64/node-v${NODE16_VERSION}-alpine-x64.tar.gz" node16_alpine
179+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-x64.tar.gz" node20 fix_nested_dir
180+
acquireExternalTool "https://vstsagenttools.blob.core.windows.net/tools/nodejs/${NODE20_VERSION}/alpine/x64/node-v${NODE20_VERSION}-alpine-x64.tar.gz" node20_alpine
172181
fi
173182

174183
if [[ "$PACKAGERUNTIME" == "linux-arm64" ]]; then
175184
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-arm64.tar.gz" node16 fix_nested_dir
185+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-arm64.tar.gz" node20 fix_nested_dir
176186
fi
177187

178188
if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
179189
acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-armv7l.tar.gz" node16 fix_nested_dir
190+
acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-armv7l.tar.gz" node20 fix_nested_dir
180191
fi

src/Runner.Common/Util/NodeUtil.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ namespace GitHub.Runner.Common.Util
66
public static class NodeUtil
77
{
88
private const string _defaultNodeVersion = "node16";
9-
public static readonly ReadOnlyCollection<string> BuiltInNodeVersions = new(new[] { "node16" });
10-
9+
public static readonly ReadOnlyCollection<string> BuiltInNodeVersions = new(new[] { "node16", "node20" });
1110
public static string GetInternalNodeVersion()
1211
{
1312
var forcedInternalNodeVersion = Environment.GetEnvironmentVariable(Constants.Variables.Agent.ForcedInternalNodeVersion);

src/Runner.Worker/ActionManifestManager.cs

+4-3
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,8 @@ private ActionExecutionData ConvertRuns(
449449
}
450450
}
451451
else if (string.Equals(usingToken.Value, "node12", StringComparison.OrdinalIgnoreCase) ||
452-
string.Equals(usingToken.Value, "node16", StringComparison.OrdinalIgnoreCase))
452+
string.Equals(usingToken.Value, "node16", StringComparison.OrdinalIgnoreCase) ||
453+
string.Equals(usingToken.Value, "node20", StringComparison.OrdinalIgnoreCase))
453454
{
454455
if (string.IsNullOrEmpty(mainToken?.Value))
455456
{
@@ -489,7 +490,7 @@ private ActionExecutionData ConvertRuns(
489490
}
490491
else
491492
{
492-
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker', 'node12' or 'node16' instead.");
493+
throw new ArgumentOutOfRangeException($"'using: {usingToken.Value}' is not supported, use 'docker', 'node12', 'node16' or 'node20' instead.");
493494
}
494495
}
495496
else if (pluginToken != null)
@@ -500,7 +501,7 @@ private ActionExecutionData ConvertRuns(
500501
};
501502
}
502503

503-
throw new NotSupportedException("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'.");
504+
throw new NotSupportedException("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12', 'node16' or 'node20'.");
504505
}
505506

506507
private void ConvertInputs(

src/Runner.Worker/Handlers/HandlerFactory.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public IHandler Create(
6969
warningActions = StringUtil.ConvertFromJson<HashSet<string>>(node16ForceWarnings);
7070
}
7171

72-
var repoActionFullName = "";
72+
string repoActionFullName;
7373
if (string.IsNullOrEmpty(repoAction.Name))
7474
{
7575
repoActionFullName = repoAction.Path; // local actions don't have a 'Name'

src/Runner.Worker/Handlers/NodeScriptActionHandler.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ public async Task RunAsync(ActionRunStage stage)
108108
ExecutionContext.Output($"The node12 is not supported. Use node16 instead.");
109109
Data.NodeVersion = "node16";
110110
}
111-
string forcedNodeVersion = System.Environment.GetEnvironmentVariable(Constants.Variables.Agent.ForcedActionsNodeVersion);
112111

112+
string forcedNodeVersion = System.Environment.GetEnvironmentVariable(Constants.Variables.Agent.ForcedActionsNodeVersion);
113113
if (forcedNodeVersion == "node16" && Data.NodeVersion != "node16")
114114
{
115115
Data.NodeVersion = "node16";

src/Test/L0/Worker/ActionManagerL0.cs

+68
Original file line numberDiff line numberDiff line change
@@ -1424,6 +1424,74 @@ public void LoadsNode16ActionDefinition()
14241424
}
14251425
}
14261426

1427+
[Fact]
1428+
[Trait("Level", "L0")]
1429+
[Trait("Category", "Worker")]
1430+
public void LoadsNode20ActionDefinition()
1431+
{
1432+
try
1433+
{
1434+
// Arrange.
1435+
Setup();
1436+
const string Content = @"
1437+
# Container action
1438+
name: 'Hello World'
1439+
description: 'Greet the world and record the time'
1440+
author: 'GitHub'
1441+
inputs:
1442+
greeting: # id of input
1443+
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
1444+
required: true
1445+
default: 'Hello'
1446+
entryPoint: # id of input
1447+
description: 'optional docker entrypoint overwrite.'
1448+
required: false
1449+
outputs:
1450+
time: # id of output
1451+
description: 'The time we did the greeting'
1452+
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
1453+
color: 'green' # optional, decorates the entry in the GitHub Marketplace
1454+
runs:
1455+
using: 'node20'
1456+
main: 'task.js'
1457+
";
1458+
Pipelines.ActionStep instance;
1459+
string directory;
1460+
CreateAction(yamlContent: Content, instance: out instance, directory: out directory);
1461+
1462+
// Act.
1463+
Definition definition = _actionManager.LoadAction(_ec.Object, instance);
1464+
1465+
// Assert.
1466+
Assert.NotNull(definition);
1467+
Assert.Equal(directory, definition.Directory);
1468+
Assert.NotNull(definition.Data);
1469+
Assert.NotNull(definition.Data.Inputs); // inputs
1470+
Dictionary<string, string> inputDefaults = new(StringComparer.OrdinalIgnoreCase);
1471+
foreach (var input in definition.Data.Inputs)
1472+
{
1473+
var name = input.Key.AssertString("key").Value;
1474+
var value = input.Value.AssertScalar("value").ToString();
1475+
1476+
_hc.GetTrace().Info($"Default: {name} = {value}");
1477+
inputDefaults[name] = value;
1478+
}
1479+
1480+
Assert.Equal(2, inputDefaults.Count);
1481+
Assert.True(inputDefaults.ContainsKey("greeting"));
1482+
Assert.Equal("Hello", inputDefaults["greeting"]);
1483+
Assert.True(string.IsNullOrEmpty(inputDefaults["entryPoint"]));
1484+
Assert.NotNull(definition.Data.Execution); // execution
1485+
1486+
Assert.NotNull(definition.Data.Execution as NodeJSActionExecutionData);
1487+
Assert.Equal("task.js", (definition.Data.Execution as NodeJSActionExecutionData).Script);
1488+
Assert.Equal("node20", (definition.Data.Execution as NodeJSActionExecutionData).NodeVersion);
1489+
}
1490+
finally
1491+
{
1492+
Teardown();
1493+
}
1494+
}
14271495
[Fact]
14281496
[Trait("Level", "L0")]
14291497
[Trait("Category", "Worker")]

src/Test/L0/Worker/ActionManifestManagerL0.cs

+44-1
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,49 @@ public void Load_Node16Action()
459459
}
460460
}
461461

462+
[Fact]
463+
[Trait("Level", "L0")]
464+
[Trait("Category", "Worker")]
465+
public void Load_Node20Action()
466+
{
467+
try
468+
{
469+
//Arrange
470+
Setup();
471+
472+
var actionManifest = new ActionManifestManager();
473+
actionManifest.Initialize(_hc);
474+
475+
//Act
476+
var result = actionManifest.Load(_ec.Object, Path.Combine(TestUtil.GetTestDataPath(), "node20action.yml"));
477+
478+
//Assert
479+
Assert.Equal("Hello World", result.Name);
480+
Assert.Equal("Greet the world and record the time", result.Description);
481+
Assert.Equal(2, result.Inputs.Count);
482+
Assert.Equal("greeting", result.Inputs[0].Key.AssertString("key").Value);
483+
Assert.Equal("Hello", result.Inputs[0].Value.AssertString("value").Value);
484+
Assert.Equal("entryPoint", result.Inputs[1].Key.AssertString("key").Value);
485+
Assert.Equal("", result.Inputs[1].Value.AssertString("value").Value);
486+
Assert.Equal(1, result.Deprecated.Count);
487+
488+
Assert.True(result.Deprecated.ContainsKey("greeting"));
489+
result.Deprecated.TryGetValue("greeting", out string value);
490+
Assert.Equal("This property has been deprecated", value);
491+
492+
Assert.Equal(ActionExecutionType.NodeJS, result.Execution.ExecutionType);
493+
494+
var nodeAction = result.Execution as NodeJSActionExecutionData;
495+
496+
Assert.Equal("main.js", nodeAction.Script);
497+
Assert.Equal("node20", nodeAction.NodeVersion);
498+
}
499+
finally
500+
{
501+
Teardown();
502+
}
503+
}
504+
462505
[Fact]
463506
[Trait("Level", "L0")]
464507
[Trait("Category", "Worker")]
@@ -715,7 +758,7 @@ public void Load_CompositeActionNoUsing()
715758
//Assert
716759
var err = Assert.Throws<ArgumentException>(() => actionManifest.Load(_ec.Object, action_path));
717760
Assert.Contains($"Fail to load {action_path}", err.Message);
718-
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12' or 'node16'.")), It.IsAny<ExecutionContextLogOptions>()), Times.Once);
761+
_ec.Verify(x => x.AddIssue(It.Is<Issue>(s => s.Message.Contains("Missing 'using' value. 'using' requires 'composite', 'docker', 'node12', 'node16' or 'node20'.")), It.IsAny<ExecutionContextLogOptions>()), Times.Once);
719762
}
720763
finally
721764
{

src/Test/L0/Worker/HandlerFactoryL0.cs

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ private TestHostContext CreateTestContext([CallerMemberName] string testName = "
3232
[Trait("Category", "Worker")]
3333
[InlineData("node12", "node16")]
3434
[InlineData("node16", "node16")]
35+
[InlineData("node20", "node20")]
3536
public void IsNodeVersionUpgraded(string inputVersion, string expectedVersion)
3637
{
3738
using (TestHostContext hc = CreateTestContext())

src/Test/L0/Worker/StepHostL0.cs

+54
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,33 @@ public async Task DetermineNodeRuntimeVersionInAlpineContainerAsync()
8282
}
8383
}
8484

85+
[Fact]
86+
[Trait("Level", "L0")]
87+
[Trait("Category", "Worker")]
88+
public async Task DetermineNode20RuntimeVersionInAlpineContainerAsync()
89+
{
90+
using (TestHostContext hc = CreateTestContext())
91+
{
92+
// Arrange.
93+
var sh = new ContainerStepHost();
94+
sh.Initialize(hc);
95+
sh.Container = new ContainerInfo() { ContainerId = "1234abcd" };
96+
97+
_dc.Setup(d => d.DockerExec(_ec.Object, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<List<string>>()))
98+
.Callback((IExecutionContext ec, string id, string options, string command, List<string> output) =>
99+
{
100+
output.Add("alpine");
101+
})
102+
.ReturnsAsync(0);
103+
104+
// Act.
105+
var nodeVersion = await sh.DetermineNodeRuntimeVersion(_ec.Object, "node20");
106+
107+
// Assert.
108+
Assert.Equal("node20_alpine", nodeVersion);
109+
}
110+
}
111+
85112
[Fact]
86113
[Trait("Level", "L0")]
87114
[Trait("Category", "Worker")]
@@ -108,6 +135,33 @@ public async Task DetermineNodeRuntimeVersionInUnknowContainerAsync()
108135
Assert.Equal("node16", nodeVersion);
109136
}
110137
}
138+
139+
[Fact]
140+
[Trait("Level", "L0")]
141+
[Trait("Category", "Worker")]
142+
public async Task DetermineNode20RuntimeVersionInUnknowContainerAsync()
143+
{
144+
using (TestHostContext hc = CreateTestContext())
145+
{
146+
// Arrange.
147+
var sh = new ContainerStepHost();
148+
sh.Initialize(hc);
149+
sh.Container = new ContainerInfo() { ContainerId = "1234abcd" };
150+
151+
_dc.Setup(d => d.DockerExec(_ec.Object, It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<List<string>>()))
152+
.Callback((IExecutionContext ec, string id, string options, string command, List<string> output) =>
153+
{
154+
output.Add("github");
155+
})
156+
.ReturnsAsync(0);
157+
158+
// Act.
159+
var nodeVersion = await sh.DetermineNodeRuntimeVersion(_ec.Object, "node20");
160+
161+
// Assert.
162+
Assert.Equal("node20", nodeVersion);
163+
}
164+
}
111165
#endif
112166
}
113167
}

src/Test/TestData/node20action.yml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: 'Hello World'
2+
description: 'Greet the world and record the time'
3+
author: 'Test Corporation'
4+
inputs:
5+
greeting: # id of input
6+
description: 'The greeting we choose - will print ""{greeting}, World!"" on stdout'
7+
required: true
8+
default: 'Hello'
9+
deprecationMessage: 'This property has been deprecated'
10+
entryPoint: # id of input
11+
description: 'optional docker entrypoint overwrite.'
12+
required: false
13+
outputs:
14+
time: # id of output
15+
description: 'The time we did the greeting'
16+
icon: 'hello.svg' # vector art to display in the GitHub Marketplace
17+
color: 'green' # optional, decorates the entry in the GitHub Marketplace
18+
runs:
19+
using: 'node20'
20+
main: 'main.js'

0 commit comments

Comments
 (0)