diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/Config.cs b/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/Config.cs new file mode 100755 index 000000000000..663cd347d712 --- /dev/null +++ b/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/Config.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Text.RegularExpressions; + +namespace Microsoft.DotNet.Installer.Tests; + +public static class Config +{ + public static string AssetsDirectory { get; } = GetRuntimeConfig(AssetsDirectorySwitch); + const string AssetsDirectorySwitch = RuntimeConfigSwitchPrefix + nameof(AssetsDirectory); + + public static string PackagesDirectory { get; } = GetRuntimeConfig(PackagesDirectorySwitch); + const string PackagesDirectorySwitch = RuntimeConfigSwitchPrefix + nameof(PackagesDirectory); + + public static string ScenarioTestsNuGetConfigPath { get; } = GetRuntimeConfig(ScenarioTestsNuGetConfigSwitch); + const string ScenarioTestsNuGetConfigSwitch = RuntimeConfigSwitchPrefix + nameof(ScenarioTestsNuGetConfigPath); + + public static string Architecture { get; } = GetRuntimeConfig(ArchitectureSwitch); + const string ArchitectureSwitch = RuntimeConfigSwitchPrefix + nameof(Architecture); + + public static bool TestRpmPackages { get; } = TryGetRuntimeConfig(TestRpmPackagesSwitch, out bool value) ? value : false; + const string TestRpmPackagesSwitch = RuntimeConfigSwitchPrefix + nameof(TestRpmPackages); + + public static bool TestDebPackages { get; } = TryGetRuntimeConfig(TestDebPackagesSwitch, out bool value) ? value : false; + const string TestDebPackagesSwitch = RuntimeConfigSwitchPrefix + nameof(TestDebPackages); + + public static bool KeepDockerImages { get; } = TryGetRuntimeConfig(KeepDockerImagesSwitch, out bool value) ? value : false; + const string KeepDockerImagesSwitch = RuntimeConfigSwitchPrefix + nameof(KeepDockerImages); + + public const string RuntimeConfigSwitchPrefix = "Microsoft.DotNet.Installer.Tests."; + + public static string GetRuntimeConfig(string key) + { + return TryGetRuntimeConfig(key, out string? value) ? value : throw new InvalidOperationException($"Runtime config setting '{key}' must be specified"); + } + + public static bool TryGetRuntimeConfig(string key, out bool value) + { + string? rawValue = (string?)AppContext.GetData(key); + if (string.IsNullOrEmpty(rawValue)) + { + value = default!; + return false; + } + value = bool.Parse(rawValue); + return true; + } + + public static bool TryGetRuntimeConfig(string key, [NotNullWhen(true)] out string? value) + { + value = (string?)AppContext.GetData(key); + if (string.IsNullOrEmpty(value)) + { + return false; + } + return true; + } +} diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/DockerHelper.cs b/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/DockerHelper.cs new file mode 100755 index 000000000000..678fe02afe2b --- /dev/null +++ b/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/DockerHelper.cs @@ -0,0 +1,258 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using Xunit.Abstractions; + +namespace Microsoft.DotNet.Installer.Tests; + +public class DockerHelper +{ + public static string DockerOS => GetDockerOS(); + public static string DockerArchitecture => GetDockerArch(); + public static string ContainerWorkDir => IsLinuxContainerModeEnabled ? "/sandbox" : "c:\\sandbox"; + public static bool IsLinuxContainerModeEnabled => string.Equals(DockerOS, "linux", StringComparison.OrdinalIgnoreCase); + public static string TestArtifactsDir { get; } = Path.Combine(Directory.GetCurrentDirectory(), "TestAppArtifacts"); + + private ITestOutputHelper OutputHelper { get; set; } + + public DockerHelper(ITestOutputHelper outputHelper) + { + OutputHelper = outputHelper; + } + + public void Build( + string tag, + string? dockerfile = null, + string? target = null, + string contextDir = ".", + bool pull = false, + string? platform = null, + params string[] buildArgs) + { + string buildArgsOption = string.Empty; + if (buildArgs != null) + { + foreach (string arg in buildArgs) + { + buildArgsOption += $" --build-arg {arg}"; + } + } + + string platformOption = string.Empty; + if (platform is not null) + { + platformOption = $" --platform {platform}"; + } + + string targetArg = target == null ? string.Empty : $" --target {target}"; + string dockerfileArg = dockerfile == null ? string.Empty : $" -f {dockerfile}"; + string pullArg = pull ? " --pull" : string.Empty; + + ExecuteWithLogging($"build -t {tag}{targetArg}{buildArgsOption}{dockerfileArg}{pullArg}{platformOption} {contextDir}"); + } + + + public static bool ContainerExists(string name) => ResourceExists("container", $"-f \"name={name}\""); + + public static bool ContainerIsRunning(string name) => Execute($"inspect --format=\"{{{{.State.Running}}}}\" {name}") == "true"; + + public void Copy(string src, string dest) => ExecuteWithLogging($"cp {src} {dest}"); + + public void DeleteContainer(string container, bool captureLogs = false) + { + if (ContainerExists(container)) + { + if (captureLogs) + { + ExecuteWithLogging($"logs {container}", ignoreErrors: true); + } + + // If a container is already stopped, running `docker stop` again has no adverse effects. + // This prevents some issues where containers could fail to be forcibly removed while they're running. + // e.g. https://github.com/dotnet/dotnet-docker/issues/5127 + StopContainer(container); + + ExecuteWithLogging($"container rm -f {container}"); + } + } + + public void DeleteImage(string tag) + { + if (ImageExists(tag)) + { + ExecuteWithLogging($"image rm -f {tag}"); + } + } + + private void StopContainer(string container) + { + if (ContainerExists(container)) + { + ExecuteWithLogging($"stop {container}", autoRetry: true); + } + } + + private static string Execute( + string args, bool ignoreErrors = false, bool autoRetry = false, ITestOutputHelper? outputHelper = null) + { + (Process Process, string StdOut, string StdErr) result; + if (autoRetry) + { + result = ExecuteWithRetry(args, outputHelper!, ExecuteProcess); + } + else + { + result = ExecuteProcess(args, outputHelper!); + } + + if (!ignoreErrors && result.Process.ExitCode != 0) + { + ProcessStartInfo startInfo = result.Process.StartInfo; + string msg = $"Failed to execute {startInfo.FileName} {startInfo.Arguments}" + + $"{Environment.NewLine}Exit code: {result.Process.ExitCode}" + + $"{Environment.NewLine}Standard Error: {result.StdErr}"; + throw new InvalidOperationException(msg); + } + + return result.StdOut; + } + + private static (Process Process, string StdOut, string StdErr) ExecuteProcess( + string args, ITestOutputHelper outputHelper) => ExecuteProcess("docker", args, outputHelper); + + private string ExecuteWithLogging(string args, bool ignoreErrors = false, bool autoRetry = false) + { + Stopwatch stopwatch = new Stopwatch(); + stopwatch.Start(); + + OutputHelper.WriteLine($"Executing: docker {args}"); + string result = Execute(args, outputHelper: OutputHelper, ignoreErrors: ignoreErrors, autoRetry: autoRetry); + + stopwatch.Stop(); + OutputHelper.WriteLine($"Execution Elapsed Time: {stopwatch.Elapsed}"); + + return result; + } + + private static (Process Process, string StdOut, string StdErr) ExecuteWithRetry( + string args, + ITestOutputHelper outputHelper, + Func executor) + { + const int maxRetries = 5; + const int waitFactor = 5; + + int retryCount = 0; + + (Process Process, string StdOut, string StdErr) result = executor(args, outputHelper); + while (result.Process.ExitCode != 0) + { + retryCount++; + if (retryCount >= maxRetries) + { + break; + } + + int waitTime = Convert.ToInt32(Math.Pow(waitFactor, retryCount - 1)); + if (outputHelper != null) + { + outputHelper.WriteLine($"Retry {retryCount}/{maxRetries}, retrying in {waitTime} seconds..."); + } + + Thread.Sleep(waitTime * 1000); + result = executor(args, outputHelper!); + } + + return result; + } + + private static (Process Process, string StdOut, string StdErr) ExecuteProcess( + string fileName, string args, ITestOutputHelper outputHelper) + { + Process process = new Process + { + EnableRaisingEvents = true, + StartInfo = + { + FileName = fileName, + Arguments = args, + RedirectStandardOutput = true, + RedirectStandardError = true, + } + }; + + StringBuilder stdOutput = new StringBuilder(); + process.OutputDataReceived += new DataReceivedEventHandler((sender, e) => stdOutput.AppendLine(e.Data)); + + StringBuilder stdError = new StringBuilder(); + process.ErrorDataReceived += new DataReceivedEventHandler((sender, e) => stdError.AppendLine(e.Data)); + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(); + + string output = stdOutput.ToString().Trim(); + if (outputHelper != null && !string.IsNullOrWhiteSpace(output)) + { + outputHelper.WriteLine(output); + } + + string error = stdError.ToString().Trim(); + if (outputHelper != null && !string.IsNullOrWhiteSpace(error)) + { + outputHelper.WriteLine(error); + } + + return (process, output, error); + } + + private static string GetDockerOS() => Execute("version -f \"{{ .Server.Os }}\""); + private static string GetDockerArch() => Execute("version -f \"{{ .Server.Arch }}\""); + + public string GetImageUser(string image) => ExecuteWithLogging($"inspect -f \"{{{{ .Config.User }}}}\" {image}"); + + public static bool ImageExists(string tag) => ResourceExists("image", tag); + + private static bool ResourceExists(string type, string filterArg) + { + string output = Execute($"{type} ls -a -q {filterArg}", true); + return output != ""; + } + + public string Run( + string image, + string name, + string? command = null, + string? workdir = null, + string? optionalRunArgs = null, + bool detach = false, + string? runAsUser = null, + bool skipAutoCleanup = false, + bool useMountedDockerSocket = false, + bool silenceOutput = false, + bool tty = true) + { + string cleanupArg = skipAutoCleanup ? string.Empty : " --rm"; + string detachArg = detach ? " -d" : string.Empty; + string ttyArg = detach && tty ? " -t" : string.Empty; + string userArg = runAsUser != null ? $" -u {runAsUser}" : string.Empty; + string workdirArg = workdir == null ? string.Empty : $" -w {workdir}"; + string mountedDockerSocketArg = useMountedDockerSocket ? " -v /var/run/docker.sock:/var/run/docker.sock" : string.Empty; + if (silenceOutput) + { + return Execute( + $"run --name {name}{cleanupArg}{workdirArg}{userArg}{detachArg}{ttyArg}{mountedDockerSocketArg} {optionalRunArgs} {image} {command}"); + } + return ExecuteWithLogging( + $"run --name {name}{cleanupArg}{workdirArg}{userArg}{detachArg}{ttyArg}{mountedDockerSocketArg} {optionalRunArgs} {image} {command}", ignoreErrors: true); + } +} diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/LinuxInstallerTests.cs b/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/LinuxInstallerTests.cs new file mode 100755 index 000000000000..236c6ddc8828 --- /dev/null +++ b/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/LinuxInstallerTests.cs @@ -0,0 +1,365 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.VisualStudio.TestPlatform.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net.Http; +using System.Runtime.Intrinsics.Arm; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using TestUtilities; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.DotNet.Installer.Tests; + +public class LinuxInstallerTests : IDisposable +{ + private readonly DockerHelper _dockerHelper; + private readonly string _tmpDir; + private readonly string _contextDir; + private readonly ITestOutputHelper _outputHelper; + + private bool _rpmContextInitialized = false; + private bool _debContextInitialized = false; + private bool _sharedContextInitialized = false; + + private const string NetStandard21RpmPackage = @"https://dotnetcli.blob.core.windows.net/dotnet/Runtime/3.1.0/netstandard-targeting-pack-2.1.0-x64.rpm"; + private const string NetStandard21DebPackage = @"https://dotnetcli.blob.core.windows.net/dotnet/Runtime/3.1.0/netstandard-targeting-pack-2.1.0-x64.deb"; + private const string RuntimeDepsRepo = "mcr.microsoft.com/dotnet/nightly/runtime-deps"; + private const string RuntimeDepsVersion = "10.0-preview"; + + public static bool IncludeRpmTests => Config.TestRpmPackages; + public static bool IncludeDebTests => Config.TestDebPackages; + + private enum PackageType + { + Rpm, + Deb + } + + public LinuxInstallerTests(ITestOutputHelper outputHelper) + { + _outputHelper = outputHelper; + _dockerHelper = new DockerHelper(_outputHelper); + + _tmpDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(_tmpDir); + _contextDir = Path.Combine(_tmpDir, Path.GetRandomFileName()); + Directory.CreateDirectory(_contextDir); + } + + public void Dispose() + { + try + { + //Directory.Delete(_tmpDir, recursive: true); + } + catch + { + } + } + + [ConditionalTheory(typeof(LinuxInstallerTests), nameof(IncludeRpmTests))] + [InlineData(RuntimeDepsRepo, $"{RuntimeDepsVersion}-azurelinux3.0")] + public void RpmTest(string repo, string tag) + { + if (!tag.Contains("azurelinux")) + { + // Only Azure Linux is currently supported for RPM tests + Assert.Fail("Only Azure Linux is currently supported for RPM tests"); + } + + InitializeContext(PackageType.Rpm); + + DistroTest($"{repo}:{tag}", PackageType.Rpm); + } + + [ConditionalTheory(typeof(LinuxInstallerTests), nameof(IncludeDebTests))] + [InlineData(RuntimeDepsRepo, $"{RuntimeDepsVersion}-trixie-slim")] + public void DebTest(string repo, string tag) + { + InitializeContext(PackageType.Deb); + + DistroTest($"{repo}:{tag}", PackageType.Deb); + } + + private void InitializeContext(PackageType packageType) + { + if (packageType == PackageType.Rpm && !_rpmContextInitialized) + { + // For rpm enumerate RPM packages, excluding those that contain ".cm." in the name + List rpmPackages = Directory.GetFiles(Config.AssetsDirectory, "*.rpm", SearchOption.AllDirectories) + .Where(p => !Path.GetFileName(p).Contains("-cm.") && !Path.GetFileName(p).EndsWith("azl.rpm")) + .ToList(); + + foreach (string rpmPackage in rpmPackages) + { + File.Copy(rpmPackage, Path.Combine(_contextDir, Path.GetFileName(rpmPackage))); + } + + DownloadFileAsync(NetStandard21RpmPackage, Path.Combine(_contextDir, Path.GetFileName(NetStandard21RpmPackage))).Wait(); + _rpmContextInitialized = true; + } + else if (!_debContextInitialized) + { + // Copy all DEB packages as well + foreach (string debPackage in Directory.GetFiles(Config.AssetsDirectory, "*.deb", SearchOption.AllDirectories)) + { + File.Copy(debPackage, Path.Combine(_contextDir, Path.GetFileName(debPackage))); + } + + // Download NetStandard 2.1 packages + DownloadFileAsync(NetStandard21DebPackage, Path.Combine(_contextDir, Path.GetFileName(NetStandard21DebPackage))).Wait(); + _debContextInitialized = true; + } + + if (!_sharedContextInitialized) + { + // Copy nuget packages + string nugetPackagesDir = Path.Combine(_contextDir, "packages"); + Directory.CreateDirectory(nugetPackagesDir); + foreach (string package in Directory.GetFiles(Config.PackagesDirectory, "*.nupkg", SearchOption.AllDirectories)) + { + File.Copy(package, Path.Combine(nugetPackagesDir, Path.GetFileName(package))); + } + + // Copy and update NuGet.config from scenario-tests repo + string newNuGetConfig = Path.Combine(_contextDir, "NuGet.config"); + File.Copy(Config.ScenarioTestsNuGetConfigPath, newNuGetConfig); + InsertLocalPackagesPathToNuGetConfig(newNuGetConfig, "/packages"); + + // Find the scenario-tests package and unpack it to the context dir, subfolder "scenario-tests" + string? scenarioTestsPackage = Directory.GetFiles(nugetPackagesDir, "Microsoft.DotNet.ScenarioTests.SdkTemplateTests*.nupkg", SearchOption.AllDirectories).FirstOrDefault(); + if (scenarioTestsPackage == null) + { + Assert.Fail("Scenario tests package not found"); + } + + ZipFile.ExtractToDirectory(scenarioTestsPackage, Path.Combine(_contextDir, "scenario-tests")); + _sharedContextInitialized = true; + } + } + + private void InsertLocalPackagesPathToNuGetConfig(string nuGetConfig, string localPackagesPath) + { + XDocument doc = XDocument.Load(nuGetConfig); + if (doc.Root != null) + { + XElement? packageSourcesElement = doc.Root.Element("packageSources"); + if (packageSourcesElement != null) + { + XElement? clearElement = packageSourcesElement.Element("clear"); + if (clearElement != null) + { + XElement newAddElement = new XElement("add", + new XAttribute("key", "local-packages"), + new XAttribute("value", localPackagesPath)); + + clearElement.AddAfterSelf(newAddElement); + } + } + + doc.Save(nuGetConfig); + } + } + + private void DistroTest(string baseImage, PackageType packageType) + { + List packageList = GetPackageList(baseImage, packageType); + string dockerfile = GenerateDockerfile(packageList, baseImage, packageType); + string testCommand = $"dotnet {GetScenarioTestsBinaryPath()} --dotnet-root /usr/share/dotnet/"; + + string tag = $"test-{Path.GetRandomFileName()}"; + string output = ""; + bool buildCompleted = false; + + try + { + // Build docker image and run the tests + _dockerHelper.Build(tag, dockerfile: dockerfile, contextDir: _contextDir); + buildCompleted = true; + output = _dockerHelper.Run(tag, tag, testCommand); + + int testResultsSummaryIndex = output.IndexOf("Tests run: "); + if (testResultsSummaryIndex >= 0) + { + string testResultsSummary = output[testResultsSummaryIndex..]; + Assert.False(AnyTestFailures(testResultsSummary), testResultsSummary); + } + else + { + Assert.Fail("Test summary not found"); + } + } + catch (Exception e) + { + if (string.IsNullOrEmpty(output)) + { + output = e.Message; + } + Assert.Fail($"{(buildCompleted ? "Build" : "Test")} failed: {output}"); + } + finally + { + if (!Config.KeepDockerImages) + { + _dockerHelper.DeleteImage(tag); + } + } + } + + private string GetScenarioTestsBinaryPath() + { + // Find scenario-tests binary in context/scenario-tests + string? scenarioTestsBinary = Directory.GetFiles(Path.Combine(_contextDir, "scenario-tests"), "Microsoft.DotNet.ScenarioTests.SdkTemplateTests.dll", SearchOption.AllDirectories).FirstOrDefault(); + if (scenarioTestsBinary == null) + { + throw new Exception("Scenario tests binary not found"); + } + + return scenarioTestsBinary.Replace(_contextDir, "").Replace("\\", "/"); + } + + private List GetPackageList(string baseImage, PackageType packageType) + { + // Order of installation is important as we do not want to use "--nodeps" + // We install in correct order, so package dependencies are present. + + // Prepare the package list in correct install order + List packageList = + [ + // Deps package should be installed first + Path.GetFileName(GetMatchingDepsPackage(baseImage, packageType)) + ]; + + // Add all other packages in correct install order + AddPackage(packageList, "dotnet-host-", packageType); + AddPackage(packageList, "dotnet-hostfxr-", packageType); + AddPackage(packageList, "dotnet-runtime-", packageType); + AddPackage(packageList, "dotnet-targeting-pack-", packageType); + AddPackage(packageList, "aspnetcore-runtime-", packageType); + AddPackage(packageList, "aspnetcore-targeting-pack-", packageType); + AddPackage(packageList, "dotnet-apphost-pack-", packageType); + if (Config.Architecture == "x64") + { + // netstandard package exists for x64 only + AddPackage(packageList, "netstandard-targeting-pack-", packageType); + } + AddPackage(packageList, "dotnet-sdk-", packageType); + + return packageList; + } + + private string GenerateDockerfile(List packageList, string baseImage, PackageType packageType) + { + StringBuilder sb = new(); + sb.AppendLine("FROM " + baseImage); + sb.AppendLine(""); + sb.AppendLine("# Copy NuGet.config"); + sb.AppendLine($"COPY NuGet.config ."); + + sb.AppendLine(""); + sb.AppendLine("# Copy scenario-tests content"); + sb.AppendLine($"COPY scenario-tests scenario-tests"); + + sb.AppendLine(""); + sb.AppendLine("# Copy nuget packages"); + sb.AppendLine($"COPY packages packages"); + + sb.AppendLine(""); + sb.AppendLine("# Copy installer packages"); + foreach (string package in packageList) + { + sb.AppendLine($"COPY {package} {package}"); + } + sb.AppendLine(""); + sb.AppendLine("# Install the installer packages and Microsoft.DotNet.ScenarioTests.SdkTemplateTests tool"); + sb.Append("RUN"); + + // TODO: remove --force-all after deps image issue has been resolved - https://github.com/dotnet/dotnet-docker/issues/6271 + string packageInstallationCommand = packageType == PackageType.Deb ? "dpkg -i --force-all" : "rpm -i"; + bool useAndOperator = false; + foreach (string package in packageList) + { + sb.AppendLine(" \\"); + sb.Append($" {(useAndOperator ? "&&" : "")} {packageInstallationCommand} {package}"); + useAndOperator = true; + } + sb.AppendLine(""); + + // Set environment for nuget.config + sb.AppendLine(""); + sb.AppendLine("# Set custom nuget.config"); + sb.AppendLine("ENV RestoreConfigFile=/NuGet.config"); + + string dockerfile = Path.Combine(_contextDir, $"Dockerfile-{Path.GetRandomFileName()}"); + File.WriteAllText(dockerfile, sb.ToString()); + return dockerfile; + } + + private bool AnyTestFailures(string testResultSummary) + { + var parts = testResultSummary.Split(',') + .Select(part => part.Split(':').Select(p => p.Trim()).ToArray()) + .Where(p => p.Length == 2) + .ToDictionary(p => p[0], p => int.Parse(p[1])); + + return parts["Errors"] > 0 || parts["Failures"] > 0; + } + + private void AddPackage(List packageList, string prefix, PackageType packageType) + { + packageList.Add(Path.GetFileName(GetContentPackage(prefix, packageType))); + } + + private string GetContentPackage(string prefix, PackageType packageType) + { + string matchPattern = PackageType.Deb == packageType ? "*.deb" : "*.rpm"; + string[] files = Directory.GetFiles(_contextDir, prefix + matchPattern, SearchOption.AllDirectories) + .Where(p => !Path.GetFileName(p).Contains("dotnet-runtime-deps-")) + .ToArray(); + if (files.Length == 0) + { + throw new Exception($"RPM package with prefix '{prefix}' not found"); + } + + return files.OrderByDescending(f => f).First(); + } + + private string GetMatchingDepsPackage(string baseImage, PackageType packageType) + { + string matchPattern = packageType == PackageType.Deb + ? "dotnet-runtime-deps-*.deb" + : "dotnet-runtime-deps-*azl*.rpm"; // We currently only support Azure Linux deps image + + string[] files = Directory.GetFiles(_contextDir, matchPattern, SearchOption.AllDirectories); + if (files.Length == 0) + { + throw new Exception($"Did not find the DEPS package."); + } + + return files.OrderByDescending(f => f).First(); + } + + private static async Task DownloadFileAsync(string url, string filePath) + { + using (HttpClient client = new HttpClient()) + { + HttpResponseMessage response = await client.GetAsync(url); + response.EnsureSuccessStatusCode(); + + using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) + { + await response.Content.CopyToAsync(fileStream); + } + } + } +} + diff --git a/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/Microsoft.DotNet.Installer.Tests.csproj b/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/Microsoft.DotNet.Installer.Tests.csproj new file mode 100755 index 000000000000..3e3707bb6cdf --- /dev/null +++ b/src/SourceBuild/content/test/Microsoft.DotNet.Installer.Tests/Microsoft.DotNet.Installer.Tests.csproj @@ -0,0 +1,41 @@ + + + + $(NetCurrent) + console%3bverbosity=normal;trx%3bverbosity=diagnostic%3bLogFileName=$(MSBuildProjectName).trx + $(VSTestCLIRunSettings);RunConfiguration.DotNetHostPath=$(DotnetTool) + + + + + + + + + + + $(ArtifactsAssetsDir) + + + $(ArtifactsPackagesDir) + + + $(RepoRoot)src\scenario-tests\NuGet.config + + + $(HostArchitecture) + + + $(TestRpmPackages) + + + $(TestDebPackages) + + + $(KeepDockerImages) + + + + +