diff --git a/src/Agent.Listener/Configuration.FreeBSD/DaemonControlManager.cs b/src/Agent.Listener/Configuration.FreeBSD/DaemonControlManager.cs new file mode 100644 index 0000000000..29a1efe02d --- /dev/null +++ b/src/Agent.Listener/Configuration.FreeBSD/DaemonControlManager.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Versioning; +using System.Text; +using Microsoft.VisualStudio.Services.Agent.Util; + +namespace Microsoft.VisualStudio.Services.Agent.Listener.Configuration +{ + [ServiceLocator(Default = typeof(DaemonControlManager))] + [SupportedOSPlatform("freebsd")] + public interface IFreeBSDServiceControlManager : IAgentService + { + void GenerateScripts(AgentSettings settings); + } + + [SupportedOSPlatform("freebsd")] + public class DaemonControlManager : ServiceControlManager, IFreeBSDServiceControlManager + { + private const string _svcNamePattern = "vsts.agent.{0}.{1}.{2}.service"; + private const string _svcDisplayPattern = "Azure Pipelines Agent ({0}.{1}.{2})"; + private const string _shTemplate = "daemon.svc.sh.template"; + private const string _shName = "svc.sh"; + + public void GenerateScripts(AgentSettings settings) + { + try + { + string serviceName; + string serviceDisplayName; + CalculateServiceName(settings, _svcNamePattern, _svcDisplayPattern, out serviceName, out serviceDisplayName); + + string svcShPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), _shName); + + string svcShContent = File.ReadAllText(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), _shTemplate)); + var tokensToReplace = new Dictionary + { + { "{{SvcDescription}}", serviceDisplayName }, + { "{{SvcNameVar}}", serviceName } + }; + + svcShContent = tokensToReplace.Aggregate( + svcShContent, + (current, item) => current.Replace(item.Key, item.Value)); + + File.WriteAllText(svcShPath, svcShContent, new UTF8Encoding(false)); + + var unixUtil = HostContext.CreateService(); + unixUtil.ChmodAsync("755", svcShPath).GetAwaiter().GetResult(); + } + catch (Exception ex) + { + Trace.Error(ex); + throw; + } + } + } +} diff --git a/src/Agent.Listener/Configuration/ConfigurationManager.cs b/src/Agent.Listener/Configuration/ConfigurationManager.cs index e2f7b3c2cf..b1fc0ecc2f 100644 --- a/src/Agent.Listener/Configuration/ConfigurationManager.cs +++ b/src/Agent.Listener/Configuration/ConfigurationManager.cs @@ -104,6 +104,7 @@ public async Task ConfigureAsync(CommandSettings command) { case PlatformUtil.OS.OSX: case PlatformUtil.OS.Linux: + case PlatformUtil.OS.FreeBSD: // Write the section header. WriteSection(StringUtil.Loc("EulasSectionHeader")); @@ -351,6 +352,7 @@ public async Task ConfigureAsync(CommandSettings command) { case PlatformUtil.OS.OSX: case PlatformUtil.OS.Linux: + case PlatformUtil.OS.FreeBSD: // Save the provided admin cred for compat with previous agent. _store.SaveCredential(credProvider.CredentialData); break; @@ -457,6 +459,12 @@ public async Task ConfigureAsync(CommandSettings command) var serviceControlManager = HostContext.GetService(); serviceControlManager.GenerateScripts(agentSettings); } + else if (PlatformUtil.RunningOnFreeBSD) + { + // generate service config script for FreeBSD + var serviceControlManager = HostContext.GetService(); + serviceControlManager.GenerateScripts(agentSettings); + } else if (PlatformUtil.RunningOnMacOS) { // generate service config script for macOS @@ -508,7 +516,12 @@ public async Task UnconfigureAsync(CommandSettings command) else if (PlatformUtil.RunningOnLinux) { // unconfig systemd service first - throw new InvalidOperationException(StringUtil.Loc("UnconfigureServiceDService")); + throw new InvalidOperationException(StringUtil.Loc("UnconfigureSystemDService")); + } + else if (PlatformUtil.RunningOnFreeBSD) + { + // unconfig daemon service first + throw new InvalidOperationException(StringUtil.Loc("UnconfigureDaemonService")); } else if (PlatformUtil.RunningOnMacOS) { diff --git a/src/Agent.Listener/Configuration/NegotiateCredential.cs b/src/Agent.Listener/Configuration/NegotiateCredential.cs index cdd4881009..a504be47aa 100644 --- a/src/Agent.Listener/Configuration/NegotiateCredential.cs +++ b/src/Agent.Listener/Configuration/NegotiateCredential.cs @@ -56,6 +56,7 @@ public override VssCredentials GetVssCredentials(IHostContext context) switch (PlatformUtil.HostOS) { case PlatformUtil.OS.Linux: + case PlatformUtil.OS.FreeBSD: case PlatformUtil.OS.OSX: credentialCache.Add(new Uri(url), "NTLM", credential); break; diff --git a/src/Agent.Plugins/PipelineCache/FingerprintCreator.cs b/src/Agent.Plugins/PipelineCache/FingerprintCreator.cs index 05230ab24f..106f2305eb 100644 --- a/src/Agent.Plugins/PipelineCache/FingerprintCreator.cs +++ b/src/Agent.Plugins/PipelineCache/FingerprintCreator.cs @@ -21,7 +21,7 @@ namespace Agent.Plugins.PipelineCache public static class FingerprintCreator { private static readonly bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - private static readonly bool isCaseSensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + private static readonly bool isCaseSensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD); // https://github.com/Microsoft/azure-pipelines-task-lib/blob/master/node/docs/findingfiles.md#matchoptions private static readonly Options minimatchOptions = new Options diff --git a/src/Agent.Sdk/CommandPlugin.cs b/src/Agent.Sdk/CommandPlugin.cs index ecb7bde28d..6395d37659 100644 --- a/src/Agent.Sdk/CommandPlugin.cs +++ b/src/Agent.Sdk/CommandPlugin.cs @@ -113,7 +113,7 @@ public VssConnection InitializeVssConnection() VssClientHttpRequestSettings.Default.UserAgent = headerValues; - if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnMacOS) + if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD || PlatformUtil.RunningOnMacOS) { // The .NET Core 2.1 runtime switched its HTTP default from HTTP 1.1 to HTTP 2. // This causes problems with some versions of the Curl handler. diff --git a/src/Agent.Sdk/ContainerInfo.cs b/src/Agent.Sdk/ContainerInfo.cs index 44d33c96a8..78dd1720fc 100644 --- a/src/Agent.Sdk/ContainerInfo.cs +++ b/src/Agent.Sdk/ContainerInfo.cs @@ -278,7 +278,7 @@ public string TranslateContainerPathForImageOS(PlatformUtil.OS runningOs, string { if (!string.IsNullOrEmpty(path)) { - if (runningOs == PlatformUtil.OS.Windows && ImageOS == PlatformUtil.OS.Linux) + if (runningOs == PlatformUtil.OS.Windows && ((ImageOS == PlatformUtil.OS.Linux) || (ImageOS == PlatformUtil.OS.FreeBSD))) { return translateWindowsDriveRegex.Replace(path, "/").Replace("\\", "/"); } diff --git a/src/Agent.Sdk/TaskPlugin.cs b/src/Agent.Sdk/TaskPlugin.cs index eb8512c34b..23f0b552c8 100644 --- a/src/Agent.Sdk/TaskPlugin.cs +++ b/src/Agent.Sdk/TaskPlugin.cs @@ -91,7 +91,7 @@ public VssConnection InitializeVssConnection() VssClientHttpRequestSettings.Default.UserAgent = headerValues; - if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnMacOS) + if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD || PlatformUtil.RunningOnMacOS) { // The .NET Core 2.1 runtime switched its HTTP default from HTTP 1.1 to HTTP 2. // This causes problems with some versions of the Curl handler. diff --git a/src/Agent.Sdk/Util/IOUtil.cs b/src/Agent.Sdk/Util/IOUtil.cs index 712ffeb92d..6958bee153 100644 --- a/src/Agent.Sdk/Util/IOUtil.cs +++ b/src/Agent.Sdk/Util/IOUtil.cs @@ -33,7 +33,7 @@ public static string ExeExtension public static StringComparison FilePathStringComparison { get => - PlatformUtil.RunningOnLinux + (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD) ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; } diff --git a/src/Agent.Sdk/Util/PlatformUtil.cs b/src/Agent.Sdk/Util/PlatformUtil.cs index a81464d4b7..a57d14f4d8 100644 --- a/src/Agent.Sdk/Util/PlatformUtil.cs +++ b/src/Agent.Sdk/Util/PlatformUtil.cs @@ -27,7 +27,7 @@ public static class PlatformUtil private static OperatingSystem[] net6SupportedSystems; private static HttpClient httpClient = new HttpClient(); - private static readonly string[] linuxReleaseFilePaths = new string[2] { "/etc/os-release", "/usr/lib/os-release" }; + private static readonly string[] unixReleaseFilePaths = new string[2] { "/etc/os-release", "/usr/lib/os-release" }; // System.Runtime.InteropServices.OSPlatform is a struct, so it is // not suitable for switch statements. @@ -36,8 +36,9 @@ public static class PlatformUtil public enum OS { Linux, + FreeBSD, OSX, - Windows, + Windows } public static OS HostOS @@ -49,6 +50,10 @@ public static OS HostOS { return OS.Linux; } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD)) + { + return OS.FreeBSD; + } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { return OS.OSX; @@ -93,6 +98,12 @@ public static bool RunningOnAlpine } } + [SupportedOSPlatformGuard("freebsd")] + public static bool RunningOnFreeBSD + { + get => PlatformUtil.HostOS == PlatformUtil.OS.FreeBSD; + } + public static bool RunningOnRHEL6 { get @@ -113,7 +124,7 @@ public static string GetSystemId() #pragma warning disable CA1416 // SupportedOSPlatformGuard not honored on enum members return PlatformUtil.HostOS switch { - PlatformUtil.OS.Linux => GetLinuxId(), + PlatformUtil.OS.Linux or PlatformUtil.OS.FreeBSD => GetUnixId(), PlatformUtil.OS.OSX => "MacOS", PlatformUtil.OS.Windows => GetWindowsId(), _ => null @@ -126,7 +137,7 @@ public static SystemVersion GetSystemVersion() #pragma warning disable CA1416 // SupportedOSPlatformGuard not honored on enum members return PlatformUtil.HostOS switch { - PlatformUtil.OS.Linux => new SystemVersion(GetLinuxName(), null), + PlatformUtil.OS.Linux or PlatformUtil.OS.FreeBSD => new SystemVersion(GetUnixName(), null), PlatformUtil.OS.OSX => new SystemVersion(GetOSxName(), null), PlatformUtil.OS.Windows => new SystemVersion(GetWindowsName(), GetWindowsVersion()), _ => null @@ -162,36 +173,36 @@ private static void DetectRHEL6() } } - private static string GetLinuxReleaseFilePath() + private static string GetUnixReleaseFilePath() { - if (RunningOnLinux) + if (RunningOnLinux || RunningOnFreeBSD) { - string releaseFilePath = linuxReleaseFilePaths.FirstOrDefault(x => File.Exists(x), null); + string releaseFilePath = unixReleaseFilePaths.FirstOrDefault(x => File.Exists(x), null); return releaseFilePath; } return null; } - private static string GetLinuxId() + private static string GetUnixId() { - string filePath = GetLinuxReleaseFilePath(); + string filePath = GetUnixReleaseFilePath(); - if (RunningOnLinux && filePath != null) + if (filePath != null) { - Regex linuxIdRegex = new Regex("^ID\\s*=\\s*\"?(?[0-9a-z._-]+)\"?"); + Regex unixIdRegex = new Regex("^ID\\s*=\\s*\"?(?[0-9a-z._-]+)\"?"); using (StreamReader reader = new StreamReader(filePath)) { while (!reader.EndOfStream) { string line = reader.ReadLine(); - var linuxIdRegexMatch = linuxIdRegex.Match(line); + var unixIdRegexMatch = unixIdRegex.Match(line); - if (linuxIdRegexMatch.Success) + if (unixIdRegexMatch.Success) { - return linuxIdRegexMatch.Groups["id"].Value; + return unixIdRegexMatch.Groups["id"].Value; } } } @@ -200,25 +211,25 @@ private static string GetLinuxId() return null; } - private static string GetLinuxName() + private static string GetUnixName() { - string filePath = GetLinuxReleaseFilePath(); + string filePath = GetUnixReleaseFilePath(); - if (RunningOnLinux && filePath != null) + if (filePath != null) { - Regex linuxVersionIdRegex = new Regex("^VERSION_ID\\s*=\\s*\"?(?[0-9a-z._-]+)\"?"); + Regex unixVersionIdRegex = new Regex("^VERSION_ID\\s*=\\s*\"?(?[0-9a-z._-]+)\"?"); using (StreamReader reader = new StreamReader(filePath)) { while (!reader.EndOfStream) { string line = reader.ReadLine(); - var linuxVersionIdRegexMatch = linuxVersionIdRegex.Match(line); + var unixVersionIdRegexMatch = unixVersionIdRegex.Match(line); - if (linuxVersionIdRegexMatch.Success) + if (unixVersionIdRegexMatch.Success) { - return linuxVersionIdRegexMatch.Groups["id"].Value; + return unixVersionIdRegexMatch.Groups["id"].Value; } } } diff --git a/src/Agent.Sdk/Util/VssUtil.cs b/src/Agent.Sdk/Util/VssUtil.cs index a1d1d278e8..5e0789567b 100644 --- a/src/Agent.Sdk/Util/VssUtil.cs +++ b/src/Agent.Sdk/Util/VssUtil.cs @@ -39,7 +39,7 @@ public static void InitializeVssClientSettings(ProductInfoHeaderValue additional VssClientHttpRequestSettings.Default.UserAgent = headerValues; VssClientHttpRequestSettings.Default.ClientCertificateManager = clientCert; - if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnMacOS) + if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD || PlatformUtil.RunningOnMacOS) { // The .NET Core 2.1 runtime switched its HTTP default from HTTP 1.1 to HTTP 2. // This causes problems with some versions of the Curl handler. diff --git a/src/Agent.Worker/ContainerOperationProvider.cs b/src/Agent.Worker/ContainerOperationProvider.cs index 882f786442..f66a064853 100644 --- a/src/Agent.Worker/ContainerOperationProvider.cs +++ b/src/Agent.Worker/ContainerOperationProvider.cs @@ -768,7 +768,7 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta } } - if (PlatformUtil.RunningOnLinux) + if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD) { bool useNode20InUnsupportedSystem = AgentKnobs.UseNode20InUnsupportedSystem.GetValue(executionContext).AsBoolean(); diff --git a/src/Agent.Worker/Handlers/NodeHandler.cs b/src/Agent.Worker/Handlers/NodeHandler.cs index fafe462f85..69a68bbcc4 100644 --- a/src/Agent.Worker/Handlers/NodeHandler.cs +++ b/src/Agent.Worker/Handlers/NodeHandler.cs @@ -173,7 +173,7 @@ public async Task RunAsync() bool useNode20InUnsupportedSystem = AgentKnobs.UseNode20InUnsupportedSystem.GetValue(ExecutionContext).AsBoolean(); bool node20ResultsInGlibCErrorHost = false; - if (PlatformUtil.HostOS == PlatformUtil.OS.Linux && !useNode20InUnsupportedSystem) + if ((PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD) && !useNode20InUnsupportedSystem) { if (supportsNode20.HasValue) { diff --git a/src/Agent.Worker/Release/Artifacts/GitHubHttpClient.cs b/src/Agent.Worker/Release/Artifacts/GitHubHttpClient.cs index d70ffac191..e79a099a18 100644 --- a/src/Agent.Worker/Release/Artifacts/GitHubHttpClient.cs +++ b/src/Agent.Worker/Release/Artifacts/GitHubHttpClient.cs @@ -48,7 +48,7 @@ private T QueryItem(string accessToken, string url, out string errorMessage) request.Headers.Add("Authorization", "Token " + accessToken); request.Headers.Add("User-Agent", "VSTS-Agent/" + BuildConstants.AgentPackage.Version); - if (PlatformUtil.RunningOnMacOS || PlatformUtil.RunningOnLinux) + if (PlatformUtil.RunningOnMacOS || PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD) { request.Version = HttpVersion.Version11; } diff --git a/src/Agent.Worker/Release/ContainerFetchEngine/HttpRetryOnTimeoutHandler.cs b/src/Agent.Worker/Release/ContainerFetchEngine/HttpRetryOnTimeoutHandler.cs index df2b2fb715..5254566535 100644 --- a/src/Agent.Worker/Release/ContainerFetchEngine/HttpRetryOnTimeoutHandler.cs +++ b/src/Agent.Worker/Release/ContainerFetchEngine/HttpRetryOnTimeoutHandler.cs @@ -33,7 +33,7 @@ protected override async Task SendAsync( { ArgUtil.NotNull(request, nameof(request)); - if (PlatformUtil.RunningOnMacOS || PlatformUtil.RunningOnLinux) + if (PlatformUtil.RunningOnMacOS || PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD) { request.Version = HttpVersion.Version11; } diff --git a/src/Agent.Worker/ResourceMetricsManager.cs b/src/Agent.Worker/ResourceMetricsManager.cs index 58facd714c..c890a57878 100644 --- a/src/Agent.Worker/ResourceMetricsManager.cs +++ b/src/Agent.Worker/ResourceMetricsManager.cs @@ -269,7 +269,7 @@ private MemoryInfo GetMemoryInfo() memoryInfo.UsedMemoryMB = (totalMemory - freeMemory) / 1024; } - if (PlatformUtil.RunningOnLinux) + if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD) { // Some compact Linux distributions like UBI may not have "free" utility installed, or it may have a custom output // We don't want to break currently existing pipelines with ADO warnings @@ -277,8 +277,17 @@ private MemoryInfo GetMemoryInfo() try { - processStartInfo.FileName = "free"; - processStartInfo.Arguments = "-m"; + if (PlatformUtil.RunningOnLinux) + { + processStartInfo.FileName = "free"; + processStartInfo.Arguments = "-m"; + } + else + { + processStartInfo.FileName = "vmstat"; + processStartInfo.Arguments = "-h"; + } + processStartInfo.RedirectStandardOutput = true; using (var process = Process.Start(processStartInfo)) diff --git a/src/Common.props b/src/Common.props index d680e37ab1..be32afa65e 100644 --- a/src/Common.props +++ b/src/Common.props @@ -22,15 +22,18 @@ DEBUG - + OS_WINDOWS - + OS_OSX - + OS_LINUX + + OS_FREEBSD + X64 @@ -46,7 +49,7 @@ ARM64 - + X64 diff --git a/src/Microsoft.VisualStudio.Services.Agent/ExtensionManager.cs b/src/Microsoft.VisualStudio.Services.Agent/ExtensionManager.cs index db95050741..8327979aaf 100644 --- a/src/Microsoft.VisualStudio.Services.Agent/ExtensionManager.cs +++ b/src/Microsoft.VisualStudio.Services.Agent/ExtensionManager.cs @@ -46,7 +46,7 @@ private List LoadExtensions() where T : class, IExtension case "Microsoft.VisualStudio.Services.Agent.Capabilities.ICapabilitiesProvider": Add(extensions, "Microsoft.VisualStudio.Services.Agent.Capabilities.AgentCapabilitiesProvider, Microsoft.VisualStudio.Services.Agent"); Add(extensions, "Microsoft.VisualStudio.Services.Agent.Capabilities.EnvironmentCapabilitiesProvider, Microsoft.VisualStudio.Services.Agent"); - if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnMacOS) + if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD || PlatformUtil.RunningOnMacOS) { Add(extensions, "Microsoft.VisualStudio.Services.Agent.Capabilities.NixCapabilitiesProvider, Microsoft.VisualStudio.Services.Agent"); } diff --git a/src/Microsoft.VisualStudio.Services.Agent/HostContext.cs b/src/Microsoft.VisualStudio.Services.Agent/HostContext.cs index 2cb0cacde0..8d98fb8833 100644 --- a/src/Microsoft.VisualStudio.Services.Agent/HostContext.cs +++ b/src/Microsoft.VisualStudio.Services.Agent/HostContext.cs @@ -439,7 +439,7 @@ public T CreateService() where T : class, IAgentService { platformTarget = arg.TypedValue.Value as Type; } - else if (PlatformUtil.RunningOnLinux + else if ((PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD) && string.Equals(arg.MemberName, nameof(ServiceLocatorAttribute.PreferredOnLinux), StringComparison.Ordinal)) { platformTarget = arg.TypedValue.Value as Type; diff --git a/src/Microsoft.VisualStudio.Services.Agent/Util/VarUtil.cs b/src/Microsoft.VisualStudio.Services.Agent/Util/VarUtil.cs index 106e98d151..6aabae6085 100644 --- a/src/Microsoft.VisualStudio.Services.Agent/Util/VarUtil.cs +++ b/src/Microsoft.VisualStudio.Services.Agent/Util/VarUtil.cs @@ -35,6 +35,8 @@ public static string OS { case PlatformUtil.OS.Linux: return "Linux"; + case PlatformUtil.OS.FreeBSD: + return "FreeBSD"; case PlatformUtil.OS.OSX: return "Darwin"; case PlatformUtil.OS.Windows: diff --git a/src/Misc/externals.sh b/src/Misc/externals.sh index c9b4bc33b4..ffa1680678 100644 --- a/src/Misc/externals.sh +++ b/src/Misc/externals.sh @@ -150,6 +150,18 @@ function acquireExternalTool() { fi } +function linkExternalTool() { + local source_file=$1 # E.g. /usr/local/bin/node + local target_dir="$LAYOUT_DIR/externals/$2" # E.g. $LAYOUT_DIR/externals/node/bin + local target_name=$3 # E.g. node + if [ -d "$target_dir" ]; then + rm -rf "$target_dir" || checkRC 'rm' + fi + + mkdir -p "$target_dir" || checkRC 'mkdir' + ln -s "$source_file" "$target_dir/$target_name" || checkRC 'ln' +} + if [[ "$PACKAGERUNTIME" == "win-x"* ]]; then # Download external tools for Windows. @@ -201,15 +213,15 @@ else ARCH="darwin-arm64" acquireExternalTool "${NODE_URL}/v${NODE16_VERSION}/node-v${NODE16_VERSION}-${ARCH}.tar.gz" node16 fix_nested_dir acquireExternalTool "${NODE_URL}/v${NODE20_VERSION}/node-v${NODE20_VERSION}-${ARCH}.tar.gz" node20_1 fix_nested_dir - elif [[ "$PACKAGERUNTIME" == "linux-musl-arm64" ]]; then - ARCH="linux-arm64-musl" + elif [[ "$PACKAGERUNTIME" == "freebsd-x64" ]]; then + # Create a link to node only for FreeBSD. - if [[ "$INCLUDE_NODE10" == "true" ]]; then - acquireExternalTool "${CONTAINER_URL}/nodejs/${ARCH}/node-v${NODE10_VERSION}-${ARCH}.tar.gz" node10/bin fix_nested_dir + if [[ "$INCLUDE_NODE6" == "true" ]]; then + linkExternalTool /usr/local/bin/node node/bin node fi - - acquireExternalTool "${CONTAINER_URL}/nodejs/${ARCH}/node-v${NODE16_VERSION}-${ARCH}.tar.gz" node16/bin fix_nested_dir - acquireExternalTool "${CONTAINER_URL}/nodejs/${ARCH}/node-v${NODE20_VERSION}-${ARCH}.tar.gz" node20_1/bin fix_nested_dir + linkExternalTool /usr/local/bin/node node10/bin node + linkExternalTool /usr/local/bin/node node16/bin node + linkExternalTool /usr/local/bin/node node20_1/bin node else case $PACKAGERUNTIME in "linux-musl-x64") ARCH="linux-x64-musl";; diff --git a/src/Misc/layoutbin/AgentService.js b/src/Misc/layoutbin/AgentService.js index 57350d0978..ee38b56135 100644 --- a/src/Misc/layoutbin/AgentService.js +++ b/src/Misc/layoutbin/AgentService.js @@ -5,7 +5,7 @@ var childProcess = require("child_process"); var path = require("path") -var supported = ['linux', 'darwin'] +var supported = ['linux', 'freebsd', 'darwin'] if (supported.indexOf(process.platform) == -1) { console.log('Unsupported platform: ' + process.platform); diff --git a/src/Misc/layoutbin/daemon.svc.sh.template b/src/Misc/layoutbin/daemon.svc.sh.template new file mode 100644 index 0000000000..97099da302 --- /dev/null +++ b/src/Misc/layoutbin/daemon.svc.sh.template @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +SVC_NAME=`echo -n "{{SvcNameVar}}" | tr -c "[:alnum:]" "_"` +SVC_DESCRIPTION="{{SvcDescription}}" + +SVC_CMD=$1 +arg_2=${2} + +AGENT_ROOT=`pwd` + +UNIT_PATH=/etc/rc.d/${SVC_NAME} +TEMPLATE_PATH=./bin/vsts.agent.daemon.template +TEMP_PATH=./bin/vsts.agent.daemon.temp +CONFIG_PATH=.service + +user_id=`id -u` + +# daemon must run as sudo +# this script is a convenience wrapper around daemon +if [ $user_id -ne 0 ]; then + echo "Must run as sudo" + exit 1 +fi + +function failed() +{ + local error=${1:-Undefined error} + echo "Failed: $error" >&2 + exit 1 +} + +if [ ! -f "${TEMPLATE_PATH}" ]; then + failed "Must run from agent root or install is corrupt" +fi + +#check if we run as root +if [[ $(id -u) != "0" ]]; then + echo "Failed: This script requires to run with sudo." >&2 + exit 1 +fi + +function install() +{ + echo "Creating launch agent in ${UNIT_PATH}" + if [ -f "${UNIT_PATH}" ]; then + failed "error: exists ${UNIT_PATH}" + fi + + if [ -f "${TEMP_PATH}" ]; then + rm "${TEMP_PATH}" || failed "failed to delete ${TEMP_PATH}" + fi + + # can optionally use username supplied + run_as_user=${arg_2:-$SUDO_USER} + + if [ -z "${run_as_user}" ]; then + failed "Unable to determine the current user. Please use './svc.sh install [username]' to specify the user." + fi + + echo "Run as user: ${run_as_user}" + + run_as_uid=$(id -u ${run_as_user}) || failed "User does not exist" + echo "Run as uid: ${run_as_uid}" + + run_as_gid=$(id -g ${run_as_user}) || failed "Group not available" + echo "gid: ${run_as_gid}" + + sed "s/{{User}}/${run_as_user}/g; s/{{Name}}/${SVC_NAME}/g; s/{{Description}}/$(echo ${SVC_DESCRIPTION} | sed -e 's/[\/&]/\\&/g')/g; s/{{AgentRoot}}/$(echo ${AGENT_ROOT} | sed -e 's/[\/&]/\\&/g')/g;" "${TEMPLATE_PATH}" > "${TEMP_PATH}" || failed "failed to create replacement temp file" + mv "${TEMP_PATH}" "${UNIT_PATH}" || failed "failed to copy unit file" + + su -l ${run_as_user} -c "env > \"${AGENT_ROOT}/.env_file\"" > /dev/null || failed "failed to create environment file" + + # unit file should be executable and writeable by the owner + chmod 755 "${UNIT_PATH}" || failed "failed to set permissions on ${UNIT_PATH}" + + # Since we started with sudo, runsvc.sh will be owned by root. Change this to current login user. + cp ./bin/runsvc.sh ./runsvc.sh || failed "failed to copy runsvc.sh" + chown ${run_as_uid}:${run_as_gid} ./runsvc.sh || failed "failed to set owner for runsvc.sh" + chmod 755 ./runsvc.sh || failed "failed to set permission for runsvc.sh" + + sysrc ${SVC_NAME}_enable="YES" || failed "failed to enable ${SVC_NAME}" + + echo "${SVC_NAME}" > ${CONFIG_PATH} || failed "failed to create .service file" + chown ${run_as_uid}:${run_as_gid} ${CONFIG_PATH} || failed "failed to set permission for ${CONFIG_PATH}" +} + +function start() +{ + service ${SVC_NAME} start || failed "failed to start ${SVC_NAME}" + status +} + +function stop() +{ + service ${SVC_NAME} stop || failed "failed to stop ${SVC_NAME}" + status +} + +function uninstall() +{ + if service ${SVC_NAME} status > /dev/null; then + stop + fi + + sysrc -x ${SVC_NAME}_enable || failed "failed to disable ${SVC_NAME}" + rm "${UNIT_PATH}" || failed "failed to delete ${UNIT_PATH}" + + if [ -f "${CONFIG_PATH}" ]; then + rm "${CONFIG_PATH}" || failed "failed to delete ${CONFIG_PATH}" + fi +} + +function status() +{ + if [ -f "${UNIT_PATH}" ]; then + echo + echo "${UNIT_PATH}" + else + echo + echo "not installed" + echo + return + fi + + service ${SVC_NAME} status +} + +function usage() +{ + echo + echo Usage: + echo "./svc.sh [install, start, stop, status, uninstall]" + echo "Commands:" + echo " install [user]: Install agent service as Root or specified user." + echo " start: Manually start the agent service." + echo " stop: Manually stop the agent service." + echo " status: Display status of agent service." + echo " uninstall: Uninstall agent service." + echo +} + +case $SVC_CMD in + "install") install;; + "status") status;; + "uninstall") uninstall;; + "start") start;; + "stop") stop;; + "status") status;; + *) usage;; +esac + +exit 0 diff --git a/src/Misc/layoutbin/de-DE/strings.json b/src/Misc/layoutbin/de-DE/strings.json index 2aa808693c..2a3b1178e1 100644 --- a/src/Misc/layoutbin/de-DE/strings.json +++ b/src/Misc/layoutbin/de-DE/strings.json @@ -664,8 +664,9 @@ "UnableToArchiveResults": "Die Testergebnisse können nicht archiviert werden: {0}.", "UnableToParseBuildTrackingConfig0": "Die Konfiguration der Legacybuildnachverfolgung kann nicht analysiert werden. Stattdessen wird ein neues Buildverzeichnis erstellt. Das vorherige Verzeichnis befindet sich möglicherweise in einem nicht freigegebenen Zustand. Legacykonfigurationsinhalte: {0}", "UnconfigAutologon": "Einstellungen für die automatische Anmeldung werden entfernt.", + "UnconfigureDaemonService": "Konfiguration des Diensts zuerst gemäß „https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-freebsd“ aufheben", "UnconfigureOSXService": "Konfiguration des Diensts zuerst gemäß „https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-osx“ aufheben", - "UnconfigureServiceDService": "Konfiguration des Diensts zuerst gemäß „https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux“ aufheben", + "UnconfigureSystemDService": "Konfiguration des Diensts zuerst gemäß „https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux“ aufheben", "UnexpectedParallelCount": "Nicht unterstützte parallele Anzahl „%s“. Geben Sie eine Zahl zwischen 1 und 128 ein.", "UninstallingService": "Dienst wird entfernt", "UnknownCodeCoverageTool": "Das Code Coverage-Tool „{0}“ wird nicht unterstützt.", diff --git a/src/Misc/layoutbin/en-US/strings.json b/src/Misc/layoutbin/en-US/strings.json index d34cf9d722..4447c19b95 100644 --- a/src/Misc/layoutbin/en-US/strings.json +++ b/src/Misc/layoutbin/en-US/strings.json @@ -664,8 +664,9 @@ "UnableToArchiveResults": "Unable to archive the test results: {0}", "UnableToParseBuildTrackingConfig0": "Unable to parse the legacy build tracking configuration. A new build directory will be created instead. The previous directory may be left in an unreclaimed state. Legacy configuration contents: {0}", "UnconfigAutologon": "Removing autologon settings", + "UnconfigureDaemonService": "Unconfigure service first according to https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-freebsd", "UnconfigureOSXService": "Unconfigure service first according to https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-osx", - "UnconfigureServiceDService": "Unconfigure service first according to https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux", + "UnconfigureSystemDService": "Unconfigure service first according to https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux", "UnexpectedParallelCount": "Unsupported parallel count '%s'. Enter a number between 1 and 128.", "UninstallingService": "Removing service", "UnknownCodeCoverageTool": "Code coverage tool '{0}' is not supported.", diff --git a/src/Misc/layoutbin/es-ES/strings.json b/src/Misc/layoutbin/es-ES/strings.json index 5023976d7f..b56db9ec2b 100644 --- a/src/Misc/layoutbin/es-ES/strings.json +++ b/src/Misc/layoutbin/es-ES/strings.json @@ -664,8 +664,9 @@ "UnableToArchiveResults": "No se pudieron archivar los resultados de la prueba: {0}", "UnableToParseBuildTrackingConfig0": "No se puede analizar la configuración de seguimiento de compilación heredada. En su lugar, se creará un nuevo directorio de compilación. El directorio anterior se puede dejar en un estado no corregido. Contenido de configuración heredado: {0}", "UnconfigAutologon": "Quitando la configuración de inicio de sesión automático", + "UnconfigureDaemonService": "Quite primero la configuración del servicio según https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-freebsd", "UnconfigureOSXService": "Quite primero la configuración del servicio según https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-osx", - "UnconfigureServiceDService": "Quite primero la configuración del servicio según https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux", + "UnconfigureSystemDService": "Quite primero la configuración del servicio según https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux", "UnexpectedParallelCount": "Recuento de elementos \"%s\" en paralelo no admitido. Especifique un número entre 1 y 128.", "UninstallingService": "Quitando servicio", "UnknownCodeCoverageTool": "No se admite la herramienta de cobertura de código \"{0}\".", diff --git a/src/Misc/layoutbin/fr-FR/strings.json b/src/Misc/layoutbin/fr-FR/strings.json index e08418927e..f1ba987b7b 100644 --- a/src/Misc/layoutbin/fr-FR/strings.json +++ b/src/Misc/layoutbin/fr-FR/strings.json @@ -664,8 +664,9 @@ "UnableToArchiveResults": "Impossible d'archiver les résultats des tests : {0}", "UnableToParseBuildTrackingConfig0": "Impossible d’analyser la configuration de suivi de build héritée. Un nouveau répertoire de build sera créé à la place. Le répertoire précédent peut rester dans un état non revendiqué. Contenu de la configuration héritée : {0}", "UnconfigAutologon": "Suppression des paramètres d’ouverture de session automatique", + "UnconfigureDaemonService": "Annuler la configuration du service en fonction de https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-freebsd", "UnconfigureOSXService": "Annuler la configuration du service en fonction de https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-osx", - "UnconfigureServiceDService": "Annuler la configuration du service en fonction de https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux", + "UnconfigureSystemDService": "Annuler la configuration du service en fonction de https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux", "UnexpectedParallelCount": "Nombre parallèle '%s' non pris en charge. Entrez un nombre compris entre 1 et 128.", "UninstallingService": "Suppression du service", "UnknownCodeCoverageTool": "L’outil de couverture du code « {0} » n’est pas pris en charge.", diff --git a/src/Misc/layoutbin/it-IT/strings.json b/src/Misc/layoutbin/it-IT/strings.json index e35fcc996d..0d17d70a73 100644 --- a/src/Misc/layoutbin/it-IT/strings.json +++ b/src/Misc/layoutbin/it-IT/strings.json @@ -664,8 +664,9 @@ "UnableToArchiveResults": "Non è possibile archiviare i risultati del test: {0}", "UnableToParseBuildTrackingConfig0": "Non è possibile analizzare la configurazione di rilevamento della compilazione legacy. Verrà invece creata una nuova directory di compilazione. La directory precedente potrebbe essere lasciata in uno stato non richiesto. Contenuto della configurazione legacy: {0}", "UnconfigAutologon": "Rimozione delle impostazioni di accesso automatico", + "UnconfigureDaemonService": "Annullare prima la configurazione del servizio in base a https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-freebsd", "UnconfigureOSXService": "Annullare prima la configurazione del servizio in base a https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-osx", - "UnconfigureServiceDService": "Annullare prima la configurazione del servizio in base a https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux", + "UnconfigureSystemDService": "Annullare prima la configurazione del servizio in base a https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux", "UnexpectedParallelCount": "Conteggio parallelo '%s' non supportato. Immettere un numero compreso tra 1 e 128.", "UninstallingService": "Rimozione del servizio", "UnknownCodeCoverageTool": "Lo strumento di code coverage '{0}' non è supportato.", diff --git a/src/Misc/layoutbin/ja-JP/strings.json b/src/Misc/layoutbin/ja-JP/strings.json index d819e5ab64..4bcedb1d66 100644 --- a/src/Misc/layoutbin/ja-JP/strings.json +++ b/src/Misc/layoutbin/ja-JP/strings.json @@ -664,8 +664,9 @@ "UnableToArchiveResults": "テスト結果をアーカイブできません: {0}", "UnableToParseBuildTrackingConfig0": "レガシ ビルド追跡構成を解析できません。代わりに新しいビルド ディレクトリが作成されます。前のディレクトリは、回復されていない状態のままである可能性があります。レガシ構成の内容: {0}", "UnconfigAutologon": "自動ログオン設定を削除しています", - "UnconfigureOSXService": "https://www.visualstudio.com/ja-jp/docs/build/admin/agents/v2-linux に従って、まずサービスの構成を解除する", - "UnconfigureServiceDService": "https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux に従って、まずサービスの構成を解除する", + "UnconfigureDaemonService": "https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-freebsd に従って、まずサービスの構成を解除する", + "UnconfigureOSXService": "https://www.visualstudio.com/ja-jp/docs/build/admin/agents/v2-osx に従って、まずサービスの構成を解除する", + "UnconfigureSystemDService": "https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux に従って、まずサービスの構成を解除する", "UnexpectedParallelCount": "サポートされていない並列カウント '%s'。1 ~ 128 の数値を入力します。", "UninstallingService": "サービスの削除", "UnknownCodeCoverageTool": "コード カバレッジ ツール '{0}' はサポートされていません。", diff --git a/src/Misc/layoutbin/ko-KR/strings.json b/src/Misc/layoutbin/ko-KR/strings.json index b67bea20e7..55b83f24e2 100644 --- a/src/Misc/layoutbin/ko-KR/strings.json +++ b/src/Misc/layoutbin/ko-KR/strings.json @@ -664,8 +664,9 @@ "UnableToArchiveResults": "테스트 결과를 보관할 수 없음: {0}", "UnableToParseBuildTrackingConfig0": "레거시 빌드 추적 구성을 구문 분석할 수 없습니다. 대신 새 빌드 디렉터리가 생성됩니다. 이전 디렉터리는 회수되지 않은 상태로 남을 수 있습니다. 기존 구성 콘텐츠: {0}", "UnconfigAutologon": "자동 로그온 설정 제거", + "UnconfigureDaemonService": "https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-freebsd에 따라 먼저 서비스 구성을 해제하세요.", "UnconfigureOSXService": "https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-osx에 따라 먼저 서비스 구성을 해제합니다.", - "UnconfigureServiceDService": "https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux에 따라 먼저 서비스 구성을 해제하세요.", + "UnconfigureSystemDService": "https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux에 따라 먼저 서비스 구성을 해제하세요.", "UnexpectedParallelCount": "지원되지 않는 병렬 개수 '%s'입니다. 1에서 128 사이의 숫자를 입력하세요.", "UninstallingService": "서비스를 제거하는 중", "UnknownCodeCoverageTool": "코드 검사 도구 '{0}'은(는) 지원되지 않습니다.", diff --git a/src/Misc/layoutbin/ru-RU/strings.json b/src/Misc/layoutbin/ru-RU/strings.json index d08946e2af..0baf6c10ab 100644 --- a/src/Misc/layoutbin/ru-RU/strings.json +++ b/src/Misc/layoutbin/ru-RU/strings.json @@ -664,8 +664,9 @@ "UnableToArchiveResults": "Не удается заархивировать результаты теста: {0}", "UnableToParseBuildTrackingConfig0": "Не удалось проанализировать устаревшую конфигурацию отслеживания сборки. Вместо этого будет создан новый каталог сборки. Предыдущий каталог может остаться в невостребованном состоянии. Содержимое устаревшей конфигурации: {0}", "UnconfigAutologon": "Удаление параметров автоматического входа", + "UnconfigureDaemonService": "Сначала отмените настройку службы в соответствии с https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-freebsd", "UnconfigureOSXService": "Сначала отмените настройку службы в соответствии с https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-osx", - "UnconfigureServiceDService": "Сначала отмените настройку службы в соответствии с https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux", + "UnconfigureSystemDService": "Сначала отмените настройку службы в соответствии с https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux", "UnexpectedParallelCount": "Неподдерживаемое значение счетчика параллелизма \"%s\". Введите число от 1 до 128.", "UninstallingService": "Удаление службы", "UnknownCodeCoverageTool": "Средство оценки объема протестированного кода \"{0}\" не поддерживается.", diff --git a/src/Misc/layoutbin/vsts.agent.daemon.template b/src/Misc/layoutbin/vsts.agent.daemon.template new file mode 100644 index 0000000000..0c802b0721 --- /dev/null +++ b/src/Misc/layoutbin/vsts.agent.daemon.template @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +# $FreeBSD$ +# +# PROVIDE: {{Name}} +# REQUIRE: NETWORKING +# KEYWORD: shutdown + +. /etc/rc.subr + +name={{Name}} +rcvar={{Name}}_enable +{{Name}}_chdir="{{AgentRoot}}" +{{Name}}_env_file="{{AgentRoot}}/.env_file" + +load_rc_config $name + +: ${{{Name}}_enable:="NO"} + +pidfile="{{AgentRoot}}/${name}.pid" + +command=/usr/sbin/daemon +command_args="-f -P $pidfile -r -t \"{{Description}}\" -u {{User}} ./runsvc.sh" + +run_rc_command "$1" diff --git a/src/Misc/layoutbin/zh-CN/strings.json b/src/Misc/layoutbin/zh-CN/strings.json index 4c09ad76f1..8f48d39311 100644 --- a/src/Misc/layoutbin/zh-CN/strings.json +++ b/src/Misc/layoutbin/zh-CN/strings.json @@ -664,8 +664,9 @@ "UnableToArchiveResults": "无法对测试结果进行存档: {0}", "UnableToParseBuildTrackingConfig0": "无法分析旧的生成跟踪配置。将改为创建新的生成目录。上一个目录可能处于未认领状态。旧配置内容: {0}", "UnconfigAutologon": "正在删除自动登录设置", + "UnconfigureDaemonService": "首先根据 https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-freebsd 取消配置服务", "UnconfigureOSXService": "首先根据 https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-osx 取消配置服务", - "UnconfigureServiceDService": "首先根据 https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux 取消配置服务", + "UnconfigureSystemDService": "首先根据 https://www.visualstudio.com/en-us/docs/build/admin/agents/v2-linux 取消配置服务", "UnexpectedParallelCount": "并行计数“%s”不受支持。输入介于 1 到 128 之间的数字。", "UninstallingService": "正在删除服务", "UnknownCodeCoverageTool": "不支持代码覆盖率工具“{0}”。", diff --git a/src/Misc/layoutbin/zh-TW/strings.json b/src/Misc/layoutbin/zh-TW/strings.json index 37f1559774..46f01aa35e 100644 --- a/src/Misc/layoutbin/zh-TW/strings.json +++ b/src/Misc/layoutbin/zh-TW/strings.json @@ -664,8 +664,9 @@ "UnableToArchiveResults": "無法封存測試結果: {0}", "UnableToParseBuildTrackingConfig0": "無法剖析舊版組建追蹤設定。系統將會改為建立新的組建目錄。上一個目錄可能處於未被認領的狀態。舊版設定內容: {0}", "UnconfigAutologon": "正在移除自動登入設定", + "UnconfigureDaemonService": "請先根據 https://www.visualstudio.com/zh-tw/docs/build/admin/agents/v2-freebsd (部分機器翻譯) 將服務取消設定", "UnconfigureOSXService": "請先根據 https://www.visualstudio.com/zh-tw/docs/build/admin/agents/v2-osx (部分機器翻譯) 取消設定服務", - "UnconfigureServiceDService": "請先根據 https://www.visualstudio.com/zh-tw/docs/build/admin/agents/v2-linux (部分機器翻譯) 將服務取消設定", + "UnconfigureSystemDService": "請先根據 https://www.visualstudio.com/zh-tw/docs/build/admin/agents/v2-linux (部分機器翻譯) 將服務取消設定", "UnexpectedParallelCount": "不支援的平行計數 '%s'。請輸入介於 1 到 128 之間的數字。", "UninstallingService": "正在移除服務", "UnknownCodeCoverageTool": "不支援程式碼涵蓋範圍工具 '{0}'。", diff --git a/src/Test/L0/ConstantGenerationL0.cs b/src/Test/L0/ConstantGenerationL0.cs index 64d2f3a085..7bb6c00504 100644 --- a/src/Test/L0/ConstantGenerationL0.cs +++ b/src/Test/L0/ConstantGenerationL0.cs @@ -22,6 +22,7 @@ public void BuildConstantGenerateSucceed() "linux-arm64", "linux-musl-x64", "linux-musl-arm64", + "freebsd-x64", "osx-x64", "osx-arm64" }; diff --git a/src/Test/L0/Listener/Configuration/AgentAutoLogonTestL0.cs b/src/Test/L0/Listener/Configuration/AgentAutoLogonTestL0.cs index 9713316a65..b82b6077dc 100644 --- a/src/Test/L0/Listener/Configuration/AgentAutoLogonTestL0.cs +++ b/src/Test/L0/Listener/Configuration/AgentAutoLogonTestL0.cs @@ -17,6 +17,7 @@ namespace Microsoft.VisualStudio.Services.Agent.Tests.Listener { [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public sealed class AgentAutoLogonTestL0 { private Mock _windowsServiceHelper; diff --git a/src/Test/L0/Listener/Configuration/ArgumentValidatorTestsL0.cs b/src/Test/L0/Listener/Configuration/ArgumentValidatorTestsL0.cs index 5966793bfc..065ab0797d 100644 --- a/src/Test/L0/Listener/Configuration/ArgumentValidatorTestsL0.cs +++ b/src/Test/L0/Listener/Configuration/ArgumentValidatorTestsL0.cs @@ -51,6 +51,7 @@ public void NonEmptyValidator() [Trait("Category", "ArgumentValidator")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public void WindowsLogonAccountValidator() { using (TestHostContext hc = new TestHostContext(this)) diff --git a/src/Test/L0/Listener/Configuration/ConfigurationManagerL0.cs b/src/Test/L0/Listener/Configuration/ConfigurationManagerL0.cs index c67e12156e..68ca618e86 100644 --- a/src/Test/L0/Listener/Configuration/ConfigurationManagerL0.cs +++ b/src/Test/L0/Listener/Configuration/ConfigurationManagerL0.cs @@ -38,6 +38,7 @@ public sealed class ConfigurationManagerL0 : IDisposable private Mock _windowsServiceControlManager; private Mock _linuxServiceControlManager; + private Mock _freebsdServiceControlManager; private Mock _macServiceControlManager; private Mock _rsaKeyManager; @@ -77,6 +78,7 @@ public ConfigurationManagerL0() _windowsServiceControlManager = new Mock(); _linuxServiceControlManager = new Mock(); + _freebsdServiceControlManager = new Mock(); _macServiceControlManager = new Mock(); _capabilitiesManager = new CapabilitiesManager(); _featureFlagProvider = new Mock(); @@ -118,6 +120,7 @@ public ConfigurationManagerL0() _credMgr.Setup(x => x.GetCredentialProvider(It.IsAny())).Returns(new TestAgentCredential()); _linuxServiceControlManager.Setup(x => x.GenerateScripts(It.IsAny())); + _freebsdServiceControlManager.Setup(x => x.GenerateScripts(It.IsAny())); _macServiceControlManager.Setup(x => x.GenerateScripts(It.IsAny())); var expectedPools = new List() { new TaskAgentPool(_expectedPoolName) { Id = _expectedPoolId } }; @@ -153,6 +156,7 @@ private TestHostContext CreateTestContext([CallerMemberName] String testName = " tc.SetSingleton(_windowsServiceControlManager.Object); tc.SetSingleton(_linuxServiceControlManager.Object); + tc.SetSingleton(_freebsdServiceControlManager.Object); tc.SetSingleton(_macServiceControlManager.Object); tc.SetSingleton(_rsaKeyManager.Object); diff --git a/src/Test/L0/Listener/Configuration/NativeWindowsServiceHelperL0.cs b/src/Test/L0/Listener/Configuration/NativeWindowsServiceHelperL0.cs index 2691ff2e27..fef1ffe395 100644 --- a/src/Test/L0/Listener/Configuration/NativeWindowsServiceHelperL0.cs +++ b/src/Test/L0/Listener/Configuration/NativeWindowsServiceHelperL0.cs @@ -18,6 +18,7 @@ namespace Test.L0.Listener.Configuration { [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public class NativeWindowsServiceHelperL0 { diff --git a/src/Test/L0/ProcessExtensionL0.cs b/src/Test/L0/ProcessExtensionL0.cs index 732ae9092e..71062e26d5 100644 --- a/src/Test/L0/ProcessExtensionL0.cs +++ b/src/Test/L0/ProcessExtensionL0.cs @@ -15,6 +15,7 @@ public sealed class ProcessExtensionL0 [Trait("Level", "L0")] [Trait("Category", "Common")] [Trait("SkipOn", "darwin")] + [Trait("SkipOn", "freebsd")] public async Task SuccessReadProcessEnv() { // With the latest update coming with macOS 10.5.7+ there are no more any ways to retrieve diff --git a/src/Test/L0/ProcessInvokerL0.cs b/src/Test/L0/ProcessInvokerL0.cs index db6ba3d65f..5f1c25ccab 100644 --- a/src/Test/L0/ProcessInvokerL0.cs +++ b/src/Test/L0/ProcessInvokerL0.cs @@ -45,6 +45,7 @@ public async Task SuccessExitsWithCodeZero() [Trait("Level", "L0")] [Trait("Category", "Common")] [Trait("SkipOn", "darwin")] + [Trait("SkipOn", "freebsd")] public async Task TestCancel() { const int SecondsToRun = 20; @@ -63,7 +64,7 @@ public async Task TestCancel() } else { - execTask = processInvoker.ExecuteAsync("", "bash", $"-c \"sleep {SecondsToRun}s\"", null, tokenSource.Token); + execTask = processInvoker.ExecuteAsync("", "bash", $"-c \"sleep {SecondsToRun}\"", null, tokenSource.Token); } await Task.Delay(500); @@ -129,7 +130,7 @@ public async Task TestCancelEnsureCompletedWhenTaskNotKilled() } else { - execTask = processInvoker.ExecuteAsync("", "bash", $"-c \"sleep {SecondsToRun}s\"", null, false, null, false, null, false, false, false, continueAfterCancelProcessTreeKillAttempt, tokenSource.Token); + execTask = processInvoker.ExecuteAsync("", "bash", $"-c \"sleep {SecondsToRun}\"", null, false, null, false, null, false, false, false, continueAfterCancelProcessTreeKillAttempt, tokenSource.Token); } await Task.Delay(500); diff --git a/src/Test/L0/TestUtil.cs b/src/Test/L0/TestUtil.cs index 15ed156348..af4537bc83 100644 --- a/src/Test/L0/TestUtil.cs +++ b/src/Test/L0/TestUtil.cs @@ -58,6 +58,7 @@ public static string WriteAllTextToTempFile(string content, string extension = n } public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + public static bool IsFreeBSD() => RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD); public static bool IsMacOS() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); } diff --git a/src/Test/L0/Util/IOUtilL0.cs b/src/Test/L0/Util/IOUtilL0.cs index 2788134171..bf9e0658d1 100644 --- a/src/Test/L0/Util/IOUtilL0.cs +++ b/src/Test/L0/Util/IOUtilL0.cs @@ -377,6 +377,7 @@ public async void DeleteDirectory_DeletesWithRetry_Success() [Trait("Category", "Common")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public async void DeleteDirectory_DeletesWithRetry_CancellationRequested() { using (TestHostContext hc = new TestHostContext(this)) @@ -429,6 +430,7 @@ public async void DeleteDirectory_DeletesWithRetry_NonExistenDir() [Trait("Category", "Common")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public async void DeleteDirectory_DeletesWithRetry_IOException() { using (TestHostContext hc = new TestHostContext(this)) @@ -852,6 +854,7 @@ public async void DeleteFile_DeletesWithRetry_NonExistenFile() [Trait("Category", "Common")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public async void DeleteFile_DeletesWithRetry_IOException() { using (TestHostContext hc = new TestHostContext(this)) @@ -881,6 +884,7 @@ await Assert.ThrowsAsync(async () => [Trait("Category", "Common")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public async void DeleteFile_DeletesWithRetry_CancellationRequested() { using (TestHostContext hc = new TestHostContext(this)) @@ -913,6 +917,7 @@ await Assert.ThrowsAsync(async () => [Trait("Category", "Common")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public void GetRelativePathWindows() { using (TestHostContext hc = new TestHostContext(this)) @@ -1024,6 +1029,7 @@ public void GetRelativePathNonWindows() [Trait("Category", "Common")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public void ResolvePathWindows() { using (TestHostContext hc = new TestHostContext(this)) diff --git a/src/Test/L0/Util/ProcessUtilL0.cs b/src/Test/L0/Util/ProcessUtilL0.cs index aed9428bf2..4a52aba9aa 100644 --- a/src/Test/L0/Util/ProcessUtilL0.cs +++ b/src/Test/L0/Util/ProcessUtilL0.cs @@ -18,6 +18,7 @@ public sealed class WindowsProcessUtilL0 [Trait("Category", "Common")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public void Test_GetProcessList(bool useInteropToFindParentProcess) { using TestHostContext hc = new TestHostContext(this); diff --git a/src/Test/L0/Worker/Build/TrackingManagerL0.cs b/src/Test/L0/Worker/Build/TrackingManagerL0.cs index 1cf3b1e42e..07a30fde2a 100644 --- a/src/Test/L0/Worker/Build/TrackingManagerL0.cs +++ b/src/Test/L0/Worker/Build/TrackingManagerL0.cs @@ -519,6 +519,7 @@ public void MarksTrackingConfigForGarbageCollection() [Trait("Category", "Worker")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public void MarksTrackingConfigForGarbageCollection_Legacy() { using (TestHostContext hc = Setup()) diff --git a/src/Test/L0/Worker/Handlers/ProcessHandlerHelperL0.cs b/src/Test/L0/Worker/Handlers/ProcessHandlerHelperL0.cs index b0e02cc1c1..fbe0b7ff08 100644 --- a/src/Test/L0/Worker/Handlers/ProcessHandlerHelperL0.cs +++ b/src/Test/L0/Worker/Handlers/ProcessHandlerHelperL0.cs @@ -137,6 +137,7 @@ public void TestNoChanges(string input) [Trait("Category", "Worker")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public void WindowsCaseInsensetiveTest() { string argsLine = "%var1% 2"; diff --git a/src/Test/L0/Worker/JobExtensionL0.cs b/src/Test/L0/Worker/JobExtensionL0.cs index 0d1b4d0c42..71767249fc 100644 --- a/src/Test/L0/Worker/JobExtensionL0.cs +++ b/src/Test/L0/Worker/JobExtensionL0.cs @@ -686,6 +686,7 @@ public async Task JobExtensionIntraTaskStateMSI() [Trait("Category", "Worker")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public async Task JobExtensionManagementScriptStep() { using CancellationTokenSource tokenSource = new CancellationTokenSource(); @@ -727,6 +728,7 @@ public async Task JobExtensionManagementScriptStep() [Trait("Category", "Worker")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public async Task JobExtensionManagementScriptStepMSI() { using CancellationTokenSource tokenSource = new CancellationTokenSource(); diff --git a/src/Test/L0/Worker/TaskManagerL0.cs b/src/Test/L0/Worker/TaskManagerL0.cs index 0cf81dd9cc..eb82e4f987 100644 --- a/src/Test/L0/Worker/TaskManagerL0.cs +++ b/src/Test/L0/Worker/TaskManagerL0.cs @@ -402,6 +402,7 @@ public void DoesNotMatchPlatform() // Act/Assert. Assert.False(data.PreferredOnPlatform(PlatformUtil.OS.Windows)); Assert.False(data.PreferredOnPlatform(PlatformUtil.OS.Linux)); + Assert.False(data.PreferredOnPlatform(PlatformUtil.OS.FreeBSD)); Assert.False(data.PreferredOnPlatform(PlatformUtil.OS.OSX)); } } @@ -573,6 +574,7 @@ public void LoadsDefinition() [Trait("Category", "Worker")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public void MatchesPlatform() { try @@ -585,6 +587,7 @@ public void MatchesPlatform() // Act/Assert. Assert.True(data.PreferredOnPlatform(PlatformUtil.OS.Windows)); Assert.False(data.PreferredOnPlatform(PlatformUtil.OS.Linux)); + Assert.False(data.PreferredOnPlatform(PlatformUtil.OS.FreeBSD)); Assert.False(data.PreferredOnPlatform(PlatformUtil.OS.OSX)); } } diff --git a/src/Test/L0/Worker/TaskRunnerL0.cs b/src/Test/L0/Worker/TaskRunnerL0.cs index a2c0b26035..5288708078 100644 --- a/src/Test/L0/Worker/TaskRunnerL0.cs +++ b/src/Test/L0/Worker/TaskRunnerL0.cs @@ -69,7 +69,7 @@ public void RunTest(TestHostContext hc, Dictionary variab [Trait("Category", "Worker")] public void GetHandlerHostOnlyTests() { - var nodeData = new NodeHandlerData() { Platforms = new string[] { "linux", "osx" } }; + var nodeData = new NodeHandlerData() { Platforms = new string[] { "linux", "freebsd", "osx" } }; var nodeOnlyExecutionData = new ExecutionData(); nodeOnlyExecutionData.Node = nodeData; var powerShell3Data = new PowerShell3HandlerData() { Platforms = new string[] { "windows" } }; diff --git a/src/Test/L0/Worker/TestResults/Legacy/TestRunPublisherTests.cs b/src/Test/L0/Worker/TestResults/Legacy/TestRunPublisherTests.cs index d40fa73b92..daba94a126 100644 --- a/src/Test/L0/Worker/TestResults/Legacy/TestRunPublisherTests.cs +++ b/src/Test/L0/Worker/TestResults/Legacy/TestRunPublisherTests.cs @@ -185,7 +185,7 @@ public void ReadResultsSendsRunTitleToReader() Assert.Equal("runName", _runContext.RunName); } -#if OS_LINUX +#if OS_LINUX || OS_FREEBSD [Fact] [Trait("Level", "L0")] [Trait("Category", "PublishTestResults")] diff --git a/src/Test/L1/Worker/CoreL1Tests.cs b/src/Test/L1/Worker/CoreL1Tests.cs index 9255fbcf7e..3830bdcd31 100644 --- a/src/Test/L1/Worker/CoreL1Tests.cs +++ b/src/Test/L1/Worker/CoreL1Tests.cs @@ -47,6 +47,7 @@ public async Task Test_Base() [Theory] [Trait("Level", "L1")] + [Trait("SkipOn", "freebsd")] [Trait("Category", "Worker")] [InlineData(false)] [InlineData(true)] @@ -102,6 +103,7 @@ public async Task Test_Base_Node10(bool writeToBlobstorageService) // Remove these SkipOn traits once the task-lib is updated. [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] [Trait("Category", "Worker")] public async Task Input_HandlesTrailingSpace(bool disableInputTrimming, bool writeToBlobstorageService) { diff --git a/src/Test/L1/Worker/SigningL1Tests.cs b/src/Test/L1/Worker/SigningL1Tests.cs index 064214b0aa..8a88139d3d 100644 --- a/src/Test/L1/Worker/SigningL1Tests.cs +++ b/src/Test/L1/Worker/SigningL1Tests.cs @@ -23,6 +23,7 @@ public class SigningL1Tests : L1TestBase // TODO: When NuGet works cross-platform, remove these traits. Also, package NuGet with the Agent. [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public async Task SignatureVerification_PassesWhenAllTasksAreSigned(bool useFingerprintList, bool useTopLevelFingerprint) { try @@ -72,6 +73,7 @@ public async Task SignatureVerification_PassesWhenAllTasksAreSigned(bool useFing // TODO: When NuGet works cross-platform, remove these traits. Also, package NuGet with the Agent. [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public async Task SignatureVerification_FailsWhenTasksArentSigned(bool useFingerprintList, bool useTopLevelFingerprint) { try @@ -113,6 +115,7 @@ public async Task SignatureVerification_FailsWhenTasksArentSigned(bool useFinger [Trait("Category", "Worker")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public async Task SignatureVerification_MultipleFingerprints() { try @@ -149,6 +152,7 @@ public async Task SignatureVerification_MultipleFingerprints() [Trait("Category", "Worker")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] [InlineData(false)] [InlineData(true)] public async Task SignatureVerification_Warning(bool writeToBlobstorageService) @@ -193,6 +197,7 @@ public async Task SignatureVerification_Warning(bool writeToBlobstorageService) [Trait("Category", "Worker")] [Trait("SkipOn", "darwin")] [Trait("SkipOn", "linux")] + [Trait("SkipOn", "freebsd")] public async Task SignatureVerification_Disabled() { try diff --git a/src/dev.sh b/src/dev.sh index 3424075fb3..34e88f6cd1 100755 --- a/src/dev.sh +++ b/src/dev.sh @@ -46,7 +46,7 @@ function detect_platform_and_runtime_id () heading "Platform / RID detection" CURRENT_PLATFORM="windows" - if [[ ($(uname) == "Linux") || ($(uname) == "Darwin") ]]; then + if [[ ($(uname) == "Linux") || ($(uname) == "FreeBSD") || ($(uname) == "Darwin") ]]; then CURRENT_PLATFORM=$(uname | awk '{print tolower($0)}') fi @@ -79,6 +79,8 @@ function detect_platform_and_runtime_id () DETECTED_RUNTIME_ID='linux-musl-arm64' fi fi + elif [[ "$CURRENT_PLATFORM" == 'freebsd' ]]; then + DETECTED_RUNTIME_ID='freebsd-x64' elif [[ "$CURRENT_PLATFORM" == 'darwin' ]]; then DETECTED_RUNTIME_ID='osx-x64' if command -v uname > /dev/null; then @@ -125,7 +127,7 @@ function cmd_layout () make_build "Layout" #change execution flag to allow running with sudo - if [[ ("$CURRENT_PLATFORM" == "linux") || ("$CURRENT_PLATFORM" == "darwin") ]]; then + if [[ ("$CURRENT_PLATFORM" == "linux") || ("$CURRENT_PLATFORM" == "freebsd") || ("$CURRENT_PLATFORM" == "darwin") ]]; then chmod +x "${LAYOUT_DIR}/bin/Agent.Listener" chmod +x "${LAYOUT_DIR}/bin/Agent.Worker" chmod +x "${LAYOUT_DIR}/bin/Agent.PluginHost" @@ -140,7 +142,7 @@ function cmd_test_l0 () { heading "Testing L0" - if [[ ("$CURRENT_PLATFORM" == "linux") || ("$CURRENT_PLATFORM" == "darwin") ]]; then + if [[ ("$CURRENT_PLATFORM" == "linux") || ("$CURRENT_PLATFORM" == "freebsd") || ("$CURRENT_PLATFORM" == "darwin") ]]; then ulimit -n 1024 fi @@ -162,7 +164,7 @@ function cmd_test_l1 () heading "Testing L1" - if [[ ("$CURRENT_PLATFORM" == "linux") || ("$CURRENT_PLATFORM" == "darwin") ]]; then + if [[ ("$CURRENT_PLATFORM" == "linux") || ("$CURRENT_PLATFORM" == "freebsd") || ("$CURRENT_PLATFORM" == "darwin") ]]; then ulimit -n 1024 fi @@ -212,7 +214,7 @@ function cmd_package () pushd "$PACKAGE_DIR" > /dev/null - if [[ ("$CURRENT_PLATFORM" == "linux") || ("$CURRENT_PLATFORM" == "darwin") ]]; then + if [[ ("$CURRENT_PLATFORM" == "linux") || ("$CURRENT_PLATFORM" == "freebsd") || ("$CURRENT_PLATFORM" == "darwin") ]]; then tar_name="${agent_pkg_name}.tar.gz" echo "Creating $tar_name in ${PACKAGE_DIR}" tar -czf "${tar_name}" -C "${LAYOUT_DIR}" . @@ -301,7 +303,7 @@ else RUNTIME_ID=$DETECTED_RUNTIME_ID fi -_VALID_RIDS='linux-x64:linux-arm:linux-arm64:linux-musl-x64:linux-musl-arm64:osx-x64:osx-arm64:win-x64:win-x86' +_VALID_RIDS='linux-x64:linux-arm:linux-arm64:linux-musl-x64:linux-musl-arm64:freebsd-x64:osx-x64:osx-arm64:win-x64:win-x86' if [[ ":$_VALID_RIDS:" != *:$RUNTIME_ID:* ]]; then failed "must specify a valid target runtime ID (one of: $_VALID_RIDS)" fi diff --git a/src/dir.proj b/src/dir.proj index b6b7f2fad9..3851e4c962 100644 --- a/src/dir.proj +++ b/src/dir.proj @@ -79,8 +79,8 @@ - - + + @@ -101,8 +101,8 @@ Properties="Configuration=$(BUILDCONFIG);PackageRuntime=$(PackageRuntime);Version=1.0.0.0;RuntimeIdentifier=$(PackageRuntime);PublishDir=$(L1Root)/bin" /> - - + +