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 @@ -339,9 +339,68 @@ await _jsonFileMerger.MergeJsonsAsync(
await vmr.StageAsync([relativeSourceMappingPath / VersionFiles.VersionDetailsProps], cancellationToken);
}

// Recompute NuGet.config feeds based on the current dependencies in Version.Details.xml
// This ensures that source-flow PRs don't remove feeds that are still required by dependencies
await UpdateNugetConfigFeedsAsync(vmr, relativeSourceMappingPath, cancellationToken);

if (!await vmr.HasWorkingTreeChangesAsync() && !await vmr.HasStagedChangesAsync())
{
_logger.LogInformation("No changes to dependencies in this forward flow update");
}
}

/// <summary>
/// Updates NuGet.config with feeds required by the current dependencies in Version.Details.xml.
/// This ensures that source-flow PRs don't remove feeds that are still needed.
/// </summary>
private async Task UpdateNugetConfigFeedsAsync(
ILocalGitRepo vmr,
UnixPath relativeSourceMappingPath,
CancellationToken cancellationToken)
{
// Read the current Version.Details.xml to get all dependencies
var versionDetailsXml = await _dependencyFileManager.ReadVersionDetailsXmlAsync(
vmr.Path,
branch: null!, // reads the working tree
relativeSourceMappingPath);
var versionDetails = _versionDetailsParser.ParseVersionDetailsXml(versionDetailsXml);

// Build the asset location mapping from all dependencies
var assetLocationMap = new Dictionary<string, HashSet<string>>();
foreach (var dependency in versionDetails.Dependencies)
{
if (!assetLocationMap.TryGetValue(dependency.Name, out HashSet<string>? value))
{
value = [];
assetLocationMap[dependency.Name] = value;
}

if (dependency.Locations != null)
{
value.UnionWith(dependency.Locations);
}
}

// Flatten the locations and split into groups
var managedFeeds = _dependencyFileManager.FlattenLocationsAndSplitIntoGroups(assetLocationMap);

// Read the current NuGet.config
(string nugetConfigName, XmlDocument nugetConfig) = await _dependencyFileManager.ReadNugetConfigAsync(
vmr.Path,
branch: null!, // reads the working tree
relativeSourceMappingPath);

// Update package sources based on the managed feeds
var updatedNugetConfig = _dependencyFileManager.UpdatePackageSources(nugetConfig, managedFeeds);

// Write the updated NuGet.config back
_fileSystem.WriteToFile(
vmr.Path / relativeSourceMappingPath / nugetConfigName,
GitFile.GetIndentedXmlBody(updatedNugetConfig));

// Stage the NuGet.config file
await vmr.StageAsync([relativeSourceMappingPath / nugetConfigName], cancellationToken);

_logger.LogInformation("Updated NuGet.config feeds based on Version.Details.xml dependencies");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
Expand Down Expand Up @@ -122,6 +123,27 @@ public async Task ForwardFlowConflictResolverMergesDependenciesCorrectly()
dependencyFileManagerMock.Setup(m => m.ReadVersionDetailsXmlAsync(
vmrPath, It.IsAny<string>(), It.IsAny<UnixPath>()))
.ReturnsAsync(doc);

// Setup for NuGet.config feed update
var nugetConfigDoc = new XmlDocument();
nugetConfigDoc.LoadXml(@"<configuration><packageSources></packageSources></configuration>");
dependencyFileManagerMock.Setup(m => m.ReadNugetConfigAsync(
vmrPath, It.IsAny<string>(), It.IsAny<UnixPath>()))
.ReturnsAsync(("NuGet.config", nugetConfigDoc));
dependencyFileManagerMock.Setup(m => m.FlattenLocationsAndSplitIntoGroups(
It.IsAny<Dictionary<string, HashSet<string>>>()))
.Returns(new Dictionary<string, HashSet<string>>());
dependencyFileManagerMock.Setup(m => m.UpdatePackageSources(
It.IsAny<XmlDocument>(), It.IsAny<Dictionary<string, HashSet<string>>>()))
.Returns(nugetConfigDoc);

// Setup version details parser to return an empty version details
versionDetailsParserMock.Setup(m => m.ParseVersionDetailsXml(
It.IsAny<XmlDocument>(),
It.IsAny<bool>()))
.Returns(new VersionDetails([], null));

var fileSystemMock = new Mock<IFileSystem>();

ForwardFlowConflictResolver resolver = new(
new Mock<IVmrInfo>().Object,
Expand All @@ -132,7 +154,7 @@ public async Task ForwardFlowConflictResolverMergesDependenciesCorrectly()
jsonMergerMock.Object,
versionDetailsFileMergerMock.Object,
versionDetailsParserMock.Object,
new Mock<IFileSystem>().Object,
fileSystemMock.Object,
NullLogger<ForwardFlowConflictResolver>.Instance);

await resolver.MergeDependenciesAsync(
Expand Down Expand Up @@ -185,5 +207,16 @@ await resolver.MergeDependenciesAsync(
It.IsAny<XmlDocument>(),
newSourceDependency),
Times.Once);

// Verify that NuGet.config feeds are recomputed based on Version.Details.xml
dependencyFileManagerMock.Verify(m => m.ReadNugetConfigAsync(
vmrPath, null!, It.IsAny<UnixPath>()),
Times.Once);
dependencyFileManagerMock.Verify(m => m.FlattenLocationsAndSplitIntoGroups(
It.IsAny<Dictionary<string, HashSet<string>>>()),
Times.Once);
dependencyFileManagerMock.Verify(m => m.UpdatePackageSources(
It.IsAny<XmlDocument>(), It.IsAny<Dictionary<string, HashSet<string>>>()),
Times.Once);
}
}
Loading