Skip to content

Commit

Permalink
rearrange error handling to one location in full runner (#10692)
Browse files Browse the repository at this point in the history
  • Loading branch information
brettfo authored Oct 1, 2024
1 parent 073100c commit ab0c204
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 202 deletions.
1 change: 1 addition & 0 deletions nuget/helpers/lib/NuGetUpdater/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tab_width = 4

# New line preferences
insert_final_newline = true
end_of_line = lf

#### .NET Coding Conventions ####
[*.{cs,vb}]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,98 @@ await RunAsync(
);
}

[Fact]
public async Task PrivateSourceAuthenticationFailureIsForwaredToApiHandler()
{
static (int, string) TestHttpHandler(string uriString)
{
var uri = new Uri(uriString, UriKind.Absolute);
var baseUrl = $"{uri.Scheme}://{uri.Host}:{uri.Port}";
return uri.PathAndQuery switch
{
// initial request is good
"/index.json" => (200, $$"""
{
"version": "3.0.0",
"resources": [
{
"@id": "{{baseUrl}}/download",
"@type": "PackageBaseAddress/3.0.0"
},
{
"@id": "{{baseUrl}}/query",
"@type": "SearchQueryService"
},
{
"@id": "{{baseUrl}}/registrations",
"@type": "RegistrationsBaseUrl"
}
]
}
"""),
// all other requests are unauthorized
_ => (401, "{}"),
};
}
using var http = TestHttpServer.CreateTestStringServer(TestHttpHandler);
await RunAsync(
packages:
[
],
job: new Job()
{
PackageManager = "nuget",
Source = new()
{
Provider = "github",
Repo = "test/repo",
Directory = "/",
},
AllowedUpdates =
[
new() { UpdateType = "all" }
]
},
files:
[
("NuGet.Config", $"""
<configuration>
<packageSources>
<clear />
<add key="private_feed" value="{http.BaseUrl.TrimEnd('/')}/index.json" allowInsecureConnections="true" />
</packageSources>
</configuration>
"""),
("project.csproj", """
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Some.Package" Version="1.0.0" />
</ItemGroup>
</Project>
""")
],
expectedResult: new RunResult()
{
Base64DependencyFiles = [],
BaseCommitSha = "TEST-COMMIT-SHA",
},
expectedApiMessages:
[
new PrivateSourceAuthenticationFailure()
{
Details = $"({http.BaseUrl.TrimEnd('/')}/index.json)"
},
new MarkAsProcessed()
{
BaseCommitSha = "TEST-COMMIT-SHA",
}
]
);
}

private static async Task RunAsync(Job job, TestFile[] files, RunResult expectedResult, object[] expectedApiMessages, MockNuGetPackage[]? packages = null)
{
// arrange
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,27 +9,33 @@ internal class TestApiHandler : IApiHandler

public IEnumerable<(Type Type, object Object)> ReceivedMessages => _receivedMessages;

public Task RecordUpdateJobError(JobErrorBase error)
{
_receivedMessages.Add((error.GetType(), error));
return Task.CompletedTask;
}

public Task UpdateDependencyList(UpdatedDependencyList updatedDependencyList)
{
_receivedMessages.Add((typeof(UpdatedDependencyList), updatedDependencyList));
_receivedMessages.Add((updatedDependencyList.GetType(), updatedDependencyList));
return Task.CompletedTask;
}

public Task IncrementMetric(IncrementMetric incrementMetric)
{
_receivedMessages.Add((typeof(IncrementMetric), incrementMetric));
_receivedMessages.Add((incrementMetric.GetType(), incrementMetric));
return Task.CompletedTask;
}

public Task CreatePullRequest(CreatePullRequest createPullRequest)
{
_receivedMessages.Add((typeof(CreatePullRequest), createPullRequest));
_receivedMessages.Add((createPullRequest.GetType(), createPullRequest));
return Task.CompletedTask;
}

public Task MarkAsProcessed(MarkAsProcessed markAsProcessed)
{
_receivedMessages.Add((typeof(MarkAsProcessed), markAsProcessed));
_receivedMessages.Add((markAsProcessed.GetType(), markAsProcessed));
return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -257,14 +257,6 @@ public static async Task MockNuGetPackagesInDirectory(MockNuGetPackage[]? packag
package.WriteToDirectory(localFeedPath);
}

// override various nuget locations
foreach (var envName in new[] { "NUGET_PACKAGES", "NUGET_HTTP_CACHE_PATH", "NUGET_SCRATCH", "NUGET_PLUGINS_CACHE_PATH" })
{
string dir = Path.Join(temporaryDirectory, envName);
Directory.CreateDirectory(dir);
Environment.SetEnvironmentVariable(envName, dir);
}

// ensure only the test feed is used
string relativeLocalFeedPath = Path.GetRelativePath(temporaryDirectory, localFeedPath);
await File.WriteAllTextAsync(Path.Join(temporaryDirectory, "NuGet.Config"), $"""
Expand All @@ -278,6 +270,14 @@ await File.WriteAllTextAsync(Path.Join(temporaryDirectory, "NuGet.Config"), $"""
"""
);
}

// override various nuget locations
foreach (var envName in new[] { "NUGET_PACKAGES", "NUGET_HTTP_CACHE_PATH", "NUGET_SCRATCH", "NUGET_PLUGINS_CACHE_PATH" })
{
string dir = Path.Join(temporaryDirectory, envName);
Directory.CreateDirectory(dir);
Environment.SetEnvironmentVariable(envName, dir);
}
}

protected static async Task<TestFile[]> RunUpdate(TestFile[] files, Func<string, Task> action)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,29 @@ public AnalyzeWorker(Logger logger)

public async Task RunAsync(string repoRoot, string discoveryPath, string dependencyPath, string analysisDirectory)
{
AnalysisResult analysisResult;
var discovery = await DeserializeJsonFileAsync<WorkspaceDiscoveryResult>(discoveryPath, nameof(WorkspaceDiscoveryResult));
var dependencyInfo = await DeserializeJsonFileAsync<DependencyInfo>(dependencyPath, nameof(DependencyInfo));
var analysisResult = await RunAsync(repoRoot, discovery, dependencyInfo);

try
{
analysisResult = await RunAsync(repoRoot, discovery, dependencyInfo);
}
catch (HttpRequestException ex)
when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
{
var localPath = PathHelper.JoinPath(repoRoot, discovery.Path);
var nugetContext = new NuGetContext(localPath);
analysisResult = new AnalysisResult
{
ErrorType = ErrorType.AuthenticationFailure,
ErrorDetails = "(" + string.Join("|", nugetContext.PackageSources.Select(s => s.Source)) + ")",
UpdatedVersion = string.Empty,
CanUpdate = false,
UpdatedDependencies = [],
};
}

await WriteResultsAsync(analysisDirectory, dependencyInfo.Name, analysisResult, _logger);
}

Expand Down Expand Up @@ -68,100 +88,84 @@ public async Task<AnalysisResult> RunAsync(string repoRoot, WorkspaceDiscoveryRe
var isUpdateNecessary = isProjectUpdateNecessary || dotnetToolsHasDependency || globalJsonHasDependency;
using var nugetContext = new NuGetContext(startingDirectory);
AnalysisResult analysisResult;
try
if (isUpdateNecessary)
{
if (isUpdateNecessary)
_logger.Log($" Determining multi-dependency property.");
var multiDependencies = DetermineMultiDependencyDetails(
discovery,
dependencyInfo.Name,
propertyBasedDependencies);

usesMultiDependencyProperty = multiDependencies.Any(md => md.DependencyNames.Count > 1);
var dependenciesToUpdate = usesMultiDependencyProperty
? multiDependencies
.SelectMany(md => md.DependencyNames)
.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase)
: [dependencyInfo.Name];
var applicableTargetFrameworks = usesMultiDependencyProperty
? multiDependencies
.SelectMany(md => md.TargetFrameworks)
.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase)
.Select(NuGetFramework.Parse)
.ToImmutableArray()
: projectFrameworks;

_logger.Log($" Finding updated version.");
updatedVersion = await FindUpdatedVersionAsync(
startingDirectory,
dependencyInfo,
dependenciesToUpdate,
applicableTargetFrameworks,
nugetContext,
_logger,
CancellationToken.None);

_logger.Log($" Finding updated peer dependencies.");
if (updatedVersion is null)
{
updatedDependencies = [];
}
else if (isProjectUpdateNecessary)
{
_logger.Log($" Determining multi-dependency property.");
var multiDependencies = DetermineMultiDependencyDetails(
updatedDependencies = await FindUpdatedDependenciesAsync(
repoRoot,
discovery,
dependencyInfo.Name,
propertyBasedDependencies);

usesMultiDependencyProperty = multiDependencies.Any(md => md.DependencyNames.Count > 1);
var dependenciesToUpdate = usesMultiDependencyProperty
? multiDependencies
.SelectMany(md => md.DependencyNames)
.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase)
: [dependencyInfo.Name];
var applicableTargetFrameworks = usesMultiDependencyProperty
? multiDependencies
.SelectMany(md => md.TargetFrameworks)
.ToImmutableHashSet(StringComparer.OrdinalIgnoreCase)
.Select(NuGetFramework.Parse)
.ToImmutableArray()
: projectFrameworks;

_logger.Log($" Finding updated version.");
updatedVersion = await FindUpdatedVersionAsync(
startingDirectory,
dependencyInfo,
dependenciesToUpdate,
applicableTargetFrameworks,
updatedVersion,
nugetContext,
_logger,
CancellationToken.None);

_logger.Log($" Finding updated peer dependencies.");
if (updatedVersion is null)
{
updatedDependencies = [];
}
else if (isProjectUpdateNecessary)
{
updatedDependencies = await FindUpdatedDependenciesAsync(
repoRoot,
discovery,
dependenciesToUpdate,
updatedVersion,
nugetContext,
_logger,
CancellationToken.None);
}
else if (dotnetToolsHasDependency)
{
var infoUrl = await nugetContext.GetPackageInfoUrlAsync(dependencyInfo.Name, updatedVersion.ToNormalizedString(), CancellationToken.None);
updatedDependencies = [new Dependency(dependencyInfo.Name, updatedVersion.ToNormalizedString(), DependencyType.DotNetTool, IsDirect: true, InfoUrl: infoUrl)];
}
else if (globalJsonHasDependency)
{
var infoUrl = await nugetContext.GetPackageInfoUrlAsync(dependencyInfo.Name, updatedVersion.ToNormalizedString(), CancellationToken.None);
updatedDependencies = [new Dependency(dependencyInfo.Name, updatedVersion.ToNormalizedString(), DependencyType.MSBuildSdk, IsDirect: true, InfoUrl: infoUrl)];
}
else
{
throw new InvalidOperationException("Unreachable.");
}

//TODO: At this point we should add the peer dependencies to a queue where
// we will analyze them one by one to see if they themselves are part of a
// multi-dependency property. Basically looping this if-body until we have
// emptied the queue and have a complete list of updated dependencies. We
// should track the dependenciesToUpdate as they have already been analyzed.
}

analysisResult = new AnalysisResult
else if (dotnetToolsHasDependency)
{
UpdatedVersion = updatedVersion?.ToNormalizedString() ?? dependencyInfo.Version,
CanUpdate = updatedVersion is not null,
VersionComesFromMultiDependencyProperty = usesMultiDependencyProperty,
UpdatedDependencies = updatedDependencies,
};
}
catch (HttpRequestException ex)
when (ex.StatusCode == HttpStatusCode.Unauthorized || ex.StatusCode == HttpStatusCode.Forbidden)
{
// TODO: consolidate this error handling between AnalyzeWorker, DiscoveryWorker, and UpdateWorker
analysisResult = new AnalysisResult
var infoUrl = await nugetContext.GetPackageInfoUrlAsync(dependencyInfo.Name, updatedVersion.ToNormalizedString(), CancellationToken.None);
updatedDependencies = [new Dependency(dependencyInfo.Name, updatedVersion.ToNormalizedString(), DependencyType.DotNetTool, IsDirect: true, InfoUrl: infoUrl)];
}
else if (globalJsonHasDependency)
{
ErrorType = ErrorType.AuthenticationFailure,
ErrorDetails = "(" + string.Join("|", nugetContext.PackageSources.Select(s => s.Source)) + ")",
UpdatedVersion = string.Empty,
CanUpdate = false,
UpdatedDependencies = [],
};
var infoUrl = await nugetContext.GetPackageInfoUrlAsync(dependencyInfo.Name, updatedVersion.ToNormalizedString(), CancellationToken.None);
updatedDependencies = [new Dependency(dependencyInfo.Name, updatedVersion.ToNormalizedString(), DependencyType.MSBuildSdk, IsDirect: true, InfoUrl: infoUrl)];
}
else
{
throw new InvalidOperationException("Unreachable.");
}

//TODO: At this point we should add the peer dependencies to a queue where
// we will analyze them one by one to see if they themselves are part of a
// multi-dependency property. Basically looping this if-body until we have
// emptied the queue and have a complete list of updated dependencies. We
// should track the dependenciesToUpdate as they have already been analyzed.
}

analysisResult = new AnalysisResult
{
UpdatedVersion = updatedVersion?.ToNormalizedString() ?? dependencyInfo.Version,
CanUpdate = updatedVersion is not null,
VersionComesFromMultiDependencyProperty = usesMultiDependencyProperty,
UpdatedDependencies = updatedDependencies,
};

_logger.Log($"Analysis complete.");
return analysisResult;
}
Expand Down
Loading

0 comments on commit ab0c204

Please sign in to comment.