mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
C#: Re-factor Dotnet to enable unit testing.
This commit is contained in:
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
@@ -13,65 +11,29 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
/// </summary>
|
||||
internal partial class DotNet : IDotNet
|
||||
{
|
||||
private readonly IDotnetCommand dotnet;
|
||||
private readonly ProgressMonitor progressMonitor;
|
||||
private readonly string dotnet;
|
||||
|
||||
public DotNet(IDependencyOptions options, ProgressMonitor progressMonitor)
|
||||
internal DotNet(IDotnetCommand dotnet, ProgressMonitor progressMonitor)
|
||||
{
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.dotnet = Path.Combine(options.DotNetPath ?? string.Empty, "dotnet");
|
||||
this.dotnet = dotnet;
|
||||
Info();
|
||||
}
|
||||
|
||||
public DotNet(IDependencyOptions options, ProgressMonitor progressMonitor) : this(new DotnetCommand(progressMonitor, Path.Combine(options.DotNetPath ?? string.Empty, "dotnet")), progressMonitor) { }
|
||||
|
||||
|
||||
private void Info()
|
||||
{
|
||||
// TODO: make sure the below `dotnet` version is matching the one specified in global.json
|
||||
var res = RunCommand("--info");
|
||||
var res = dotnet.RunCommand("--info");
|
||||
if (!res)
|
||||
{
|
||||
throw new Exception($"{dotnet} --info failed.");
|
||||
throw new Exception($"{dotnet.Exec} --info failed.");
|
||||
}
|
||||
}
|
||||
|
||||
private ProcessStartInfo MakeDotnetStartInfo(string args, bool redirectStandardOutput)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo(dotnet, args)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = redirectStandardOutput
|
||||
};
|
||||
// Set the .NET CLI language to English to avoid localized output.
|
||||
startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en";
|
||||
return startInfo;
|
||||
}
|
||||
|
||||
private bool RunCommand(string args)
|
||||
{
|
||||
progressMonitor.RunningProcess($"{dotnet} {args}");
|
||||
using var proc = Process.Start(MakeDotnetStartInfo(args, redirectStandardOutput: false));
|
||||
proc?.WaitForExit();
|
||||
var exitCode = proc?.ExitCode ?? -1;
|
||||
if (exitCode != 0)
|
||||
{
|
||||
progressMonitor.CommandFailed(dotnet, args, exitCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool RunCommand(string args, out IList<string> output)
|
||||
{
|
||||
progressMonitor.RunningProcess($"{dotnet} {args}");
|
||||
var pi = MakeDotnetStartInfo(args, redirectStandardOutput: true);
|
||||
var exitCode = pi.ReadOutput(out output);
|
||||
if (exitCode != 0)
|
||||
{
|
||||
progressMonitor.CommandFailed(dotnet, args, exitCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string GetRestoreArgs(string projectOrSolutionFile, string packageDirectory) =>
|
||||
$"restore --no-dependencies \"{projectOrSolutionFile}\" --packages \"{packageDirectory}\" /p:DisableImplicitNuGetFallbackFolder=true";
|
||||
|
||||
@@ -82,7 +44,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
args += $" --configfile \"{pathToNugetConfig}\"";
|
||||
}
|
||||
var success = RunCommand(args, out var output);
|
||||
var success = dotnet.RunCommand(args, out var output);
|
||||
stdout = string.Join("\n", output);
|
||||
return success;
|
||||
}
|
||||
@@ -91,7 +53,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
var args = GetRestoreArgs(solutionFile, packageDirectory);
|
||||
args += " --verbosity normal";
|
||||
if (RunCommand(args, out var output))
|
||||
if (dotnet.RunCommand(args, out var output))
|
||||
{
|
||||
var regex = RestoreProjectRegex();
|
||||
projects = output
|
||||
@@ -108,13 +70,13 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
public bool New(string folder)
|
||||
{
|
||||
var args = $"new console --no-restore --output \"{folder}\"";
|
||||
return RunCommand(args);
|
||||
return dotnet.RunCommand(args);
|
||||
}
|
||||
|
||||
public bool AddPackage(string folder, string package)
|
||||
{
|
||||
var args = $"add \"{folder}\" package \"{package}\" --no-restore";
|
||||
return RunCommand(args);
|
||||
return dotnet.RunCommand(args);
|
||||
}
|
||||
|
||||
public IList<string> GetListedRuntimes() => GetListed("--list-runtimes", "runtime");
|
||||
@@ -123,7 +85,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
|
||||
private IList<string> GetListed(string args, string artifact)
|
||||
{
|
||||
if (RunCommand(args, out var artifacts))
|
||||
if (dotnet.RunCommand(args, out var artifacts))
|
||||
{
|
||||
progressMonitor.LogInfo($"Found {artifact}s: {string.Join("\n", artifacts)}");
|
||||
return artifacts;
|
||||
@@ -134,7 +96,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
public bool Exec(string execArgs)
|
||||
{
|
||||
var args = $"exec {execArgs}";
|
||||
return RunCommand(args);
|
||||
return dotnet.RunCommand(args);
|
||||
}
|
||||
|
||||
[GeneratedRegex("Restored\\s+(.+\\.csproj)", RegexOptions.Compiled)]
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Semmle.Util;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
/// <summary>
|
||||
/// Low level utilities to run the "dotnet" command.
|
||||
/// </summary>
|
||||
internal sealed class DotnetCommand : IDotnetCommand
|
||||
{
|
||||
private readonly ProgressMonitor progressMonitor;
|
||||
|
||||
public string Exec { get; }
|
||||
|
||||
public DotnetCommand(ProgressMonitor progressMonitor, string exec)
|
||||
{
|
||||
this.progressMonitor = progressMonitor;
|
||||
this.Exec = exec;
|
||||
}
|
||||
|
||||
private ProcessStartInfo MakeDotnetStartInfo(string args, bool redirectStandardOutput)
|
||||
{
|
||||
var startInfo = new ProcessStartInfo(Exec, args)
|
||||
{
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = redirectStandardOutput
|
||||
};
|
||||
// Set the .NET CLI language to English to avoid localized output.
|
||||
startInfo.EnvironmentVariables["DOTNET_CLI_UI_LANGUAGE"] = "en";
|
||||
return startInfo;
|
||||
}
|
||||
|
||||
public bool RunCommand(string args)
|
||||
{
|
||||
progressMonitor.RunningProcess($"{Exec} {args}");
|
||||
using var proc = Process.Start(MakeDotnetStartInfo(args, redirectStandardOutput: false));
|
||||
proc?.WaitForExit();
|
||||
var exitCode = proc?.ExitCode ?? -1;
|
||||
if (exitCode != 0)
|
||||
{
|
||||
progressMonitor.CommandFailed(Exec, args, exitCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RunCommand(string args, out IList<string> output)
|
||||
{
|
||||
progressMonitor.RunningProcess($"{Exec} {args}");
|
||||
var pi = MakeDotnetStartInfo(args, redirectStandardOutput: true);
|
||||
var exitCode = pi.ReadOutput(out output);
|
||||
if (exitCode != 0)
|
||||
{
|
||||
progressMonitor.CommandFailed(Exec, args, exitCode);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Semmle.Extraction.CSharp.DependencyFetching
|
||||
{
|
||||
internal interface IDotnetCommand
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the dotnet executable.
|
||||
/// </summary>
|
||||
string Exec { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Execute `dotnet <args>` and return true if the command succeeded, otherwise false.
|
||||
/// </summary>
|
||||
bool RunCommand(string args);
|
||||
|
||||
/// <summary>
|
||||
/// Execute `dotnet <args>` and return true if the command succeeded, otherwise false.
|
||||
/// The output of the command is returned in `output`.
|
||||
/// </summary>
|
||||
bool RunCommand(string args, out IList<string> output);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user