diff --git a/releaseNote.md b/releaseNote.md
index 10c1cdcf7..d1558eede 100644
--- a/releaseNote.md
+++ b/releaseNote.md
@@ -1,13 +1,9 @@
## What's Changed
-* Try add orchestrationid into user-agent using token claim. by @TingluoHuang in https://github.com/actions/runner/pull/3945
-* Fix null reference exception in user agent handling by @salmanmkc in https://github.com/actions/runner/pull/3946
-* Runner Support for executing Node24 Actions by @salmanmkc in https://github.com/actions/runner/pull/3940
-* Update dotnet sdk to latest version @8.0.412 by @github-actions[bot] in https://github.com/actions/runner/pull/3941
+* Update Docker to v28.3.2 and Buildx to v0.26.1 by @github-actions[bot] in https://github.com/actions/runner/pull/3953
+* Fix if statement structure in update script and variable reference by @salmanmkc in https://github.com/actions/runner/pull/3956
-## New Contributors
-* @salmanmkc made their first contribution in https://github.com/actions/runner/pull/3946
-**Full Changelog**: https://github.com/actions/runner/compare/v2.326.0...v2.327.0
+**Full Changelog**: https://github.com/actions/runner/compare/v2.327.0...v2.327.1
_Note: Actions Runner follows a progressive release policy, so the latest release might not be available to your enterprise, organization, or repository yet.
To confirm which version of the Actions Runner you should expect, please view the download instructions for your enterprise, organization, or repository.
diff --git a/src/Runner.Common/Util/NodeUtil.cs b/src/Runner.Common/Util/NodeUtil.cs
index 65a3278fb..27e124cd9 100644
--- a/src/Runner.Common/Util/NodeUtil.cs
+++ b/src/Runner.Common/Util/NodeUtil.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace GitHub.Runner.Common.Util
@@ -18,6 +19,87 @@ namespace GitHub.Runner.Common.Util
}
return _defaultNodeVersion;
}
+ ///
+ /// Determines the appropriate Node version for Actions to use
+ ///
+ /// Optional dictionary containing workflow-level environment variables
+ /// Feature flag indicating if Node 24 should be the default
+ /// Feature flag indicating if Node 24 is required
+ /// Optional callback for emitting warnings
+ /// The Node version to use (node20 or node24) and warning message if both env vars are set
+ public static (string nodeVersion, string warningMessage) DetermineActionsNodeVersion(
+ IDictionary workflowEnvironment = null,
+ bool useNode24ByDefault = false,
+ bool requireNode24 = false)
+ {
+ // Get effective values for the flags, with workflow taking precedence over system
+ bool forceNode24 = IsEnvironmentVariableTrue(Constants.Runner.NodeMigration.ForceNode24Variable, workflowEnvironment);
+ bool allowUnsecureNode = IsEnvironmentVariableTrue(Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable, workflowEnvironment);
+
+ string warningMessage = null;
+
+ // Phase 3: Always use Node 24 regardless of environment variables
+ if (requireNode24)
+ {
+ return (Constants.Runner.NodeMigration.Node24, null);
+ }
+
+ // Check if both flags are set from the same source
+ bool bothFromWorkflow = false;
+ bool bothFromSystem = false;
+
+ if (workflowEnvironment != null)
+ {
+ bool workflowHasForce = workflowEnvironment.TryGetValue(Constants.Runner.NodeMigration.ForceNode24Variable, out string forceValue) &&
+ !string.IsNullOrEmpty(forceValue);
+ bool workflowHasAllow = workflowEnvironment.TryGetValue(Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable, out string allowValue) &&
+ !string.IsNullOrEmpty(allowValue);
+
+ bothFromWorkflow = workflowHasForce && workflowHasAllow &&
+ string.Equals(forceValue, "true", StringComparison.OrdinalIgnoreCase) &&
+ string.Equals(allowValue, "true", StringComparison.OrdinalIgnoreCase);
+ }
+
+ // Check if both are set in system and neither is overridden by workflow
+ string sysForce = Environment.GetEnvironmentVariable(Constants.Runner.NodeMigration.ForceNode24Variable);
+ string sysAllow = Environment.GetEnvironmentVariable(Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable);
+
+ bool systemHasForce = !string.IsNullOrEmpty(sysForce) && string.Equals(sysForce, "true", StringComparison.OrdinalIgnoreCase);
+ bool systemHasAllow = !string.IsNullOrEmpty(sysAllow) && string.Equals(sysAllow, "true", StringComparison.OrdinalIgnoreCase);
+
+ // Both are set in system and not overridden by workflow
+ bothFromSystem = systemHasForce && systemHasAllow &&
+ (workflowEnvironment == null ||
+ (!workflowEnvironment.ContainsKey(Constants.Runner.NodeMigration.ForceNode24Variable) &&
+ !workflowEnvironment.ContainsKey(Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable)));
+
+ // Handle the case when both are set in the same source
+ if ((bothFromWorkflow || bothFromSystem) && !requireNode24)
+ {
+ string defaultVersion = useNode24ByDefault ? Constants.Runner.NodeMigration.Node24 : Constants.Runner.NodeMigration.Node20;
+ warningMessage = $"Both {Constants.Runner.NodeMigration.ForceNode24Variable} and {Constants.Runner.NodeMigration.AllowUnsecureNodeVersionVariable} environment variables are set to true. This is likely a configuration error. Using the default Node version: {defaultVersion}.";
+ return (defaultVersion, warningMessage);
+ }
+
+ // Phase 2: Node 24 is the default
+ if (useNode24ByDefault)
+ {
+ if (allowUnsecureNode)
+ {
+ return (Constants.Runner.NodeMigration.Node20, null);
+ }
+
+ return (Constants.Runner.NodeMigration.Node24, null);
+ }
+
+ // Phase 1: Node 20 is the default
+ if (forceNode24)
+ {
+ return (Constants.Runner.NodeMigration.Node24, null);
+ }
+
+ return (Constants.Runner.NodeMigration.Node20, null);
+ }
///
/// Checks if Node24 is requested but running on ARM32 Linux, and determines if fallback is needed.
@@ -26,14 +108,34 @@ namespace GitHub.Runner.Common.Util
/// A tuple containing the adjusted node version and an optional warning message
public static (string nodeVersion, string warningMessage) CheckNodeVersionForLinuxArm32(string preferredVersion)
{
- if (string.Equals(preferredVersion, "node24", StringComparison.OrdinalIgnoreCase) &&
+ if (string.Equals(preferredVersion, Constants.Runner.NodeMigration.Node24, StringComparison.OrdinalIgnoreCase) &&
Constants.Runner.PlatformArchitecture.Equals(Constants.Architecture.Arm) &&
Constants.Runner.Platform.Equals(Constants.OSPlatform.Linux))
{
- return ("node20", "Node 24 is not supported on Linux ARM32 platforms. Falling back to Node 20.");
+ return (Constants.Runner.NodeMigration.Node20, "Node 24 is not supported on Linux ARM32 platforms. Falling back to Node 20.");
}
return (preferredVersion, null);
}
+
+ ///
+ /// Checks if an environment variable is set to "true" in either the workflow environment or system environment
+ ///
+ /// The name of the environment variable
+ /// Optional dictionary containing workflow-level environment variables
+ /// True if the variable is set to "true" in either environment
+ private static bool IsEnvironmentVariableTrue(string variableName, IDictionary workflowEnvironment)
+ {
+ // Workflow environment variables take precedence over system environment variables
+ // If the workflow explicitly sets the value (even to false), we respect that over the system value
+ if (workflowEnvironment != null && workflowEnvironment.TryGetValue(variableName, out string workflowValue))
+ {
+ return string.Equals(workflowValue, "true", StringComparison.OrdinalIgnoreCase);
+ }
+
+ // Fall back to system environment variable only if workflow doesn't specify this variable
+ string systemValue = Environment.GetEnvironmentVariable(variableName);
+ return string.Equals(systemValue, "true", StringComparison.OrdinalIgnoreCase);
+ }
}
}
diff --git a/src/Runner.Worker/Handlers/HandlerFactory.cs b/src/Runner.Worker/Handlers/HandlerFactory.cs
index 5ea9361cd..0f92b9bba 100644
--- a/src/Runner.Worker/Handlers/HandlerFactory.cs
+++ b/src/Runner.Worker/Handlers/HandlerFactory.cs
@@ -58,10 +58,33 @@ namespace GitHub.Runner.Worker.Handlers
var nodeData = data as NodeJSActionExecutionData;
// With node12 EoL in 04/2022 and node16 EoL in 09/23, we want to execute all JS actions using node20
+ // With node20 EoL approaching, we're preparing to migrate to node24
if (string.Equals(nodeData.NodeVersion, "node12", StringComparison.InvariantCultureIgnoreCase) ||
string.Equals(nodeData.NodeVersion, "node16", StringComparison.InvariantCultureIgnoreCase))
{
- nodeData.NodeVersion = "node20";
+ nodeData.NodeVersion = Common.Constants.Runner.NodeMigration.Node20;
+ }
+
+ // Check if node20 was explicitly specified in the action
+ // We don't modify if node24 was explicitly specified
+ if (string.Equals(nodeData.NodeVersion, Constants.Runner.NodeMigration.Node20, StringComparison.InvariantCultureIgnoreCase))
+ {
+ bool useNode24ByDefault = true;
+ bool requireNode24 = FeatureManager.IsFeatureEnabled(executionContext.Global.Variables, Constants.Runner.NodeMigration.RequireNode24Flag);
+
+ var (nodeVersion, configWarningMessage) = NodeUtil.DetermineActionsNodeVersion(environment, useNode24ByDefault, requireNode24);
+ var (finalNodeVersion, platformWarningMessage) = NodeUtil.CheckNodeVersionForLinuxArm32(nodeVersion);
+ nodeData.NodeVersion = finalNodeVersion;
+
+ if (!string.IsNullOrEmpty(configWarningMessage))
+ {
+ executionContext.Warning(configWarningMessage);
+ }
+
+ if (!string.IsNullOrEmpty(platformWarningMessage))
+ {
+ executionContext.Warning(platformWarningMessage);
+ }
}
(handler as INodeScriptActionHandler).Data = nodeData;
diff --git a/src/runnerversion b/src/runnerversion
index fe24dbf39..74c6532ee 100644
--- a/src/runnerversion
+++ b/src/runnerversion
@@ -1 +1 @@
-2.327.0
+2.327.1