Force JS Actions Node version to 16 if FF is on unless user opted out (#1716)

* Set GH actions Node version to 16 if FF is on unless user opted out

* Add L0s (WIP)

* Wrap tests into theory

* Only check for node12 actions

* Refactor node version picking
This commit is contained in:
Ferenc Hammerl
2022-02-25 20:59:16 +01:00
committed by GitHub
parent e8ccafea63
commit 100c99f263
3 changed files with 118 additions and 1 deletions

View File

@@ -220,6 +220,7 @@ namespace GitHub.Runner.Common
public static readonly string AllowUnsupportedStopCommandTokens = "ACTIONS_ALLOW_UNSECURE_STOPCOMMAND_TOKENS"; public static readonly string AllowUnsupportedStopCommandTokens = "ACTIONS_ALLOW_UNSECURE_STOPCOMMAND_TOKENS";
public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG"; public static readonly string RunnerDebug = "ACTIONS_RUNNER_DEBUG";
public static readonly string StepDebug = "ACTIONS_STEP_DEBUG"; public static readonly string StepDebug = "ACTIONS_STEP_DEBUG";
public static readonly string AllowActionsUseUnsecureNodeVersion = "ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION";
} }
public static class Agent public static class Agent

View File

@@ -55,7 +55,23 @@ namespace GitHub.Runner.Worker.Handlers
else if (data.ExecutionType == ActionExecutionType.NodeJS) else if (data.ExecutionType == ActionExecutionType.NodeJS)
{ {
handler = HostContext.CreateService<INodeScriptActionHandler>(); handler = HostContext.CreateService<INodeScriptActionHandler>();
(handler as INodeScriptActionHandler).Data = data as NodeJSActionExecutionData; var nodeData = data as NodeJSActionExecutionData;
// With node12 EoL in 04/2022, we want to be able to uniformly upgrade all JS actions to node16 from the server
if (string.Equals(nodeData.NodeVersion, "node12", StringComparison.InvariantCultureIgnoreCase) &&
(executionContext.Global.Variables.GetBoolean("DistributedTask.ForceGithubJavascriptActionsToNode16") ?? false))
{
// The user can opt out of this behaviour by setting this variable to true, either setting 'env' in their workflow or as an environment variable on their machine
executionContext.Global.EnvironmentVariables.TryGetValue(Constants.Variables.Actions.AllowActionsUseUnsecureNodeVersion, out var workflowOptOut);
var isWorkflowOptOutSet = !string.IsNullOrEmpty(workflowOptOut);
var isLocalOptOut = StringUtil.ConvertToBoolean(Environment.GetEnvironmentVariable(Constants.Variables.Actions.AllowActionsUseUnsecureNodeVersion));
bool isOptOut = isWorkflowOptOutSet ? StringUtil.ConvertToBoolean(workflowOptOut) : isLocalOptOut;
if (!isOptOut)
{
nodeData.NodeVersion = "node16";
}
}
(handler as INodeScriptActionHandler).Data = nodeData;
} }
else if (data.ExecutionType == ActionExecutionType.Script) else if (data.ExecutionType == ActionExecutionType.Script)
{ {

View File

@@ -0,0 +1,100 @@
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Moq;
using Xunit;
using GitHub.Runner.Worker;
using GitHub.Runner.Worker.Handlers;
using GitHub.DistributedTask.Pipelines;
using GitHub.DistributedTask.WebApi;
namespace GitHub.Runner.Common.Tests.Worker
{
public sealed class HandlerFactoryL0
{
private Mock<IExecutionContext> _ec;
private TestHostContext CreateTestContext([CallerMemberName] string testName = "")
{
var hostContext = new TestHostContext(this, testName);
_ec = new Mock<IExecutionContext>();
_ec.SetupAllProperties();
_ec.Object.Initialize(hostContext);
var handler = new Mock<INodeScriptActionHandler>();
handler.SetupAllProperties();
hostContext.EnqueueInstance(handler.Object);
//hostContext.EnqueueInstance(new ActionCommandManager() as IActionCommandManager);
return hostContext;
}
[Theory]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
[InlineData("node12", "", "", "", "node12")]
[InlineData("node12", "true", "", "", "node16")]
[InlineData("node12", "true", "", "true", "node12")]
[InlineData("node12", "true", "true", "", "node12")]
[InlineData("node12", "true", "true", "true", "node12")]
[InlineData("node12", "true", "false", "true", "node16")] // workflow overrides env
[InlineData("node16", "", "", "", "node16")]
[InlineData("node16", "true", "", "", "node16")]
[InlineData("node16", "true", "", "true", "node16")]
[InlineData("node16", "true", "true", "", "node16")]
[InlineData("node16", "true", "true", "true", "node16")]
[InlineData("node16", "true", "false", "true", "node16")]
public void IsNodeVersionUpgraded(string inputVersion, string serverFeatureFlag, string workflowOptOut, string machineOptOut, string expectedVersion)
{
using (TestHostContext hc = CreateTestContext())
{
// Arrange.
var hf = new HandlerFactory();
hf.Initialize(hc);
// Server Feature Flag
var variables = new Dictionary<string, VariableValue>();
if (!string.IsNullOrEmpty(serverFeatureFlag))
{
variables["DistributedTask.ForceGithubJavascriptActionsToNode16"] = serverFeatureFlag;
}
Variables serverVariables = new Variables(hc, variables);
// Workflow opt-out
var workflowVariables = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(workflowOptOut))
{
workflowVariables[Constants.Variables.Actions.AllowActionsUseUnsecureNodeVersion] = workflowOptOut;
}
// Machine opt-out
if (!string.IsNullOrEmpty(machineOptOut))
{
Environment.SetEnvironmentVariable(Constants.Variables.Actions.AllowActionsUseUnsecureNodeVersion, machineOptOut);
}
_ec.Setup(x => x.Global).Returns(new GlobalContext()
{
Variables = serverVariables,
EnvironmentVariables = workflowVariables
});
// Act.
var data = new NodeJSActionExecutionData();
data.NodeVersion = inputVersion;
var handler = hf.Create(
_ec.Object,
new ScriptReference(),
new Mock<IStepHost>().Object,
data,
new Dictionary<string, string>(),
new Dictionary<string, string>(),
new Variables(hc, new Dictionary<string, VariableValue>()), "", new List<JobExtensionRunner>()
) as INodeScriptActionHandler;
// Assert.
Assert.Equal(expectedVersion, handler.Data.NodeVersion);
Environment.SetEnvironmentVariable(Constants.Variables.Actions.AllowActionsUseUnsecureNodeVersion, null);
}
}
}
}