mirror of
https://github.com/github/codeql.git
synced 2025-12-16 16:53:25 +01:00
Merge pull request #20936 from michaelnebel/csharp/nocrashdotnetinfo
C#: Retry logic for `dotnet --info` when it fails with exit code 143.
This commit is contained in:
@@ -48,7 +48,7 @@ namespace Semmle.Autobuild.CSharp
|
|||||||
{
|
{
|
||||||
// When a custom .NET CLI has been installed, `dotnet --info` has already been executed
|
// When a custom .NET CLI has been installed, `dotnet --info` has already been executed
|
||||||
// to verify the installation.
|
// to verify the installation.
|
||||||
var ret = dotNetPath is null ? GetInfoCommand(builder.Actions, dotNetPath, environment) : BuildScript.Success;
|
var ret = dotNetPath is null ? DotNet.InfoScript(builder.Actions, DotNetCommand(builder.Actions, dotNetPath), environment, builder.Logger) : BuildScript.Success;
|
||||||
foreach (var projectOrSolution in builder.ProjectsOrSolutionsToBuild)
|
foreach (var projectOrSolution in builder.ProjectsOrSolutionsToBuild)
|
||||||
{
|
{
|
||||||
var cleanCommand = GetCleanCommand(builder.Actions, dotNetPath, environment);
|
var cleanCommand = GetCleanCommand(builder.Actions, dotNetPath, environment);
|
||||||
@@ -111,14 +111,6 @@ namespace Semmle.Autobuild.CSharp
|
|||||||
private static string DotNetCommand(IBuildActions actions, string? dotNetPath) =>
|
private static string DotNetCommand(IBuildActions actions, string? dotNetPath) =>
|
||||||
dotNetPath is not null ? actions.PathCombine(dotNetPath, "dotnet") : "dotnet";
|
dotNetPath is not null ? actions.PathCombine(dotNetPath, "dotnet") : "dotnet";
|
||||||
|
|
||||||
private static BuildScript GetInfoCommand(IBuildActions actions, string? dotNetPath, IDictionary<string, string>? environment)
|
|
||||||
{
|
|
||||||
var info = new CommandBuilder(actions, null, environment).
|
|
||||||
RunCommand(DotNetCommand(actions, dotNetPath)).
|
|
||||||
Argument("--info");
|
|
||||||
return info.Script;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static CommandBuilder GetCleanCommand(IBuildActions actions, string? dotNetPath, IDictionary<string, string>? environment)
|
private static CommandBuilder GetCleanCommand(IBuildActions actions, string? dotNetPath, IDictionary<string, string>? environment)
|
||||||
{
|
{
|
||||||
var clean = new CommandBuilder(actions, null, environment).
|
var clean = new CommandBuilder(actions, null, environment).
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
using Semmle.Util;
|
using Semmle.Util;
|
||||||
@@ -36,12 +37,29 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
|||||||
|
|
||||||
public static IDotNet Make(ILogger logger, string? dotNetPath, TemporaryDirectory tempWorkingDirectory, DependabotProxy? dependabotProxy) => new DotNet(logger, dotNetPath, tempWorkingDirectory, dependabotProxy);
|
public static IDotNet Make(ILogger logger, string? dotNetPath, TemporaryDirectory tempWorkingDirectory, DependabotProxy? dependabotProxy) => new DotNet(logger, dotNetPath, tempWorkingDirectory, dependabotProxy);
|
||||||
|
|
||||||
|
private static void HandleRetryExitCode143(string dotnet, int attempt, ILogger logger)
|
||||||
|
{
|
||||||
|
logger.LogWarning($"Running '{dotnet} --info' failed with exit code 143. Retrying...");
|
||||||
|
var sleep = Math.Pow(2, attempt) * 1000;
|
||||||
|
Thread.Sleep((int)sleep);
|
||||||
|
}
|
||||||
|
|
||||||
private void Info()
|
private void Info()
|
||||||
{
|
{
|
||||||
var res = dotnetCliInvoker.RunCommand("--info", silent: false);
|
// Allow up to four attempts (with up to three retries) to run `dotnet --info`, to mitigate transient issues
|
||||||
if (!res)
|
for (int attempt = 0; attempt < 4; attempt++)
|
||||||
{
|
{
|
||||||
throw new Exception($"{dotnetCliInvoker.Exec} --info failed.");
|
var exitCode = dotnetCliInvoker.RunCommandExitCode("--info", silent: false);
|
||||||
|
switch (exitCode)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return;
|
||||||
|
case 143 when attempt < 3:
|
||||||
|
HandleRetryExitCode143(dotnetCliInvoker.Exec, attempt, logger);
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
throw new Exception($"{dotnetCliInvoker.Exec} --info failed with exit code {exitCode}.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,6 +211,35 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
|||||||
return BuildScript.Failure;
|
return BuildScript.Failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a script for running `dotnet --info`, with retries on exit code 143.
|
||||||
|
/// </summary>
|
||||||
|
public static BuildScript InfoScript(IBuildActions actions, string dotnet, IDictionary<string, string>? environment, ILogger logger)
|
||||||
|
{
|
||||||
|
var info = new CommandBuilder(actions, null, environment).
|
||||||
|
RunCommand(dotnet).
|
||||||
|
Argument("--info");
|
||||||
|
var script = info.Script;
|
||||||
|
for (var attempt = 0; attempt < 4; attempt++)
|
||||||
|
{
|
||||||
|
var attemptCopy = attempt; // Capture in local variable
|
||||||
|
script = BuildScript.Bind(script, ret =>
|
||||||
|
{
|
||||||
|
switch (ret)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
return BuildScript.Success;
|
||||||
|
case 143 when attemptCopy < 3:
|
||||||
|
HandleRetryExitCode143(dotnet, attemptCopy, logger);
|
||||||
|
return info.Script;
|
||||||
|
default:
|
||||||
|
return BuildScript.Failure;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return script;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a script for downloading specific .NET SDK versions, if the
|
/// Returns a script for downloading specific .NET SDK versions, if the
|
||||||
/// versions are not already installed.
|
/// versions are not already installed.
|
||||||
@@ -292,9 +339,7 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var dotnetInfo = new CommandBuilder(actions, environment: MinimalEnvironment).
|
var dotnetInfo = InfoScript(actions, actions.PathCombine(path, "dotnet"), MinimalEnvironment.ToDictionary(), logger);
|
||||||
RunCommand(actions.PathCombine(path, "dotnet")).
|
|
||||||
Argument("--info").Script;
|
|
||||||
|
|
||||||
Func<string, BuildScript> getInstallAndVerify = version =>
|
Func<string, BuildScript> getInstallAndVerify = version =>
|
||||||
// run `dotnet --info` after install, to check that it executes successfully
|
// run `dotnet --info` after install, to check that it executes successfully
|
||||||
|
|||||||
@@ -57,15 +57,21 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
|||||||
return startInfo;
|
return startInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool RunCommandAux(string args, string? workingDirectory, out IList<string> output, bool silent)
|
private int RunCommandExitCodeAux(string args, string? workingDirectory, out IList<string> output, out string dirLog, bool silent)
|
||||||
{
|
{
|
||||||
var dirLog = string.IsNullOrWhiteSpace(workingDirectory) ? "" : $" in {workingDirectory}";
|
dirLog = string.IsNullOrWhiteSpace(workingDirectory) ? "" : $" in {workingDirectory}";
|
||||||
var pi = MakeDotnetStartInfo(args, workingDirectory);
|
var pi = MakeDotnetStartInfo(args, workingDirectory);
|
||||||
var threadId = Environment.CurrentManagedThreadId;
|
var threadId = Environment.CurrentManagedThreadId;
|
||||||
void onOut(string s) => logger.Log(silent ? Severity.Debug : Severity.Info, s, threadId);
|
void onOut(string s) => logger.Log(silent ? Severity.Debug : Severity.Info, s, threadId);
|
||||||
void onError(string s) => logger.LogError(s, threadId);
|
void onError(string s) => logger.LogError(s, threadId);
|
||||||
logger.LogInfo($"Running '{Exec} {args}'{dirLog}");
|
logger.LogInfo($"Running '{Exec} {args}'{dirLog}");
|
||||||
var exitCode = pi.ReadOutput(out output, onOut, onError);
|
var exitCode = pi.ReadOutput(out output, onOut, onError);
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool RunCommandAux(string args, string? workingDirectory, out IList<string> output, bool silent)
|
||||||
|
{
|
||||||
|
var exitCode = RunCommandExitCodeAux(args, workingDirectory, out output, out var dirLog, silent);
|
||||||
if (exitCode != 0)
|
if (exitCode != 0)
|
||||||
{
|
{
|
||||||
logger.LogError($"Command '{Exec} {args}'{dirLog} failed with exit code {exitCode}");
|
logger.LogError($"Command '{Exec} {args}'{dirLog} failed with exit code {exitCode}");
|
||||||
@@ -77,6 +83,9 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
|||||||
public bool RunCommand(string args, bool silent = true) =>
|
public bool RunCommand(string args, bool silent = true) =>
|
||||||
RunCommandAux(args, null, out _, silent);
|
RunCommandAux(args, null, out _, silent);
|
||||||
|
|
||||||
|
public int RunCommandExitCode(string args, bool silent = true) =>
|
||||||
|
RunCommandExitCodeAux(args, null, out _, out _, silent);
|
||||||
|
|
||||||
public bool RunCommand(string args, out IList<string> output, bool silent = true) =>
|
public bool RunCommand(string args, out IList<string> output, bool silent = true) =>
|
||||||
RunCommandAux(args, null, out output, silent);
|
RunCommandAux(args, null, out output, silent);
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,12 @@ namespace Semmle.Extraction.CSharp.DependencyFetching
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
bool RunCommand(string args, bool silent = true);
|
bool RunCommand(string args, bool silent = true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Execute `dotnet <paramref name="args"/>` and return the exit code.
|
||||||
|
/// If `silent` is true the output of the command is logged as `debug` otherwise as `info`.
|
||||||
|
/// </summary>
|
||||||
|
int RunCommandExitCode(string args, bool silent = true);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Execute `dotnet <paramref name="args"/>` and return true if the command succeeded, otherwise false.
|
/// Execute `dotnet <paramref name="args"/>` and return true if the command succeeded, otherwise false.
|
||||||
/// The output of the command is returned in `output`.
|
/// The output of the command is returned in `output`.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Semmle.Extraction.Tests
|
|||||||
private string lastArgs = "";
|
private string lastArgs = "";
|
||||||
public string WorkingDirectory { get; private set; } = "";
|
public string WorkingDirectory { get; private set; } = "";
|
||||||
public bool Success { get; set; } = true;
|
public bool Success { get; set; } = true;
|
||||||
|
public int ExitCode { get; set; } = 0;
|
||||||
|
|
||||||
public DotNetCliInvokerStub(IList<string> output)
|
public DotNetCliInvokerStub(IList<string> output)
|
||||||
{
|
{
|
||||||
@@ -26,6 +27,12 @@ namespace Semmle.Extraction.Tests
|
|||||||
return Success;
|
return Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int RunCommandExitCode(string args, bool silent)
|
||||||
|
{
|
||||||
|
lastArgs = args;
|
||||||
|
return ExitCode;
|
||||||
|
}
|
||||||
|
|
||||||
public bool RunCommand(string args, out IList<string> output, bool silent)
|
public bool RunCommand(string args, out IList<string> output, bool silent)
|
||||||
{
|
{
|
||||||
lastArgs = args;
|
lastArgs = args;
|
||||||
@@ -83,7 +90,7 @@ namespace Semmle.Extraction.Tests
|
|||||||
public void TestDotnetInfoFailure()
|
public void TestDotnetInfoFailure()
|
||||||
{
|
{
|
||||||
// Setup
|
// Setup
|
||||||
var dotnetCliInvoker = new DotNetCliInvokerStub(new List<string>()) { Success = false };
|
var dotnetCliInvoker = new DotNetCliInvokerStub(new List<string>()) { ExitCode = 1 };
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
try
|
try
|
||||||
@@ -94,7 +101,7 @@ namespace Semmle.Extraction.Tests
|
|||||||
// Verify
|
// Verify
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Assert.Equal("dotnet --info failed.", e.Message);
|
Assert.Equal("dotnet --info failed with exit code 1.", e.Message);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Assert.Fail("Expected exception");
|
Assert.Fail("Expected exception");
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@pytest.mark.skip(reason=".NET 10 info command crashes")
|
|
||||||
def test1(codeql, csharp):
|
def test1(codeql, csharp):
|
||||||
codeql.database.create()
|
codeql.database.create()
|
||||||
|
|
||||||
@pytest.mark.skip(reason=".NET 10 info command crashes")
|
|
||||||
def test2(codeql, csharp):
|
def test2(codeql, csharp):
|
||||||
codeql.database.create(build_mode="none")
|
codeql.database.create(build_mode="none")
|
||||||
|
|||||||
Reference in New Issue
Block a user