From 54735941fee4c908ddc0ce6ed8af347295c75ec1 Mon Sep 17 00:00:00 2001 From: Ismayil Ismayilov Date: Fri, 23 Jun 2023 13:40:38 +0400 Subject: [PATCH 1/8] Added telemetry classes to produce inline publish functionality --- src/Agent.Listener/Agent.cs | 15 +++ .../Telemetry/CustomerIntelligenceServer.cs | 35 ++++++ .../Telemetry/TelemetryPublisher.cs | 112 ++++++++++++++++++ 3 files changed, 162 insertions(+) create mode 100644 src/Agent.Listener/Telemetry/CustomerIntelligenceServer.cs create mode 100644 src/Agent.Listener/Telemetry/TelemetryPublisher.cs diff --git a/src/Agent.Listener/Agent.cs b/src/Agent.Listener/Agent.cs index 7628352679..2213ee5e79 100644 --- a/src/Agent.Listener/Agent.cs +++ b/src/Agent.Listener/Agent.cs @@ -15,6 +15,10 @@ using System.IO; using System.Reflection; using System.Runtime.CompilerServices; +using Microsoft.TeamFoundation.TestClient.PublishTestResults.Telemetry; +using Microsoft.VisualStudio.Services.Agent.Listener.Telemetry; +using System.Collections.Generic; +using Newtonsoft.Json; namespace Microsoft.VisualStudio.Services.Agent.Listener { @@ -227,6 +231,17 @@ public async Task ExecuteCommand(CommandSettings command) } } } + + var tp = HostContext.GetService(); + + // Sample telemetry data to publish + //var cmd = new Command("area", "feature"); + //var props = new Dictionary() { { "test", "data" } }; + //cmd.Data = JsonConvert.SerializeObject(props); + //cmd.Properties.Add("area", "PipelinesTasks"); + //cmd.Properties.Add("feature", "ExecutionHandler"); + //await tp.PublishEvent(HostContext, cmd); + // Run the agent interactively or as service return await RunAsync(settings, command.GetRunOnce()); } diff --git a/src/Agent.Listener/Telemetry/CustomerIntelligenceServer.cs b/src/Agent.Listener/Telemetry/CustomerIntelligenceServer.cs new file mode 100644 index 0000000000..3fba6079d5 --- /dev/null +++ b/src/Agent.Listener/Telemetry/CustomerIntelligenceServer.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.VisualStudio.Services.Agent.Util; +using Microsoft.VisualStudio.Services.CustomerIntelligence.WebApi; +using Microsoft.VisualStudio.Services.WebApi; +using Microsoft.VisualStudio.Services.WebPlatform; + +namespace Microsoft.VisualStudio.Services.Agent.Listener.Telemetry +{ + [ServiceLocator(Default = typeof(CustomerIntelligenceServer))] + public interface ICustomerIntelligenceServer : IAgentService + { + void Initialize(VssConnection connection); + Task PublishEventsAsync(CustomerIntelligenceEvent[] ciEvents); + } + + // This service is used for tracking task events which are applicable for VSTS internal tasks + public class CustomerIntelligenceServer : AgentService, ICustomerIntelligenceServer + { + private CustomerIntelligenceHttpClient _ciClient; + + public void Initialize(VssConnection connection) + { + ArgUtil.NotNull(connection, nameof(connection)); + _ciClient = connection.GetClient(); + } + + public Task PublishEventsAsync(CustomerIntelligenceEvent[] ciEvents) + { + return _ciClient.PublishEventsAsync(events: ciEvents); + } + } +} diff --git a/src/Agent.Listener/Telemetry/TelemetryPublisher.cs b/src/Agent.Listener/Telemetry/TelemetryPublisher.cs new file mode 100644 index 0000000000..5faa4fde0c --- /dev/null +++ b/src/Agent.Listener/Telemetry/TelemetryPublisher.cs @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.VisualStudio.Services.Agent.Listener.Configuration; +using Microsoft.VisualStudio.Services.Agent.Util; +using Microsoft.VisualStudio.Services.Common; +using Microsoft.VisualStudio.Services.WebPlatform; + +using Newtonsoft.Json; + +namespace Microsoft.VisualStudio.Services.Agent.Listener.Telemetry +{ + [ServiceLocator(Default = typeof(TelemetryPublisher))] + public interface IAgenetListenerTelemetryPublisher : IAgentService + { + public Task PublishEvent(IHostContext context, Command command); + } + + public sealed class TelemetryPublisher : AgentService, IAgenetListenerTelemetryPublisher + { + private ICustomerIntelligenceServer _ciService; + + public string Name => "publish"; + public List Aliases => null; + + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA2000:Dispose objects before losing scope", MessageId = "jobServer")] + public async Task PublishEvent(IHostContext context, Command command) + { + Trace.Info("Loading Credentials"); + var credMgr = HostContext.GetService(); + VssCredentials creds = credMgr.LoadCredentials(); + + ArgUtil.NotNull(creds, nameof(creds)); + + var configManager = HostContext.GetService(); + AgentSettings settings = configManager.LoadSettings(); + + var vssConnection = VssUtil.CreateConnection(new Uri(settings.ServerUrl), creds, Trace); + try + { + _ciService = HostContext.GetService(); + _ciService.Initialize(vssConnection); + } + + catch (Exception ex) + { + Trace.Warning(StringUtil.Loc("TelemetryCommandFailed", ex.Message)); + return; + } + + ArgUtil.NotNull(context, nameof(context)); + ArgUtil.NotNull(command, nameof(command)); + Dictionary eventProperties = command.Properties; + string data = command.Data; + string area; + if (!eventProperties.TryGetValue(WellKnownEventTrackProperties.Area, out area) || string.IsNullOrEmpty(area)) + { + throw new ArgumentException(StringUtil.Loc("ArgumentNeeded", "Area")); + } + + string feature; + if (!eventProperties.TryGetValue(WellKnownEventTrackProperties.Feature, out feature) || string.IsNullOrEmpty(feature)) + { + throw new ArgumentException(StringUtil.Loc("ArgumentNeeded", "Feature")); + } + + if (string.IsNullOrEmpty(data)) + { + throw new ArgumentException(StringUtil.Loc("ArgumentNeeded", "EventTrackerData")); + } + + CustomerIntelligenceEvent ciEvent; + try + { + var ciProperties = JsonConvert.DeserializeObject>(data); + ciEvent = new CustomerIntelligenceEvent() + { + Area = area, + Feature = feature, + Properties = ciProperties + }; + } + catch (Exception ex) + { + throw new ArgumentException(StringUtil.Loc("TelemetryCommandDataError", data, ex.Message)); + } + + await PublishEventsAsync(context, ciEvent); + } + + private async Task PublishEventsAsync(IHostContext context, CustomerIntelligenceEvent ciEvent) + { + try + { + await _ciService.PublishEventsAsync(new CustomerIntelligenceEvent[] { ciEvent }); + } + catch (Exception ex) + { + Trace.Warning(StringUtil.Loc("TelemetryCommandFailed", ex.Message)); + } + } + } + internal static class WellKnownEventTrackProperties + { + internal static readonly string Area = "area"; + internal static readonly string Feature = "feature"; + } +} \ No newline at end of file From b683b83fec8d3a62c2bb133459dc6872d98f2f6d Mon Sep 17 00:00:00 2001 From: Ismayil Ismayilov Date: Fri, 30 Jun 2023 19:40:05 +0400 Subject: [PATCH 2/8] Clean PR and remove unnecessary changes --- src/Agent.Listener/Agent.cs | 36 +++++++++++++----- .../Telemetry/TelemetryPublisher.cs | 37 +++++++++++-------- 2 files changed, 48 insertions(+), 25 deletions(-) diff --git a/src/Agent.Listener/Agent.cs b/src/Agent.Listener/Agent.cs index 2213ee5e79..f25e8e2af4 100644 --- a/src/Agent.Listener/Agent.cs +++ b/src/Agent.Listener/Agent.cs @@ -232,15 +232,33 @@ public async Task ExecuteCommand(CommandSettings command) } } - var tp = HostContext.GetService(); - - // Sample telemetry data to publish - //var cmd = new Command("area", "feature"); - //var props = new Dictionary() { { "test", "data" } }; - //cmd.Data = JsonConvert.SerializeObject(props); - //cmd.Properties.Add("area", "PipelinesTasks"); - //cmd.Properties.Add("feature", "ExecutionHandler"); - //await tp.PublishEvent(HostContext, cmd); + //Publish inital telemetry data + var telemetryPublisher = HostContext.GetService(); + + try + { + var systemVersion = PlatformUtil.GetSystemVersion(); + + Dictionary telemetryData = new Dictionary + { + { "OS", PlatformUtil.GetSystemId() ?? "" }, + { "OSVersion", systemVersion?.Name?.ToString() ?? "" }, + { "OSBuild", systemVersion?.Version?.ToString() ?? "" }, + { "configuredAsService", $"{configuredAsService}"}, + { "startupType", startupTypeAsString } + }; + var cmd = new Command("telemetry", "publish"); + cmd.Data = JsonConvert.SerializeObject(telemetryData); + cmd.Properties.Add("area", "PipelinesTasks"); + cmd.Properties.Add("feature", "AgentListener"); + await telemetryPublisher.PublishEvent(HostContext, cmd); + } + + catch (Exception ex) + { + Trace.Warning($"Unable to publish telemetry data. {ex}"); + } + // Run the agent interactively or as service return await RunAsync(settings, command.GetRunOnce()); diff --git a/src/Agent.Listener/Telemetry/TelemetryPublisher.cs b/src/Agent.Listener/Telemetry/TelemetryPublisher.cs index 5faa4fde0c..967b2ecdb0 100644 --- a/src/Agent.Listener/Telemetry/TelemetryPublisher.cs +++ b/src/Agent.Listener/Telemetry/TelemetryPublisher.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; + using Microsoft.VisualStudio.Services.Agent.Listener.Configuration; using Microsoft.VisualStudio.Services.Agent.Util; using Microsoft.VisualStudio.Services.Common; @@ -30,30 +31,34 @@ public sealed class TelemetryPublisher : AgentService, IAgenetListenerTelemetryP [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA2000:Dispose objects before losing scope", MessageId = "jobServer")] public async Task PublishEvent(IHostContext context, Command command) { - Trace.Info("Loading Credentials"); - var credMgr = HostContext.GetService(); - VssCredentials creds = credMgr.LoadCredentials(); - ArgUtil.NotNull(creds, nameof(creds)); + if (_ciService == null) + { + var credMgr = context.GetService(); + VssCredentials creds = credMgr.LoadCredentials(); - var configManager = HostContext.GetService(); - AgentSettings settings = configManager.LoadSettings(); + ArgUtil.NotNull(creds, nameof(creds)); - var vssConnection = VssUtil.CreateConnection(new Uri(settings.ServerUrl), creds, Trace); - try - { - _ciService = HostContext.GetService(); - _ciService.Initialize(vssConnection); - } + var configManager = context.GetService(); + AgentSettings settings = configManager.LoadSettings(); - catch (Exception ex) - { - Trace.Warning(StringUtil.Loc("TelemetryCommandFailed", ex.Message)); - return; + var vssConnection = VssUtil.CreateConnection(new Uri(settings.ServerUrl), creds, Trace); + try + { + _ciService = context.GetService(); + _ciService.Initialize(vssConnection); + } + + catch (Exception ex) + { + Trace.Warning(StringUtil.Loc("TelemetryCommandFailed", ex.Message)); + return; + } } ArgUtil.NotNull(context, nameof(context)); ArgUtil.NotNull(command, nameof(command)); + Dictionary eventProperties = command.Properties; string data = command.Data; string area; From a98b50a9815bb455c7b5444db968b5db1e322660 Mon Sep 17 00:00:00 2001 From: Ismayil Ismayilov Date: Mon, 3 Jul 2023 15:40:10 +0400 Subject: [PATCH 3/8] Added DI for tests --- src/Test/L0/Listener/AgentL0.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/Test/L0/Listener/AgentL0.cs b/src/Test/L0/Listener/AgentL0.cs index 5ec33cc5c5..14250ab78d 100644 --- a/src/Test/L0/Listener/AgentL0.cs +++ b/src/Test/L0/Listener/AgentL0.cs @@ -13,6 +13,7 @@ using Microsoft.VisualStudio.Services.WebApi; using Pipelines = Microsoft.TeamFoundation.DistributedTask.Pipelines; using Microsoft.VisualStudio.Services.Agent.Util; +using Microsoft.VisualStudio.Services.Agent.Listener.Telemetry; namespace Microsoft.VisualStudio.Services.Agent.Tests.Listener { @@ -29,6 +30,7 @@ public sealed class AgentL0 private Mock _proxy; private Mock _cert; private Mock _updater; + private Mock _listenerTelemetryPublisher; public AgentL0() { @@ -43,6 +45,7 @@ public AgentL0() _proxy = new Mock(); _cert = new Mock(); _updater = new Mock(); + _listenerTelemetryPublisher = new Mock(); } private AgentJobRequestMessage CreateJobRequestMessage(string jobName) @@ -81,6 +84,8 @@ public async void TestRunAsync() hc.SetSingleton(_proxy.Object); hc.SetSingleton(_cert.Object); hc.SetSingleton(_configStore.Object); + hc.SetSingleton(_listenerTelemetryPublisher.Object); + agent.Initialize(hc); var settings = new AgentSettings { @@ -193,7 +198,7 @@ public async void TestExecuteCommandForRunAsService(string[] args, bool configur hc.SetSingleton(_proxy.Object); hc.SetSingleton(_cert.Object); hc.SetSingleton(_configStore.Object); - + hc.SetSingleton(_listenerTelemetryPublisher.Object); var command = new CommandSettings(hc, args); _configurationManager.Setup(x => x.IsConfigured()).Returns(true); @@ -227,6 +232,7 @@ public async void TestMachineProvisionerCLI() hc.SetSingleton(_proxy.Object); hc.SetSingleton(_cert.Object); hc.SetSingleton(_configStore.Object); + hc.SetSingleton(_listenerTelemetryPublisher.Object); var command = new CommandSettings(hc, new[] { "run" }); @@ -263,6 +269,7 @@ public async void TestMachineProvisionerCLICompat() hc.SetSingleton(_proxy.Object); hc.SetSingleton(_cert.Object); hc.SetSingleton(_configStore.Object); + hc.SetSingleton(_listenerTelemetryPublisher.Object); var command = new CommandSettings(hc, new string[] { }); @@ -301,6 +308,8 @@ public async void TestRunOnce() hc.SetSingleton(_proxy.Object); hc.SetSingleton(_cert.Object); hc.SetSingleton(_configStore.Object); + hc.SetSingleton(_listenerTelemetryPublisher.Object); + agent.Initialize(hc); var settings = new AgentSettings { @@ -397,6 +406,8 @@ public async void TestRunOnceOnlyTakeOneJobMessage() hc.SetSingleton(_proxy.Object); hc.SetSingleton(_cert.Object); hc.SetSingleton(_configStore.Object); + hc.SetSingleton(_listenerTelemetryPublisher.Object); + agent.Initialize(hc); var settings = new AgentSettings { @@ -501,6 +512,7 @@ public async void TestRunOnceHandleUpdateMessage() hc.SetSingleton(_cert.Object); hc.SetSingleton(_configStore.Object); hc.SetSingleton(_updater.Object); + hc.SetSingleton(_listenerTelemetryPublisher.Object); agent.Initialize(hc); var settings = new AgentSettings @@ -592,6 +604,7 @@ public async void TestInfoArgumentsCLI(string arg, int expected = Constants.Agen hc.SetSingleton(_proxy.Object); hc.SetSingleton(_cert.Object); hc.SetSingleton(_configStore.Object); + hc.SetSingleton(_listenerTelemetryPublisher.Object); var command = new CommandSettings(hc, new[] { arg }); @@ -719,6 +732,8 @@ public async void TestMetadataUpdate() hc.SetSingleton(_proxy.Object); hc.SetSingleton(_cert.Object); hc.SetSingleton(_configStore.Object); + hc.SetSingleton(_listenerTelemetryPublisher.Object); + agent.Initialize(hc); var settings = new AgentSettings { From b8938bde442ece702b753c343077a6837acb49e0 Mon Sep 17 00:00:00 2001 From: Ismayil Ismayilov Date: Thu, 13 Jul 2023 03:04:55 +0400 Subject: [PATCH 4/8] Add default value if build repository clean is not set --- src/Agent.Worker/Build/BuildJobExtension.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Agent.Worker/Build/BuildJobExtension.cs b/src/Agent.Worker/Build/BuildJobExtension.cs index de5ac9d9f6..96c7823cab 100644 --- a/src/Agent.Worker/Build/BuildJobExtension.cs +++ b/src/Agent.Worker/Build/BuildJobExtension.cs @@ -257,10 +257,7 @@ private void UpdateCheckoutTasksAndVariables(IExecutionContext executionContext, executionContext.SetVariable(Constants.Variables.Build.RepoGitSubmoduleCheckout, submoduleCheckout.Value.ToString()); } - if (repoCleanFromSelf.HasValue) - { - executionContext.SetVariable(Constants.Variables.Build.RepoClean, repoCleanFromSelf.Value.ToString()); - } + executionContext.SetVariable(Constants.Variables.Build.RepoClean, repoCleanFromSelf.HasValue ? repoCleanFromSelf.Value.ToString() : "False"); } private bool TryGetRepositoryInfoFromLocalPath(IExecutionContext executionContext, string localPath, out RepositoryInfo repoInfo) From b54d9dc5db08f85d3b04ab4cdafebbbc34bbb0c1 Mon Sep 17 00:00:00 2001 From: Ismayil Ismayilov Date: Sat, 5 Aug 2023 00:26:06 +0400 Subject: [PATCH 5/8] Added DeprecatedKnob to keep backward compatibility --- src/Agent.Sdk/Knob/AgentKnobs.cs | 6 ++++++ src/Agent.Worker/Build/BuildJobExtension.cs | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Agent.Sdk/Knob/AgentKnobs.cs b/src/Agent.Sdk/Knob/AgentKnobs.cs index 250a9db015..6767a0035b 100644 --- a/src/Agent.Sdk/Knob/AgentKnobs.cs +++ b/src/Agent.Sdk/Knob/AgentKnobs.cs @@ -479,5 +479,11 @@ public class AgentKnobs new RuntimeKnobSource("AGENT_FORCE_CREATE_TASKS_DIRECTORY"), new EnvironmentKnobSource("AGENT_FORCE_CREATE_TASKS_DIRECTORY"), new BuiltInDefaultKnobSource("false")); + + public static readonly Knob DisableCleanRepoDefaultValue = new DeprecatedKnob( + nameof(DisableCleanRepoDefaultValue), + "Avoid to set default value if build.repository.clean variable is not set on Trigger Yaml UI or in checkout steps yaml config", + new EnvironmentKnobSource("AGENT_DISABLE_CLEAN_REPO_DEFAULT_VALUE"), + new BuiltInDefaultKnobSource("false")); } } diff --git a/src/Agent.Worker/Build/BuildJobExtension.cs b/src/Agent.Worker/Build/BuildJobExtension.cs index 96c7823cab..ad20e01f95 100644 --- a/src/Agent.Worker/Build/BuildJobExtension.cs +++ b/src/Agent.Worker/Build/BuildJobExtension.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using Microsoft.TeamFoundation.DistributedTask.Pipelines; +using Agent.Sdk.Knob; namespace Microsoft.VisualStudio.Services.Agent.Worker.Build { @@ -257,7 +258,19 @@ private void UpdateCheckoutTasksAndVariables(IExecutionContext executionContext, executionContext.SetVariable(Constants.Variables.Build.RepoGitSubmoduleCheckout, submoduleCheckout.Value.ToString()); } - executionContext.SetVariable(Constants.Variables.Build.RepoClean, repoCleanFromSelf.HasValue ? repoCleanFromSelf.Value.ToString() : "False"); + // This condition is for maintaining backward compatibility. + // Remove the if-else condition and keep only the context inside the 'else' to set the default value in future releases. + if (AgentKnobs.DisableCleanRepoDefaultValue.GetValue(executionContext).AsBoolean()) + { + if (repoCleanFromSelf.HasValue) + { + executionContext.SetVariable(Constants.Variables.Build.RepoClean, repoCleanFromSelf.Value.ToString()); + } + } + else + { + executionContext.SetVariable(Constants.Variables.Build.RepoClean, repoCleanFromSelf.HasValue ? repoCleanFromSelf.Value.ToString() : "False"); + } } private bool TryGetRepositoryInfoFromLocalPath(IExecutionContext executionContext, string localPath, out RepositoryInfo repoInfo) From 22a7e048c72d0ad1154528bcccf88e2cc492fe3f Mon Sep 17 00:00:00 2001 From: Ismayil Ismayilov Date: Wed, 16 Aug 2023 17:28:38 +0400 Subject: [PATCH 6/8] Change context to provide SystemEnvironment data --- src/Agent.Worker/Build/BuildJobExtension.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Agent.Worker/Build/BuildJobExtension.cs b/src/Agent.Worker/Build/BuildJobExtension.cs index ad20e01f95..cbc1fcecd1 100644 --- a/src/Agent.Worker/Build/BuildJobExtension.cs +++ b/src/Agent.Worker/Build/BuildJobExtension.cs @@ -260,7 +260,7 @@ private void UpdateCheckoutTasksAndVariables(IExecutionContext executionContext, // This condition is for maintaining backward compatibility. // Remove the if-else condition and keep only the context inside the 'else' to set the default value in future releases. - if (AgentKnobs.DisableCleanRepoDefaultValue.GetValue(executionContext).AsBoolean()) + if (AgentKnobs.DisableCleanRepoDefaultValue.GetValue(UtilKnobValueContext.Instance()).AsBoolean()) { if (repoCleanFromSelf.HasValue) { From 0bdafe56a10450c1923c4e5f3759c325e3632e55 Mon Sep 17 00:00:00 2001 From: Ismayil Ismayilov Date: Wed, 16 Aug 2023 17:33:52 +0400 Subject: [PATCH 7/8] Restore unrelated changes --- src/Agent.Listener/Telemetry/TelemetryPublisher.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Agent.Listener/Telemetry/TelemetryPublisher.cs b/src/Agent.Listener/Telemetry/TelemetryPublisher.cs index 755886ad7d..4e7828778e 100644 --- a/src/Agent.Listener/Telemetry/TelemetryPublisher.cs +++ b/src/Agent.Listener/Telemetry/TelemetryPublisher.cs @@ -28,7 +28,6 @@ public sealed class TelemetryPublisher : AgentService, IAgenetListenerTelemetryP public List Aliases => null; - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA2000:Dispose objects before losing scope", MessageId = "jobServer")] public async Task PublishEvent(IHostContext context, Command command) { From 9a2ca35948e85bc1a564af87e9869d75eda181a7 Mon Sep 17 00:00:00 2001 From: Ismayil Ismayilov Date: Tue, 19 Sep 2023 01:26:19 +0400 Subject: [PATCH 8/8] Fix merge conflicts error --- src/Agent.Sdk/Knob/AgentKnobs.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Agent.Sdk/Knob/AgentKnobs.cs b/src/Agent.Sdk/Knob/AgentKnobs.cs index 0b71e09120..224370922b 100644 --- a/src/Agent.Sdk/Knob/AgentKnobs.cs +++ b/src/Agent.Sdk/Knob/AgentKnobs.cs @@ -503,7 +503,8 @@ public class AgentKnobs nameof(DisableCleanRepoDefaultValue), "Avoid to set default value if build.repository.clean variable is not set on Trigger Yaml UI or in checkout steps yaml config", new EnvironmentKnobSource("AGENT_DISABLE_CLEAN_REPO_DEFAULT_VALUE"), - + new BuiltInDefaultKnobSource("false")); + public static readonly Knob IgnoreVSTSTaskLib = new Knob( nameof(IgnoreVSTSTaskLib), "Ignores the VSTSTaskLib folder when copying tasks.",