Skip to content
Merged
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 @@ -25,5 +25,5 @@ protected override async Task ExecuteInternalAsync(
bool recursive,
CancellationToken cancellationToken)
=>
await vmrManager.InitializeRepository(mapping, targetRevision, recursive, cancellationToken);
await vmrManager.InitializeRepository(mapping, targetRevision, null, recursive, cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ protected override async Task ExecuteInternalAsync(
bool recursive,
CancellationToken cancellationToken)
=>
await vmrManager.UpdateRepository(mapping, targetRevision, _options.NoSquash, recursive, cancellationToken);
await vmrManager.UpdateRepository(mapping, targetRevision, null, _options.NoSquash, recursive, cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public override async Task<int> ExecuteAsync()
return Constants.ErrorCode;
}

var vmrManager = Provider.GetRequiredService<TVmrManager>();
TVmrManager vmrManager = Provider.GetRequiredService<TVmrManager>();

IEnumerable<(SourceMapping Mapping, string? Revision)> reposToSync;

Expand All @@ -61,7 +61,7 @@ public override async Task<int> ExecuteAsync()

SourceMapping ResolveMapping(string repo)
{
return vmrManager!.Mappings.FirstOrDefault(m => m.Name.Equals(repo, StringComparison.InvariantCultureIgnoreCase))
return vmrManager.Mappings.FirstOrDefault(m => m.Name.Equals(repo, StringComparison.InvariantCultureIgnoreCase))
?? throw new Exception($"No mapping named '{repo}' found");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
Expand Down Expand Up @@ -48,6 +49,12 @@ protected MsBuildPropsFile(bool? orderPropertiesAscending)

public void SerializeToXml(string path)
{
var directory = Path.GetDirectoryName(path) ?? throw new ArgumentException($"'{path}' is not a valid path.");
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}

XmlSerializer serializer = new XmlSerializer(typeof(XmlElement));
var xmlDocument = new XmlDocument();
XmlElement root = xmlDocument.CreateElement(ProjectPropertyName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Xml;
using Microsoft.DotNet.DarcLib.Models;
using Microsoft.DotNet.DarcLib.VirtualMonoRepo;

#nullable enable
namespace Microsoft.DotNet.Darc.Models.VirtualMonoRepo;
Expand All @@ -14,8 +15,8 @@ public interface IAllVersionsPropsFile : IMsBuildPropsFile
{
Dictionary<string, string> Versions { get; }

(string? Sha, string? Version) GetVersion(string repository);
void UpdateVersion(string repository, string sha, string version);
VmrDependencyVersion? GetVersion(string repository);
void UpdateVersion(string repository, string sha, string packageVersion);
}

/// <summary>
Expand All @@ -36,21 +37,27 @@ public AllVersionsPropsFile(Dictionary<string, string> versions)
Versions = versions;
}

public (string? Sha, string? Version) GetVersion(string repository)
public VmrDependencyVersion? GetVersion(string repository)
{
var key = SanitizePropertyName(repository) + ShaPropertyName;
Versions.TryGetValue(key, out var sha);

if (sha is null)
{
return null;
Comment thread
premun marked this conversation as resolved.
}

key = SanitizePropertyName(repository) + PackageVersionPropertyName;
Versions.TryGetValue(key, out var version);
return (sha, version);

return new(sha, version);
}

public void UpdateVersion(string repository, string sha, string version)
public void UpdateVersion(string repository, string sha, string packageVersion)
{
var key = SanitizePropertyName(repository);
Versions[key + ShaPropertyName] = sha;
Versions[key + PackageVersionPropertyName] = version;
Versions[key + PackageVersionPropertyName] = packageVersion;
}

public static AllVersionsPropsFile DeserializeFromXml(string path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,34 @@ public interface IVmrInitializer : IVmrManager
/// </summary>
/// <param name="mappingName">Name of a repository mapping</param>
/// <param name="targetRevision">Revision (commit SHA, branch, tag..) onto which to synchronize, leave empty for HEAD</param>
/// <param name="targetVersion">Version of packages, that the SHA we're updating to, produced</param>
/// <param name="initializeDependencies">When true, initializes dependencies (from Version.Details.xml) recursively</param>
/// <param name="cancellationToken">Cancellation token</param>
Task InitializeRepository(string mappingName, string? targetRevision, bool initializeDependencies, CancellationToken cancellationToken)
Task InitializeRepository(
string mappingName,
string? targetRevision,
string? targetVersion,
bool initializeDependencies,
CancellationToken cancellationToken)
{
var mapping = Mappings.FirstOrDefault(m => m.Name == mappingName)
?? throw new Exception($"No repository mapping named `{mappingName}` found!");

return InitializeRepository(mapping, targetRevision, initializeDependencies, cancellationToken);
return InitializeRepository(mapping, targetRevision, targetVersion, initializeDependencies, cancellationToken);
}

/// <summary>
/// Initializes new repo that hasn't been synchronized into the VMR yet.
/// </summary>
/// <param name="mapping">Repository mapping</param>
/// <param name="targetRevision">Revision (commit SHA, branch, tag..) onto which to synchronize, leave empty for HEAD</param>
/// <param name="targetVersion">Version of packages, that the SHA we're updating to, produced</param>
/// <param name="initializeDependencies">When true, initializes dependencies (from Version.Details.xml) recursively</param>
/// <param name="cancellationToken">Cancellation token</param>
Task InitializeRepository(SourceMapping mapping, string? targetRevision, bool initializeDependencies, CancellationToken cancellationToken);
Task InitializeRepository(
SourceMapping mapping,
string? targetRevision,
string? targetVersion,
bool initializeDependencies,
CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,37 @@ public interface IVmrUpdater : IVmrManager
/// </summary>
/// <param name="mappingName">Name of a repository mapping</param>
/// <param name="targetRevision">Revision (commit SHA, branch, tag..) onto which to synchronize, leave empty for HEAD</param>
/// <param name="targetVersion">Version of packages, that the SHA we're updating to, produced</param>
/// <param name="noSquash">Whether to pull changes commit by commit instead of squashing all updates into one</param>
/// <param name="updateDependencies">When true, updates dependencies (from Version.Details.xml) recursively</param>
/// <param name="cancellationToken">Cancellation token</param>
Task UpdateRepository(
string mappingName,
string? targetRevision,
string? targetVersion,
bool noSquash,
bool updateDependencies,
CancellationToken cancellationToken)
{
var mapping = Mappings.FirstOrDefault(m => m.Name == mappingName)
?? throw new Exception($"No repository mapping named `{mappingName}` found!");

return UpdateRepository(mapping, targetRevision, noSquash, updateDependencies, cancellationToken);
return UpdateRepository(mapping, targetRevision, null, noSquash, updateDependencies, cancellationToken);
}

/// <summary>
/// Updates repo in the VMR to given revision.
/// </summary>
/// <param name="mapping">Repository mapping</param>
/// <param name="targetRevision">Revision (commit SHA, branch, tag..) onto which to synchronize, leave empty for HEAD</param>
/// <param name="targetVersion">Version of packages, that the SHA we're updating to, produced</param>
/// <param name="noSquash">Whether to pull changes commit by commit instead of squashing all updates into one</param>
/// <param name="updateDependencies">When true, updates dependencies (from Version.Details.xml) recursively</param>
/// <param name="cancellationToken">Cancellation token</param>
Task UpdateRepository(
SourceMapping mapping,
string? targetRevision,
string? targetVersion,
bool noSquash,
bool updateDependencies,
CancellationToken cancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ public class SourceMappingParser : ISourceMappingParser
{
public async Task<IReadOnlyCollection<SourceMapping>> ParseMappings(string vmrPath)
{
var mappingFilePath = Path.Combine(vmrPath, VmrManagerBase.VmrSourcesPath, VmrManagerBase.SourceMappingsFileName);
var mappingFilePath = Path.Combine(vmrPath, VmrDependencyTracker.VmrSourcesDir, VmrDependencyTracker.SourceMappingsFileName);
var mappingFile = new FileInfo(mappingFilePath);

if (!mappingFile.Exists)
{
throw new FileNotFoundException(
$"Failed to find {VmrManagerBase.SourceMappingsFileName} file in the VMR directory",
$"Failed to find {VmrDependencyTracker.SourceMappingsFileName} file in the VMR directory",
mappingFilePath);
}

Expand All @@ -41,7 +41,7 @@ public async Task<IReadOnlyCollection<SourceMapping>> ParseMappings(string vmrPa

using var stream = File.Open(mappingFile.FullName, FileMode.Open);
var settings = await JsonSerializer.DeserializeAsync<SourceMappingFile>(stream, options)
?? throw new Exception($"Failed to deserialize {VmrManagerBase.SourceMappingsFileName}");
?? throw new Exception($"Failed to deserialize {VmrDependencyTracker.SourceMappingsFileName}");

var patchesPath = settings.PatchesPath;
if (patchesPath is not null)
Expand All @@ -56,6 +56,18 @@ public async Task<IReadOnlyCollection<SourceMapping>> ParseMappings(string vmrPa

private static SourceMapping CreateMapping(SourceMappingSetting defaults, SourceMappingSetting setting, string? patchesPath)
{
if (setting.Name is null)
{
throw new InvalidOperationException(
$"Missing `{nameof(SourceMapping.Name).ToLower()}` in {VmrDependencyTracker.SourceMappingsFileName}");
}

if (setting.DefaultRemote is null)
{
throw new InvalidOperationException(
$"Missing `{nameof(SourceMapping.DefaultRemote).ToLower()}` in {VmrDependencyTracker.SourceMappingsFileName}");
}

IEnumerable<string> include = setting.Include ?? Enumerable.Empty<string>();
IEnumerable<string> exclude = setting.Exclude ?? Enumerable.Empty<string>();

Expand All @@ -77,9 +89,9 @@ private static SourceMapping CreateMapping(SourceMappingSetting defaults, Source
: Array.Empty<string>();

return new SourceMapping(
Name: setting.Name ?? throw new InvalidOperationException($"Missing `{nameof(SourceMapping.Name).ToLower()}` in {VmrManagerBase.SourceMappingsFileName}"),
Name: setting.Name,
Version: setting.Version,
DefaultRemote: setting.DefaultRemote ?? throw new InvalidOperationException($"Missing `{nameof(SourceMapping.DefaultRemote).ToLower()}` in {VmrManagerBase.SourceMappingsFileName}"),
DefaultRemote: setting.DefaultRemote,
DefaultRef: setting.DefaultRef ?? defaults.DefaultRef ?? "main",
Include: include.ToImmutableArray(),
Exclude: exclude.ToImmutableArray(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Microsoft.DotNet.Darc.Models.VirtualMonoRepo;

#nullable enable
namespace Microsoft.DotNet.DarcLib.VirtualMonoRepo;

public record VmrDependencyVersion(string Sha, string? PackageVersion);

public interface IVmrDependencyTracker
{
string VmrPath { get; }

string SourcesPath { get; }

IReadOnlyCollection<SourceMapping> Mappings { get; }

string GetRepoSourcesPath(SourceMapping mapping) => Path.Combine(SourcesPath, mapping.Name);

void UpdateDependencyVersion(SourceMapping mapping, VmrDependencyVersion version);

VmrDependencyVersion? GetDependencyVersion(SourceMapping mapping);
}

/// <summary>
/// Holds information about versions of individual repositories synchronized in the VMR.
/// Uses the AllRepoVersions.props file as source of truth and propagates changes into the git-info files.
/// </summary>
public class VmrDependencyTracker : IVmrDependencyTracker
{
public const string SourceMappingsFileName = "source-mappings.json";
public const string VmrSourcesDir = "src";
public const string GitInfoSourcesDir = "git-info";

// TODO: https://github.com/dotnet/source-build/issues/2250
private const string DefaultVersion = "8.0.100";

private readonly Lazy<AllVersionsPropsFile> _repoVersions;
private readonly string _allVersionsFilePath;

public string VmrPath { get; }

public string SourcesPath { get; }

public IReadOnlyCollection<SourceMapping> Mappings { get; }

public VmrDependencyTracker(
IVmrManagerConfiguration configuration,
IReadOnlyCollection<SourceMapping> mappings)
{
VmrPath = configuration.VmrPath;
SourcesPath = Path.Combine(configuration.VmrPath, VmrSourcesDir);
Mappings = mappings;

_allVersionsFilePath = Path.Combine(VmrPath, GitInfoSourcesDir, AllVersionsPropsFile.FileName);
_repoVersions = new Lazy<AllVersionsPropsFile>(LoadAllVersionsFile, LazyThreadSafetyMode.ExecutionAndPublication);
}

public VmrDependencyVersion? GetDependencyVersion(SourceMapping mapping)
=> _repoVersions.Value.GetVersion(mapping.Name);

public void UpdateDependencyVersion(SourceMapping mapping, VmrDependencyVersion version)
{
// TODO: https://github.com/dotnet/source-build/issues/2250
if (version.PackageVersion is null)
{
version = version with { PackageVersion = DefaultVersion };
}

_repoVersions.Value.UpdateVersion(mapping.Name, version.Sha, version.PackageVersion);
_repoVersions.Value.SerializeToXml(_allVersionsFilePath);

var (buildId, releaseLabel) = VersionFiles.DeriveBuildInfo(mapping.Name, version.PackageVersion);

var gitInfo = new GitInfoFile
{
GitCommitHash = version.Sha,
OfficialBuildId = buildId,
PreReleaseVersionLabel = releaseLabel,
IsStable = string.IsNullOrWhiteSpace(releaseLabel),
OutputPackageVersion = version.PackageVersion,
};

gitInfo.SerializeToXml(GetGitInfoFilePath(mapping));
}

private AllVersionsPropsFile LoadAllVersionsFile()
{
if (!File.Exists(_allVersionsFilePath))
{
return new AllVersionsPropsFile(new());
}

return AllVersionsPropsFile.DeserializeFromXml(_allVersionsFilePath);
}

private string GetGitInfoFilePath(SourceMapping mapping) => Path.Combine(VmrPath, GitInfoSourcesDir, $"{mapping.Name}.props");
}
Loading