Skip to content

Commit

Permalink
Check if task node runner is deprecated (#4794)
Browse files Browse the repository at this point in the history
* check if task node runner is deprecated

* use hashset

* return hashset

---------

Co-authored-by: Kirill Ivlev <[email protected]>
  • Loading branch information
DenisRumyantsev and kirill-ivlev authored Jun 3, 2024
1 parent beac576 commit 5aaa84f
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 3 deletions.
6 changes: 6 additions & 0 deletions src/Agent.Sdk/Knob/AgentKnobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,12 @@ public class AgentKnobs
new RuntimeKnobSource("AZP_AGENT_CHECK_FOR_TASK_DEPRECATION"),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob CheckIfTaskNodeRunnerIsDeprecated = new Knob(
nameof(CheckIfTaskNodeRunnerIsDeprecated),
"If true, the agent will check in the 'Initialize job' step each task used in the job if this task has node handlers, and all of them are deprecated.",
new RuntimeKnobSource("AZP_AGENT_CHECK_IF_TASK_NODE_RUNNER_IS_DEPRECATED"),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob MountWorkspace = new Knob(
nameof(MountWorkspace),
"If true, the agent will mount the Pipeline.Workspace directory instead of the Working directory for steps which target a Docker container.",
Expand Down
88 changes: 85 additions & 3 deletions src/Agent.Worker/TaskManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ into taskGrouping
return;
}

HashSet<Guid> exceptionList = GetTaskExceptionSet();

foreach (var task in uniqueTasks.Select(x => x.Reference))
{
if (task.Id == Pipelines.PipelineConstants.CheckoutTask.Id && task.Version == Pipelines.PipelineConstants.CheckoutTask.Version)
Expand All @@ -90,6 +92,14 @@ into taskGrouping
{
CheckForTaskDeprecation(executionContext, task);
}

if (AgentKnobs.CheckIfTaskNodeRunnerIsDeprecated.GetValue(executionContext).AsBoolean())
{
if (!exceptionList.Contains(task.Id))
{
CheckIfTaskNodeRunnerIsDeprecated(executionContext, task);
}
}
}
}

Expand Down Expand Up @@ -319,9 +329,7 @@ private async Task DownloadAsync(IExecutionContext executionContext, Pipelines.T

private void CheckForTaskDeprecation(IExecutionContext executionContext, Pipelines.TaskStepDefinitionReference task)
{
string taskJsonPath = Path.Combine(GetDirectory(task), "task.json");
string taskJsonText = File.ReadAllText(taskJsonPath);
JObject taskJson = JObject.Parse(taskJsonText);
JObject taskJson = GetTaskJson(task);
var deprecated = taskJson["deprecated"];

if (deprecated != null && deprecated.Value<bool>())
Expand Down Expand Up @@ -363,6 +371,80 @@ private void CheckForTaskDeprecation(IExecutionContext executionContext, Pipelin
}
}

private void CheckIfTaskNodeRunnerIsDeprecated(IExecutionContext executionContext, Pipelines.TaskStepDefinitionReference task)
{
string[] deprecatedNodeRunners = { "Node", "Node10" };
string[] approvedNodeRunners = { "Node16", "Node20_1" }; // Node runners which are not considered as deprecated

JObject taskJson = GetTaskJson(task);
var taskRunners = (JObject)taskJson["execution"];

foreach (string runner in approvedNodeRunners)
{
if (taskRunners.ContainsKey(runner))
{
return; // Agent never uses deprecated Node runners if there are approved Node runners
}
}

List<string> taskNodeRunners = new(); // If we are here and task has Node runners, all of them are deprecated

foreach (string runner in deprecatedNodeRunners)
{
if (taskRunners.ContainsKey(runner))
{
switch (runner)
{
case "Node":
taskNodeRunners.Add("6"); // Just "Node" is Node version 6
break;
default:
taskNodeRunners.Add(runner[4..]); // Postfix after "Node"
break;
}
}
}

if (taskNodeRunners.Count > 0) // Tasks may have only PowerShell runners and don't have Node runners at all
{
string friendlyName = taskJson["friendlyName"].Value<string>();
int majorVersion = new Version(task.Version).Major;
executionContext.Warning(StringUtil.Loc("DeprecatedNodeRunner", friendlyName, majorVersion, task.Name, taskNodeRunners.Last()));
}
}

/// <summary>
/// This method provides a set of in-the-box pipeline tasks for which we don't want to display Node deprecation warnings.
/// </summary>
/// <returns> Set of tasks ID </returns>
private HashSet<Guid> GetTaskExceptionSet()
{
string exceptionListFile = HostContext.GetConfigFile(WellKnownConfigFile.TaskExceptionList);
var exceptionList = new List<Guid>();

if (File.Exists(exceptionListFile))
{
try
{
exceptionList = IOUtil.LoadObject<List<Guid>>(exceptionListFile);
}
catch (Exception ex)
{
Trace.Info($"Unable to deserialize exception list {ex}");
exceptionList = new List<Guid>();
}
}

return exceptionList.ToHashSet();
}

private JObject GetTaskJson(Pipelines.TaskStepDefinitionReference task)
{
string taskJsonPath = Path.Combine(GetDirectory(task), "task.json");
string taskJsonText = File.ReadAllText(taskJsonPath);
return JObject.Parse(taskJsonText);
}

private void ExtractZip(String zipFile, String destinationDirectory)
{
ZipFile.ExtractToDirectory(zipFile, destinationDirectory);
Expand Down
1 change: 1 addition & 0 deletions src/Misc/layoutbin/en-US/strings.json
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@
"DeploymentPoolName": "Deployment Pool name",
"DeploymentPoolNotFound": "Deployment pool not found: '{0}'",
"DeprecatedNode6": "This task uses Node 6 execution handler, which will be removed March 31st 2022. If you are the developer of the task - please consider the migration guideline to Node 10 handler - https://aka.ms/migrateTaskNode10 (check this page also if you would like to disable Node 6 deprecation warnings). If you are the user - feel free to reach out to the owners of this task to proceed on migration.",
"DeprecatedNodeRunner": "Task '{0}' version {1} ({2}@{1}) is dependent on a Node version ({3}) that is end-of-life. Contact the extension owner for an updated version of the task. Task maintainers should review Node upgrade guidance: https://aka.ms/node-runner-guidance",
"DeprecatedRunner": "Task '{0}' is dependent on a task runner that is end-of-life and will be removed in the future. Authors should review Node upgrade guidance: https://aka.ms/node-runner-guidance.",
"DeprecationMessage": "Task '{0}' version {1} ({2}@{1}) is deprecated.",
"DeprecationMessageHelpUrl": "Please see {0} for more information about this task.",
Expand Down
6 changes: 6 additions & 0 deletions src/Test/L0/TestHostContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,12 @@ public string GetConfigFile(WellKnownConfigFile configFile)
".setup_info");
break;

case WellKnownConfigFile.TaskExceptionList:
path = Path.Combine(
GetDirectory(WellKnownDirectory.Bin),
"tasks-exception-list.json");
break;

default:
throw new NotSupportedException($"Unexpected well known config file: '{configFile}'");
}
Expand Down

0 comments on commit 5aaa84f

Please sign in to comment.