Skip to content

Commit

Permalink
feat(nuget): Make nuget restore errors non fatal (#3072)
Browse files Browse the repository at this point in the history
* fix: make nuget restore errors non fatal

* misc: add nuget retry logic

* fix: sonar issues
  • Loading branch information
dupdob authored Nov 8, 2024
1 parent 4ba906b commit b7f9cca
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public void HappyFlow()

[TestMethodWithIgnoreIfSupport]
[IgnoreIf(nameof(Is.Unix))] //DotnetFramework does not run on Unix
public void ThrowIfRestoreFails()
public void RetryIfRestoreFails()
{
var nugetPath = @"C:\choco\bin\NuGet.exe";
var msBuildVersion = "16.0.0";
Expand Down Expand Up @@ -112,19 +112,21 @@ public void ThrowIfRestoreFails()
{
ExitCode = 1,
Output = "Packages restore failed."
});
processExecutorMock.Setup(x => x.Start(It.Is<string>(s => s.Contains("Microsoft Visual Studio")), It.Is<string>(s => s.Contains("vswhere.exe")),
@"-latest -requires Microsoft.Component.MSBuild -products * -find MSBuild\**\Bin\MSBuild.exe", null, It.IsAny<int>()))
}).Verifiable(Times.Once);

processExecutorMock.Setup(x => x.Start(nugetDirectory, nugetPath,
$"restore \"{Path.GetFullPath(SolutionPath)}\"", null, It.IsAny<int>()))
.Returns(new ProcessResult()
{
ExitCode = 0,
Output = "Msbuild executable path found at "
});
var target = new NugetRestoreProcess(processExecutorMock.Object);
ExitCode = 1,
Output = "Packages restore failed."
}).Verifiable(Times.Once);

var action = () => target.RestorePackages(SolutionPath);
var target = new NugetRestoreProcess(processExecutorMock.Object);

target.RestorePackages(SolutionPath);

action.ShouldThrow<InputException>("Packages restore failed.");
processExecutorMock.VerifyAll();
}

[TestMethodWithIgnoreIfSupport]
Expand Down
17 changes: 10 additions & 7 deletions src/Stryker.Core/Stryker.Core/Initialisation/InputFileResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,15 +256,18 @@ private IAnalyzerResults AnalyzeSingleProject(IProjectAnalyzer project, IStryker

if (!buildResultOverallSuccess)
{
if (options.DevMode)
{
// clear the logs to remove the noise
_buildalyzerLog.GetStringBuilder().Clear();
}
// if this is a full framework project, we can retry after a nuget restore
if (buildResult.Any(r => !IsValid(r) && r.TargetsFullFramework()))
{
_logger.LogWarning("Project {projectFilePath} analysis failed. Stryker will retry after a nuget restore.", projectLogName);

if (options.DevMode)
{
_logger.LogWarning("The MsBuild log is below.");
_logger.LogInformation(_buildalyzerLog.ToString());
_buildalyzerLog.GetStringBuilder().Clear();
}

_nugetRestoreProcess.RestorePackages(options.SolutionPath, options.MsBuildPath ?? buildResult.First().MsBuildPath());
}
var buildOptions = new EnvironmentOptions
Expand All @@ -279,10 +282,10 @@ private IAnalyzerResults AnalyzeSingleProject(IProjectAnalyzer project, IStryker
buildResult.Any(br => IsValid(br) && br.TargetFramework == tf));
}

LogAnalyzerResult(buildResult, options);
if (buildResultOverallSuccess)
{
_logger.LogDebug("Analysis of project {projectFilePath} succeeded.", projectLogName);
LogAnalyzerResult(buildResult, options);
return buildResult;
}
var failedFrameworks = project.ProjectFile.TargetFrameworks.Where(tf =>
Expand Down Expand Up @@ -316,7 +319,7 @@ private void LogAnalyzerResult(IAnalyzerResults analyzerResults, IStrykerOptions
foreach (var analyzerResult in analyzerResults)
{
log.AppendLine($"TargetFramework: {analyzerResult.TargetFramework}");
log.AppendLine("Succeeded: {analyzerResult.Succeeded}");
log.AppendLine($"Succeeded: {analyzerResult.Succeeded}");

var properties = analyzerResult.Properties ?? new Dictionary<string, string>();
foreach (var property in importantProperties)
Expand Down
61 changes: 36 additions & 25 deletions src/Stryker.Core/Stryker.Core/Initialisation/NugetRestoreProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,7 @@ public void RestorePackages(string solutionPath, string msbuildPath = null)
var solutionDir = Path.GetDirectoryName(solutionPath);

var helper = new MsBuildHelper(null, ProcessExecutor, msbuildPath, _logger);
// Locate MSBuild.exe
var msBuildVersionOutput = helper.GetVersion();
string msBuildVersion;
if (string.IsNullOrWhiteSpace(msBuildVersionOutput))
{
msBuildVersion = string.Empty;
_logger.LogDebug("Auto detected msbuild at: {MsBuildPath}, but failed to get version.", msbuildPath);
}
else
{
msBuildVersion = msBuildVersionOutput.Trim();
_logger.LogDebug("Auto detected msbuild version {MsBuildVersion} at: {MsBuildPath}", msBuildVersion,
msbuildPath);
}
var msBuildVersion = FindMsBuildShortVersion(helper);

// Validate nuget.exe is installed and included in path
var nugetWhereExeResult = ProcessExecutor.Start(solutionDir, "where.exe", "nuget.exe");
Expand All @@ -74,7 +61,14 @@ public void RestorePackages(string solutionPath, string msbuildPath = null)
var nugetPath = nugetWhereExeResult.Output
.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).First().Trim();

if (!InternalRestore(solutionPath, msBuildVersion, nugetPath) && !string.IsNullOrEmpty(msBuildVersion))
{
InternalRestore(solutionPath, string.Empty, nugetPath);
}
}

private bool InternalRestore(string solutionPath, string msBuildVersion, string nugetPath)
{
// Restore packages using nuget.exe
var nugetRestoreCommand = $"restore \"{solutionPath}\"";
if (!string.IsNullOrEmpty(msBuildVersion))
Expand All @@ -85,25 +79,42 @@ public void RestorePackages(string solutionPath, string msbuildPath = null)
_logger.LogDebug("Restoring packages using command: {NugetPath} {NugetRestoreCommand}", nugetPath,
nugetRestoreCommand);

const int NugetRestoreTimeoutMs = 120000;
try
{
var nugetRestoreResult = ProcessExecutor.Start(Path.GetDirectoryName(nugetPath), nugetPath,
nugetRestoreCommand, timeoutMs: 120000);
if (nugetRestoreResult.ExitCode != ExitCodes.Success)
nugetRestoreCommand, timeoutMs: NugetRestoreTimeoutMs);
if (nugetRestoreResult.ExitCode == ExitCodes.Success)
{
_logger.LogCritical("Failed to restore nuget packages. Nuget error: {Error}",
nugetRestoreResult.Error);
throw new InputException(
"Nuget.exe failed to restore packages for your solution. Please review your nuget setup.",
nugetRestoreResult.Output);
_logger.LogDebug("Restored packages using nuget.exe, output: {Error}", nugetRestoreResult.Output);
return true;
}

_logger.LogDebug("Restored packages using nuget.exe, output: {Error}", nugetRestoreResult.Output);
_logger.LogError("Failed to restore nuget packages. Nuget error: {Error}", nugetRestoreResult.Error);
}
catch (OperationCanceledException)
catch (OperationCanceledException e)
{
throw new InputException(
"Nuget.exe failed to restore packages for your solution. Please review your nuget setup.");
_logger.LogError(e, "Failed to restore nuget packages in less than {time} seconds.", NugetRestoreTimeoutMs / 1000);
}
return false;
}

private string FindMsBuildShortVersion(MsBuildHelper helper)
{
var msBuildVersionOutput = helper.GetVersion();
string msBuildVersion;
if (string.IsNullOrWhiteSpace(msBuildVersionOutput))
{
msBuildVersion = string.Empty;
_logger.LogInformation("Auto detected msbuild at: {MsBuildPath}, but failed to get version.", helper.GetMsBuildPath());
}
else
{
msBuildVersion = msBuildVersionOutput.Trim();
_logger.LogDebug("Auto detected msbuild version {MsBuildVersion} at: {MsBuildPath}", msBuildVersion,
helper.GetMsBuildPath());
}

return msBuildVersion;
}
}

0 comments on commit b7f9cca

Please sign in to comment.