Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,12 @@ internal sealed partial class FeedManager : IDisposable
/// </summary>
public ImmutableHashSet<string> InheritedFeeds => AllFeeds.Except(ExplicitFeeds).ToImmutableHashSet();

private readonly Lazy<(bool, ImmutableHashSet<string>)> lazyReachableExplicitFeeds;

/// <summary>
/// Gets whether there was a timeout when checking the reachability of the explicitly configured NuGet feeds.
/// </summary>
public bool ExplicitFeedTimeout => lazyReachableExplicitFeeds.Value.Item1;
private readonly Lazy<ImmutableHashSet<string>> lazyReachableExplicitFeeds;

/// <summary>
/// Gets the list of reachable NuGet feeds that are explicitly configured.
/// </summary>
public ImmutableHashSet<string> ReachableExplicitFeeds => lazyReachableExplicitFeeds.Value.Item2;
public ImmutableHashSet<string> ReachableExplicitFeeds => lazyReachableExplicitFeeds.Value;

private readonly Lazy<ImmutableHashSet<string>> lazyReachableFeeds;
/// <summary>
Expand All @@ -96,15 +91,11 @@ public FeedManager(ILogger logger, IDotNet dotnet, DependabotProxy? dependabotPr

lazyExplicitFeeds = new Lazy<ImmutableHashSet<string>>(GetExplicitFeeds);
lazyAllFeeds = new Lazy<ImmutableHashSet<string>>(GetAllFeeds);
lazyReachableExplicitFeeds = new Lazy<(bool, ImmutableHashSet<string>)>(() =>
{
var timeout = CheckSpecifiedFeeds(ExplicitFeeds, out var reachableFeeds);
return (timeout, reachableFeeds);
});
lazyReachableExplicitFeeds = new Lazy<ImmutableHashSet<string>>(() => CheckSpecifiedFeeds(ExplicitFeeds));
lazyReachableFeeds = new Lazy<ImmutableHashSet<string>>(() =>
{
// Inherited feeds should only be used, if they are indeed reachable (as they may be environment specific).
CheckSpecifiedFeeds(InheritedFeeds, out var reachableInheritedFeeds);
var reachableInheritedFeeds = CheckSpecifiedFeeds(InheritedFeeds);
return ReachableExplicitFeeds.Union(reachableInheritedFeeds).ToImmutableHashSet();
});
lazyReachableFallbackFeeds = new Lazy<ImmutableHashSet<string>>(() =>
Expand Down Expand Up @@ -271,7 +262,7 @@ private static async Task<HttpResponseMessage> ExecuteGetRequest(string address,
return await httpClient.GetAsync(address, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
}

private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount, out bool isTimeout)
private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount)
{
logger.LogInfo($"Checking if NuGet feed '{feed}' is reachable...");

Expand Down Expand Up @@ -304,8 +295,6 @@ private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount,

using HttpClient client = new(httpClientHandler);

isTimeout = false;

for (var i = 0; i < tryCount; i++)
{
using var cts = new CancellationTokenSource();
Expand Down Expand Up @@ -335,7 +324,6 @@ private bool IsFeedReachable(string feed, int timeoutMilliSeconds, int tryCount,
}

logger.LogWarning($"Didn't receive answer from NuGet feed '{feed}'. Tried it {tryCount} times.");
isTimeout = true;
return false;
}

Expand All @@ -359,12 +347,8 @@ private HashSet<string> GetExcludedFeeds()
/// Checks that we can connect to the specified NuGet feeds.
/// </summary>
/// <param name="feeds">The set of package feeds to check.</param>
/// <param name="reachableFeeds">The list of feeds that were reachable.</param>
/// <returns>
/// True if there is a timeout when trying to reach the feeds (excluding any feeds that are configured
/// to be excluded from the check) or false otherwise.
/// </returns>
private bool CheckSpecifiedFeeds(ImmutableHashSet<string> feeds, out ImmutableHashSet<string> reachableFeeds)
/// <returns>The list of feeds that were reachable.</returns>
private ImmutableHashSet<string> CheckSpecifiedFeeds(ImmutableHashSet<string> feeds)
{
// Exclude any feeds from the feed check that are configured by the corresponding environment variable.
// These feeds are always assumed to be reachable.
Expand All @@ -380,12 +364,10 @@ private bool CheckSpecifiedFeeds(ImmutableHashSet<string> feeds, out ImmutableHa
return true;
}).ToHashSet();

var reachable = GetReachableNuGetFeeds(feedsToCheck, isFallback: false, out var isTimeout);
var reachable = GetReachableNuGetFeeds(feedsToCheck, isFallback: false);

// Always consider feeds excluded for the reachability check as reachable.
reachableFeeds = reachable.Union(feeds.Where(feed => excludedFeeds.Contains(feed))).ToImmutableHashSet();

return isTimeout;
return reachable.Union(feeds.Where(feed => excludedFeeds.Contains(feed))).ToImmutableHashSet();
}

/// <summary>
Expand All @@ -398,7 +380,7 @@ public bool IsDefaultFeedReachable()
if (CheckNugetFeedResponsiveness)
{
var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback: false);
return IsFeedReachable(PublicNugetOrgFeed, initialTimeout, tryCount, out var _);
return IsFeedReachable(PublicNugetOrgFeed, initialTimeout, tryCount);
}

return true;
Expand All @@ -409,22 +391,15 @@ public bool IsDefaultFeedReachable()
/// </summary>
/// <param name="feedsToCheck">The feeds to check.</param>
/// <param name="isFallback">Whether the feeds are fallback feeds or not.</param>
/// <param name="isTimeout">Whether a timeout occurred while checking the feeds.</param>
/// <returns>The list of feeds that could be reached.</returns>
private List<string> GetReachableNuGetFeeds(HashSet<string> feedsToCheck, bool isFallback, out bool isTimeout)
private List<string> GetReachableNuGetFeeds(HashSet<string> feedsToCheck, bool isFallback)
{
var fallbackStr = isFallback ? "fallback " : "";
logger.LogInfo($"Checking {fallbackStr}NuGet feed reachability on feeds: {string.Join(", ", feedsToCheck.OrderBy(f => f))}");

var (initialTimeout, tryCount) = GetFeedRequestSettings(isFallback);
var timeout = false;
var reachableFeeds = feedsToCheck
.Where(feed =>
{
var reachable = IsFeedReachable(feed, initialTimeout, tryCount, out var feedTimeout);
timeout |= feedTimeout;
return reachable;
})
.Where(feed => IsFeedReachable(feed, initialTimeout, tryCount))
.ToList();

if (reachableFeeds.Count == 0)
Expand All @@ -436,7 +411,6 @@ private List<string> GetReachableNuGetFeeds(HashSet<string> feedsToCheck, bool i
logger.LogInfo($"Reachable {fallbackStr}NuGet feeds: {string.Join(", ", reachableFeeds.OrderBy(f => f))}");
}

isTimeout = timeout;
return reachableFeeds;
}

Expand All @@ -460,7 +434,7 @@ private List<string> GetReachableFallbackNugetFeeds()
}
}

return GetReachableNuGetFeeds(fallbackFeeds, isFallback: true, out var _);
return GetReachableNuGetFeeds(fallbackFeeds, isFallback: true);
}

private ImmutableHashSet<string> GetExplicitFeeds()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,19 +127,8 @@ public HashSet<AssemblyLookupLocation> Restore()
compilationInfoContainer.CompilationInfos.Add(("Inherited NuGet feed count", inheritedFeeds.Count.ToString()));
}

var allExplicitReachable = explicitFeeds.Count == feedManager.ReachableExplicitFeeds.Count;
EmitUnreachableFeedsDiagnostics(allExplicitReachable);

if (feedManager.ExplicitFeedTimeout)
{
// If we experience a timeout, we use this fallback.
// todo: we could also check the reachability of the inherited nuget feeds, but to use those in the fallback we would need to handle authentication too.
var unresponsiveMissingPackageLocation = DownloadMissingPackages([]);
return unresponsiveMissingPackageLocation is null
? []
: [unresponsiveMissingPackageLocation];
}

var unreachableExplicitFeeds = explicitFeeds.Except(feedManager.ReachableExplicitFeeds).ToImmutableHashSet();
EmitFeedReachabilityDiagnostics(unreachableExplicitFeeds);
}

try
Expand Down Expand Up @@ -547,25 +536,31 @@ private void TryChangeProjectFile(DirectoryInfo projectDir, Regex pattern, strin
}

/// <summary>
/// If <paramref name="allFeedsReachable"/> is `false`, logs this and emits a diagnostic.
/// If <paramref name="unreachableFeeds"/> is not empty, logs this and emits a diagnostic.
/// Adds a `CompilationInfos` entry either way.
/// </summary>
/// <param name="allFeedsReachable">Whether all feeds were reachable or not.</param>
private void EmitUnreachableFeedsDiagnostics(bool allFeedsReachable)
/// <param name="unreachableFeeds">The feeds that were not reachable.</param>
private void EmitFeedReachabilityDiagnostics(ImmutableHashSet<string> unreachableFeeds)
{
if (!allFeedsReachable)
if (unreachableFeeds.Count > 0)
{
logger.LogWarning("Found unreachable NuGet feed in C# analysis with build-mode 'none'. This may cause missing dependencies in the analysis.");
var orderedUnreachableFeeds = unreachableFeeds.OrderBy(feed => feed).ToList();
var unreachableFeedList = string.Join(", ", orderedUnreachableFeeds);
logger.LogWarning($"Found unreachable NuGet feeds in C# analysis with build-mode 'none': {unreachableFeedList}. This may cause missing dependencies in the analysis.");
compilationInfoContainer.CompilationInfos.Add(("Unreachable NuGet feeds", unreachableFeedList));
diagnosticsWriter.AddEntry(new DiagnosticMessage(
Language.CSharp,
"buildless/unreachable-feed",
"Found unreachable NuGet feed in C# analysis with build-mode 'none'",
"Found unreachable NuGet feeds in C# analysis with build-mode 'none'",
visibility: new DiagnosticMessage.TspVisibility(statusPage: true, cliSummaryTable: true, telemetry: true),
markdownMessage: "Found unreachable NuGet feed in C# analysis with build-mode 'none'. This may cause missing dependencies in the analysis.",
markdownMessage: string.Format(
"Found unreachable NuGet feeds in C# analysis with build-mode 'none':\n\n{0}\n\nThis may cause missing dependencies in the analysis.",
string.Join("\n", orderedUnreachableFeeds.Select(feed => $"- `{feed}`"))
),
severity: DiagnosticMessage.TspSeverity.Note
));
}
compilationInfoContainer.CompilationInfos.Add(("All NuGet feeds reachable", allFeedsReachable ? "1" : "0"));
compilationInfoContainer.CompilationInfos.Add(("All NuGet feeds reachable", unreachableFeeds.Count == 0 ? "1" : "0"));
}

private void EmitNugetConfigDiagnostics()
Expand Down Expand Up @@ -625,14 +620,6 @@ public void Dispose()
feedManager.Dispose();
}

/// <summary>
/// Returns the full path to a temporary directory with the given subfolder name.
/// </summary>
private static string ComputeTempDirectoryPath(string subfolderName)
{
return Path.Join(FileUtils.GetTemporaryWorkingDirectory(out _), subfolderName);
}

/// <summary>
/// Computes a unique temporary directory path based on the source directory and the subfolder name.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
| All NuGet feeds reachable | 1.0 |
| Failed project restore with missing package error | 0.0 |
| Failed project restore with package source error | 0.0 |
| Failed solution restore with missing package error | 0.0 |
| Failed solution restore with package source error | 0.0 |
| Inherited NuGet feed count | 1.0 |
| NuGet feed responsiveness checked | 1.0 |
| Project files on filesystem | 1.0 |
| Reachable fallback NuGet feed count | 1.0 |
| Resource extraction enabled | 1.0 |
| Restored .NET framework variants | 1.0 |
| Restored projects through solution files | 0.0 |
| Solution files on filesystem | 0.0 |
| Source files generated | 2.0 |
| Source files on filesystem | 1.0 |
| Successfully restored project files | 1.0 |
| Successfully restored solution files | 0.0 |
| Unresolved references | 0.0 |
| UseWPF set | 0.0 |
| UseWindowsForms set | 0.0 |
| WebView extraction enabled | 1.0 |
| All NuGet feeds reachable | 1 |
| Failed project restore with missing package error | 0 |
| Failed project restore with package source error | 0 |
| Failed solution restore with missing package error | 0 |
| Failed solution restore with package source error | 0 |
| Inherited NuGet feed count | 1 |
| NuGet feed responsiveness checked | 1 |
| Project files on filesystem | 1 |
| Reachable fallback NuGet feed count | 1 |
| Resource extraction enabled | 1 |
| Restored .NET framework variants | 1 |
| Restored projects through solution files | 0 |
| Solution files on filesystem | 0 |
| Source files generated | 2 |
| Source files on filesystem | 1 |
| Successfully restored project files | 1 |
| Successfully restored solution files | 0 |
| Unresolved references | 0 |
| UseWPF set | 0 |
| UseWindowsForms set | 0 |
| WebView extraction enabled | 1 |
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import csharp
import semmle.code.csharp.commons.Diagnostics

query predicate compilationInfo(string key, float value) {
query predicate compilationInfo(string key, string value) {
key != "Resolved references" and
key != "Resolved assembly conflicts" and
not key.matches("Compiler diagnostic count for%") and
exists(Compilation c, string infoKey, string infoValue | infoValue = c.getInfo(infoKey) |
key = infoKey and
value = infoValue.toFloat()
or
not exists(infoValue.toFloat()) and
key = infoKey + ": " + infoValue and
value = 1
)
value = any(Compilation c).getInfo(key)
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
| All NuGet feeds reachable | 1.0 |
| Failed project restore with missing package error | 0.0 |
| Failed project restore with package source error | 0.0 |
| Failed solution restore with missing package error | 0.0 |
| Failed solution restore with package source error | 0.0 |
| Inherited NuGet feed count | 1.0 |
| NuGet feed responsiveness checked | 1.0 |
| Project files on filesystem | 2.0 |
| Reachable fallback NuGet feed count | 1.0 |
| Resource extraction enabled | 0.0 |
| Restored .NET framework variants | 1.0 |
| Restored projects through solution files | 2.0 |
| Solution files on filesystem | 1.0 |
| Source files generated | 1.0 |
| Source files on filesystem | 2.0 |
| Successfully restored project files | 0.0 |
| Successfully restored solution files | 1.0 |
| Unresolved references | 0.0 |
| UseWPF set | 0.0 |
| UseWindowsForms set | 0.0 |
| WebView extraction enabled | 1.0 |
| All NuGet feeds reachable | 1 |
| Failed project restore with missing package error | 0 |
| Failed project restore with package source error | 0 |
| Failed solution restore with missing package error | 0 |
| Failed solution restore with package source error | 0 |
| Inherited NuGet feed count | 1 |
| NuGet feed responsiveness checked | 1 |
| Project files on filesystem | 2 |
| Reachable fallback NuGet feed count | 1 |
| Resource extraction enabled | 0 |
| Restored .NET framework variants | 1 |
| Restored projects through solution files | 2 |
| Solution files on filesystem | 1 |
| Source files generated | 1 |
| Source files on filesystem | 2 |
| Successfully restored project files | 0 |
| Successfully restored solution files | 1 |
| Unresolved references | 0 |
| UseWPF set | 0 |
| UseWindowsForms set | 0 |
| WebView extraction enabled | 1 |
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import csharp
import semmle.code.csharp.commons.Diagnostics

query predicate compilationInfo(string key, float value) {
query predicate compilationInfo(string key, string value) {
key != "Resolved references" and
key != "Resolved assembly conflicts" and
not key.matches(["Compiler diagnostic count for%", "Extractor message count for group%"]) and
exists(Compilation c, string infoKey, string infoValue | infoValue = c.getInfo(infoKey) |
key = infoKey and
value = infoValue.toFloat()
or
not exists(infoValue.toFloat()) and
key = infoKey + ": " + infoValue and
value = 1
)
value = any(Compilation c).getInfo(key)
}
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
| All NuGet feeds reachable | 1.0 |
| Failed project restore with missing package error | 0.0 |
| Failed project restore with package source error | 0.0 |
| Failed solution restore with missing package error | 0.0 |
| Failed solution restore with package source error | 0.0 |
| Inherited NuGet feed count | 1.0 |
| NuGet feed responsiveness checked | 1.0 |
| Project files on filesystem | 1.0 |
| Reachable fallback NuGet feed count | 1.0 |
| Resource extraction enabled | 0.0 |
| Restored .NET framework variants | 1.0 |
| Restored projects through solution files | 0.0 |
| Solution files on filesystem | 0.0 |
| Source files generated | 1.0 |
| Source files on filesystem | 3.0 |
| Successfully restored project files | 1.0 |
| Successfully restored solution files | 0.0 |
| Unresolved references | 0.0 |
| UseWPF set | 0.0 |
| UseWindowsForms set | 1.0 |
| WebView extraction enabled | 1.0 |
| All NuGet feeds reachable | 1 |
| Failed project restore with missing package error | 0 |
| Failed project restore with package source error | 0 |
| Failed solution restore with missing package error | 0 |
| Failed solution restore with package source error | 0 |
| Inherited NuGet feed count | 1 |
| NuGet feed responsiveness checked | 1 |
| Project files on filesystem | 1 |
| Reachable fallback NuGet feed count | 1 |
| Resource extraction enabled | 0 |
| Restored .NET framework variants | 1 |
| Restored projects through solution files | 0 |
| Solution files on filesystem | 0 |
| Source files generated | 1 |
| Source files on filesystem | 3 |
| Successfully restored project files | 1 |
| Successfully restored solution files | 0 |
| Unresolved references | 0 |
| UseWPF set | 0 |
| UseWindowsForms set | 1 |
| WebView extraction enabled | 1 |
Loading
Loading