diff --git a/.azure-pipelines/build-job.yml b/.azure-pipelines/build-job.yml index 8f560d4300..f82a0d4cc4 100644 --- a/.azure-pipelines/build-job.yml +++ b/.azure-pipelines/build-job.yml @@ -138,6 +138,7 @@ jobs: - script: ${{ variables.devCommand }} testl0 Debug ${{ parameters.os }}-${{ parameters.arch }} workingDirectory: src displayName: Unit tests + timeoutInMinutes: 5 # Install nuget - ${{ if eq(parameters.os, 'win') }}: diff --git a/src/Agent.Sdk/Knob/AgentKnobs.cs b/src/Agent.Sdk/Knob/AgentKnobs.cs index 03d4be36da..b5b4ba3591 100644 --- a/src/Agent.Sdk/Knob/AgentKnobs.cs +++ b/src/Agent.Sdk/Knob/AgentKnobs.cs @@ -506,11 +506,17 @@ public class AgentKnobs new EnvironmentKnobSource("AGENT_DISABLE_CLEAN_REPO_DEFAULT_VALUE"), new BuiltInDefaultKnobSource("false")); - public static readonly Knob IgnoreVSTSTaskLib = new Knob( + public static readonly Knob IgnoreVSTSTaskLib = new Knob( nameof(IgnoreVSTSTaskLib), "Ignores the VSTSTaskLib folder when copying tasks.", new RuntimeKnobSource("AZP_AGENT_IGNORE_VSTSTASKLIB"), new EnvironmentKnobSource("AZP_AGENT_IGNORE_VSTSTASKLIB"), new BuiltInDefaultKnobSource("false")); + + public static readonly Knob CheckForTaskDeprecation = new Knob( + nameof(CheckForTaskDeprecation), + "If true, the agent will check in the 'Initialize job' step each task used in the job for task deprecation.", + new EnvironmentKnobSource("AZP_AGENT_CHECK_FOR_TASK_DEPRECATION"), + new BuiltInDefaultKnobSource("false")); } } diff --git a/src/Agent.Worker/TaskManager.cs b/src/Agent.Worker/TaskManager.cs index 908b6752eb..3d944f1f3a 100644 --- a/src/Agent.Worker/TaskManager.cs +++ b/src/Agent.Worker/TaskManager.cs @@ -7,6 +7,7 @@ using Pipelines = Microsoft.TeamFoundation.DistributedTask.Pipelines; using Microsoft.VisualStudio.Services.Agent.Util; using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; @@ -73,7 +74,13 @@ into taskGrouping Trace.Info("Skip download checkout task."); continue; } + await DownloadAsync(executionContext, task); + + if (AgentKnobs.CheckForTaskDeprecation.GetValue(UtilKnobValueContext.Instance()).AsBoolean()) + { + CheckForTaskDeprecation(executionContext, task); + } } } @@ -308,6 +315,45 @@ 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); + var deprecated = taskJson["deprecated"]; + + if (deprecated != null && deprecated.Value()) + { + string friendlyName = taskJson["friendlyName"].Value(); + int majorVersion = new Version(task.Version).Major; + string deprecationMessage = StringUtil.Loc("DeprecationMessage", friendlyName, majorVersion, task.Name); + var removalDate = taskJson["removalDate"]; + + if (removalDate != null) + { + string whitespace = " "; + string removalDateString = removalDate.Value().ToString("MMMM d, yyyy"); + deprecationMessage += whitespace + StringUtil.Loc("DeprecationMessageRemovalDate", removalDateString); + var helpUrl = taskJson["helpUrl"]; + + if (helpUrl != null) + { + string helpUrlString = helpUrl.Value(); + string category = taskJson["category"].Value().ToLower(); + string urlPrefix = $"https://docs.microsoft.com/azure/devops/pipelines/tasks/{category}/"; + + if (helpUrlString.StartsWith(urlPrefix)) + { + string versionHelpUrl = $"{helpUrlString}-v{majorVersion}".Replace(urlPrefix, $"https://learn.microsoft.com/azure/devops/pipelines/tasks/reference/"); + deprecationMessage += whitespace + StringUtil.Loc("DeprecationMessageHelpUrl", versionHelpUrl); + } + } + } + + executionContext.Warning(deprecationMessage); + } + } + private void ExtractZip(String zipFile, String destinationDirectory) { ZipFile.ExtractToDirectory(zipFile, destinationDirectory); diff --git a/src/Misc/layoutbin/en-US/strings.json b/src/Misc/layoutbin/en-US/strings.json index 2250c75638..7f755a6a94 100644 --- a/src/Misc/layoutbin/en-US/strings.json +++ b/src/Misc/layoutbin/en-US/strings.json @@ -262,6 +262,9 @@ "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.", "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.", + "DeprecationMessageRemovalDate": "This task will be removed. From {0}, onwards it may no longer be available.", "DirectoryHierarchyUnauthorized": "Permission to read the directory contents is required for '{0}' and each directory up the hierarchy. {1}", "DirectoryIsEmptyForArtifact": "Directory '{0}' is empty. Nothing will be added to build artifact '{1}'.", "DirectoryNotFound": "Directory not found: '{0}'",