Skip to content

Commit 31a772f

Browse files
authored
Do not report warnings when SourceLink packages are not referenced ex… (#991)
* Do not report warnings when SourceLink packages are not referenced explicitly and the repository has no URL or no commit. This allows the user to build an app in a new local repo, without having to set origin remote or commit changes first.
1 parent 0537c44 commit 31a772f

35 files changed

+574
-110
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*.suo
66
*.user
77
.vs/
8+
.vscode/
89

910
# Build results
1011
artifacts/

src/Common/GetSourceLinkUrlGitTask.cs

+9-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,15 @@ private void ExecuteImpl()
8585
if (string.IsNullOrEmpty(gitUrl))
8686
{
8787
SourceLinkUrl = NotApplicableValue;
88-
Log.LogWarning(CommonResources.UnableToDetermineRepositoryUrl);
88+
89+
// If SourceRoot has commit sha but not repository URL the source control info is available,
90+
// but the remote for the repo has not been defined yet. We already reported missing remote in that case
91+
// (unless suppressed).
92+
if (string.IsNullOrEmpty(SourceRoot.GetMetadata(Names.SourceRoot.RevisionId)))
93+
{
94+
Log.LogWarning(CommonResources.UnableToDetermineRepositoryUrl);
95+
}
96+
8997
return;
9098
}
9199

src/Microsoft.Build.Tasks.Git.UnitTests/GitDataTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public void MinimalGitData()
5454
Assert.Equal("1111111111111111111111111111111111111111", repository.GetHeadCommitSha());
5555

5656
var warnings = new List<(string, object?[])>();
57-
var sourceRoots = GitOperations.GetSourceRoots(repository, remoteName: null, (message, args) => warnings.Add((message, args)));
57+
var sourceRoots = GitOperations.GetSourceRoots(repository, remoteName: null, warnOnMissingCommit: true, (message, args) => warnings.Add((message, args)));
5858
AssertEx.Equal(new[]
5959
{
6060
$@"'{repoDir.Path}{s}' SourceControl='git' RevisionId='1111111111111111111111111111111111111111' ScmRepositoryUrl='http://github.com/test-org/test-repo'",

src/Microsoft.Build.Tasks.Git.UnitTests/GitOperationsTests.cs

+45-9
Original file line numberDiff line numberDiff line change
@@ -448,16 +448,53 @@ public void ApplyInsteadOfUrlMapping_Multiple()
448448
Assert.Equal("A.com", actualMappedUrl);
449449
}
450450

451-
[Fact]
452-
public void GetSourceRoots_RepoWithoutCommits()
451+
[Theory]
452+
[CombinatorialData]
453+
public void GetSourceRoots_RepoWithoutCommits(bool warnOnMissingCommit)
453454
{
454455
var repo = CreateRepository();
455456

456457
var warnings = new List<(string, object?[])>();
457-
var items = GitOperations.GetSourceRoots(repo, remoteName: null, (message, args) => warnings.Add((message, args)));
458+
var items = GitOperations.GetSourceRoots(repo, remoteName: null, warnOnMissingCommit, (message, args) => warnings.Add((message, args)));
458459

459460
Assert.Empty(items);
460-
AssertEx.Equal(new[] { Resources.RepositoryHasNoCommit }, warnings.Select(TestUtilities.InspectDiagnostic));
461+
AssertEx.Equal(warnOnMissingCommit ? new[] { Resources.RepositoryHasNoCommit } : Array.Empty<string>(), warnings.Select(TestUtilities.InspectDiagnostic));
462+
}
463+
464+
[Fact]
465+
public void GetSourceRoots_RepoWithCommits_WithoutUrl()
466+
{
467+
var repo = CreateRepository(
468+
commitSha: "0000000000000000000000000000000000000000");
469+
470+
var warnings = new List<(string, object?[])>();
471+
var items = GitOperations.GetSourceRoots(repo, remoteName: null, warnOnMissingCommit: true, (message, args) => warnings.Add((message, args)));
472+
473+
AssertEx.Equal(new[]
474+
{
475+
$@"'{_workingDir}{s}' SourceControl='git' RevisionId='0000000000000000000000000000000000000000'",
476+
}, items.Select(TestUtilities.InspectSourceRoot));
477+
478+
Assert.Empty(warnings.Select(TestUtilities.InspectDiagnostic));
479+
}
480+
481+
[Fact]
482+
public void GetSourceRoots_RepoWithCommits_WithUrl()
483+
{
484+
var repo = CreateRepository(
485+
commitSha: "0000000000000000000000000000000000000000",
486+
config: CreateConfig(
487+
("remote.origin.url", "http://github.com/abc")));
488+
489+
var warnings = new List<(string, object?[])>();
490+
var items = GitOperations.GetSourceRoots(repo, remoteName: null, warnOnMissingCommit: true, (message, args) => warnings.Add((message, args)));
491+
492+
AssertEx.Equal(new[]
493+
{
494+
$@"'{_workingDir}{s}' SourceControl='git' RevisionId='0000000000000000000000000000000000000000' ScmRepositoryUrl='http://github.com/abc'",
495+
}, items.Select(TestUtilities.InspectSourceRoot));
496+
497+
Assert.Empty(warnings.Select(TestUtilities.InspectDiagnostic));
461498
}
462499

463500
[Fact]
@@ -480,7 +517,7 @@ public void GetSourceRoots_RepoWithoutCommitsWithSubmodules()
480517
CreateSubmodule("sub6", "sub/6", "", "6666666666666666666666666666666666666666")));
481518

482519
var warnings = new List<(string, object?[])>();
483-
var items = GitOperations.GetSourceRoots(repo, remoteName: null, (message, args) => warnings.Add((message, args)));
520+
var items = GitOperations.GetSourceRoots(repo, remoteName: null, warnOnMissingCommit: false, (message, args) => warnings.Add((message, args)));
484521

485522
// Module without a configuration entry is not initialized.
486523
// URLs listed in .submodules are ignored (they are used by git submodule initialize to generate URLs stored in config).
@@ -493,9 +530,8 @@ public void GetSourceRoots_RepoWithoutCommitsWithSubmodules()
493530

494531
AssertEx.Equal(new[]
495532
{
496-
Resources.RepositoryHasNoCommit,
497533
string.Format(Resources.SourceCodeWontBeAvailableViaSourceLink, string.Format(Resources.InvalidSubmoduleUrl, "sub4", "https:///"))
498-
}, warnings.Select(TestUtilities.InspectDiagnostic)); ;
534+
}, warnings.Select(TestUtilities.InspectDiagnostic));
499535
}
500536

501537
[Fact]
@@ -511,7 +547,7 @@ public void GetSourceRoots_RepoWithCommitsWithSubmodules()
511547
CreateSubmodule("2", "sub/2", "http://2.com", "2222222222222222222222222222222222222222")));
512548

513549
var warnings = new List<(string, object?[])>();
514-
var items = GitOperations.GetSourceRoots(repo, remoteName: null, (message, args) => warnings.Add((message, args)));
550+
var items = GitOperations.GetSourceRoots(repo, remoteName: null, warnOnMissingCommit: false, (message, args) => warnings.Add((message, args)));
515551

516552
AssertEx.Equal(new[]
517553
{
@@ -547,7 +583,7 @@ public void GetSourceRoots_RelativeSubmodulePath()
547583
CreateSubmodule("1", "sub/1", "---", "1111111111111111111111111111111111111111", containingRepositoryWorkingDir: repoDir.Path)));
548584

549585
var warnings = new List<(string, object?[])>();
550-
var items = GitOperations.GetSourceRoots(repo, remoteName: null, (message, args) => warnings.Add((message, args)));
586+
var items = GitOperations.GetSourceRoots(repo, remoteName: null, warnOnMissingCommit: false, (message, args) => warnings.Add((message, args)));
551587

552588
AssertEx.Equal(new[]
553589
{

src/Microsoft.Build.Tasks.Git/GitOperations.cs

+21-13
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ internal static class GitOperations
2525
private const string UrlSectionName = "url";
2626
private const string UrlVariableName = "url";
2727

28-
public static string? GetRepositoryUrl(GitRepository repository, string? remoteName, Action<string, object?[]>? logWarning = null)
29-
=> GetRepositoryUrl(repository, remoteName, recursionDepth: 0, logWarning);
28+
public static string? GetRepositoryUrl(GitRepository repository, string? remoteName, bool warnOnMissingRemote = true, Action<string, object?[]>? logWarning = null)
29+
=> GetRepositoryUrl(repository, remoteName, recursionDepth: 0, warnOnMissingRemote, logWarning);
3030

31-
private static string? GetRepositoryUrl(GitRepository repository, string? remoteName, int recursionDepth, Action<string, object?[]>? logWarning = null)
31+
private static string? GetRepositoryUrl(GitRepository repository, string? remoteName, int recursionDepth, bool warnOnMissingRemote, Action<string, object?[]>? logWarning)
3232
{
3333
NullableDebug.Assert(repository.WorkingDirectory != null);
3434

35-
var remoteUrl = GetRemoteUrl(repository, ref remoteName, logWarning);
35+
var remoteUrl = GetRemoteUrl(repository, ref remoteName, warnOnMissingRemote, logWarning);
3636
if (remoteUrl == null)
3737
{
3838
return null;
@@ -45,10 +45,10 @@ internal static class GitOperations
4545
return null;
4646
}
4747

48-
return ResolveUrl(uri, repository.Environment, remoteName, recursionDepth, logWarning);
48+
return ResolveUrl(uri, repository.Environment, remoteName, recursionDepth, warnOnMissingRemote, logWarning);
4949
}
5050

51-
private static string? GetRemoteUrl(GitRepository repository, ref string? remoteName, Action<string, object?[]>? logWarning)
51+
private static string? GetRemoteUrl(GitRepository repository, ref string? remoteName, bool warnOnMissingRemote, Action<string, object?[]>? logWarning)
5252
{
5353
string? unknownRemoteName = null;
5454
string? remoteUrl = null;
@@ -63,7 +63,11 @@ internal static class GitOperations
6363

6464
if (remoteUrl == null && !TryGetRemote(repository.Config, out remoteName, out remoteUrl))
6565
{
66-
logWarning?.Invoke(Resources.RepositoryHasNoRemote, new[] { repository.WorkingDirectory });
66+
if (warnOnMissingRemote)
67+
{
68+
logWarning?.Invoke(Resources.RepositoryHasNoRemote, new[] { repository.WorkingDirectory });
69+
}
70+
6771
return null;
6872
}
6973

@@ -75,7 +79,7 @@ internal static class GitOperations
7579
return remoteUrl;
7680
}
7781

78-
private static string? ResolveUrl(Uri uri, GitEnvironment environment, string? remoteName, int recursionDepth, Action<string, object?[]>? logWarning)
82+
private static string? ResolveUrl(Uri uri, GitEnvironment environment, string? remoteName, int recursionDepth, bool warnOnMissingRemote, Action<string, object?[]>? logWarning)
7983
{
8084
if (!uri.IsFile)
8185
{
@@ -85,7 +89,11 @@ internal static class GitOperations
8589
var repositoryPath = uri.LocalPath;
8690
if (!GitRepository.TryGetRepositoryLocation(repositoryPath, out var remoteRepositoryLocation))
8791
{
88-
logWarning?.Invoke(Resources.RepositoryHasNoRemote, new[] { repositoryPath });
92+
if (warnOnMissingRemote)
93+
{
94+
logWarning?.Invoke(Resources.RepositoryHasNoRemote, new[] { repositoryPath });
95+
}
96+
8997
return uri.AbsoluteUri;
9098
}
9199

@@ -102,7 +110,7 @@ internal static class GitOperations
102110
return null;
103111
}
104112

105-
return GetRepositoryUrl(remoteRepository, remoteName, recursionDepth + 1, logWarning) ?? uri.AbsoluteUri;
113+
return GetRepositoryUrl(remoteRepository, remoteName, recursionDepth + 1, warnOnMissingRemote, logWarning) ?? uri.AbsoluteUri;
106114
}
107115

108116
private static bool TryGetRemote(GitConfig config, [NotNullWhen(true)]out string? remoteName, [NotNullWhen(true)]out string? remoteUrl)
@@ -239,7 +247,7 @@ private static bool TryParseScp(string value, [NotNullWhen(true)]out Uri? uri)
239247
return Uri.TryCreate(url, UriKind.Absolute, out uri);
240248
}
241249

242-
public static ITaskItem[] GetSourceRoots(GitRepository repository, string? remoteName, Action<string, object?[]> logWarning)
250+
public static ITaskItem[] GetSourceRoots(GitRepository repository, string? remoteName, bool warnOnMissingCommit, Action<string, object?[]> logWarning)
243251
{
244252
// Not supported for repositories without a working directory.
245253
NullableDebug.Assert(repository.WorkingDirectory != null);
@@ -262,7 +270,7 @@ public static ITaskItem[] GetSourceRoots(GitRepository repository, string? remot
262270
item.SetMetadata(Names.SourceRoot.RevisionId, revisionId);
263271
result.Add(item);
264272
}
265-
else
273+
else if (warnOnMissingCommit)
266274
{
267275
logWarning(Resources.RepositoryHasNoCommit, Array.Empty<object>());
268276
}
@@ -298,7 +306,7 @@ public static ITaskItem[] GetSourceRoots(GitRepository repository, string? remot
298306
continue;
299307
}
300308

301-
var submoduleUrl = ResolveUrl(submoduleUri, repository.Environment, remoteName, recursionDepth: 0, logWarning);
309+
var submoduleUrl = ResolveUrl(submoduleUri, repository.Environment, remoteName, recursionDepth: 0, warnOnMissingRemote: true, logWarning);
302310
if (submoduleUrl == null)
303311
{
304312
logWarning(Resources.SourceCodeWontBeAvailableViaSourceLink,

src/Microsoft.Build.Tasks.Git/LocateRepository.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ private protected override void Execute(GitRepository repository)
5353

5454
RepositoryId = repository.GitDirectory;
5555
WorkingDirectory = repository.WorkingDirectory;
56-
Url = GitOperations.GetRepositoryUrl(repository, RemoteName, Log.LogWarning);
57-
Roots = GitOperations.GetSourceRoots(repository, RemoteName, Log.LogWarning);
56+
Url = GitOperations.GetRepositoryUrl(repository, RemoteName, warnOnMissingRemote: !NoWarnOnMissingInfo, Log.LogWarning);
57+
Roots = GitOperations.GetSourceRoots(repository, RemoteName, warnOnMissingCommit: !NoWarnOnMissingInfo, Log.LogWarning);
5858
RevisionId = repository.GetHeadCommitSha();
5959
}
6060
}

src/Microsoft.Build.Tasks.Git/RepositoryTask.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ public abstract class RepositoryTask : Task
3030
#endif
3131

3232
/// <summary>
33-
/// True to report a warning when the repository can't be located.
33+
/// True to report a warning when the repository can't be located, it's missing remote or a commit.
3434
/// </summary>
35-
public bool NoWarnOnMissingRepository { get; set; }
35+
public bool NoWarnOnMissingInfo { get; set; }
3636

3737
public sealed override bool Execute()
3838
{
@@ -62,7 +62,7 @@ bool logAssemblyLoadingErrors()
6262

6363
private void ReportMissingRepositoryWarning(string initialPath)
6464
{
65-
if (!NoWarnOnMissingRepository)
65+
if (!NoWarnOnMissingInfo)
6666
{
6767
Log.LogWarning(Resources.UnableToLocateRepository, initialPath);
6868
}

src/Microsoft.Build.Tasks.Git/build/Microsoft.Build.Tasks.Git.targets

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
Path="$(MSBuildProjectDirectory)"
2828
RemoteName="$(GitRepositoryRemoteName)"
2929
ConfigurationScope="$(GitRepositoryConfigurationScope)"
30-
NoWarnOnMissingRepository="$(PkgMicrosoft_Build_Tasks_Git.Equals(''))">
30+
NoWarnOnMissingInfo="$(PkgMicrosoft_Build_Tasks_Git.Equals(''))">
3131

3232
<Output TaskParameter="RepositoryId" PropertyName="_GitRepositoryId" />
3333
<Output TaskParameter="Url" PropertyName="ScmRepositoryUrl" />

src/SourceLink.Common.UnitTests/GenerateSourceLinkFileTests.cs

+40-4
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,44 @@ public void Empty(bool noWarning)
3333

3434
Assert.True(task.Execute());
3535

36-
AssertEx.AssertEqualToleratingWhitespaceDifferences(
37-
noWarning ? "" : "WARNING : " + string.Format(Resources.SourceControlInformationIsNotAvailableGeneratedSourceLinkEmpty),
38-
engine.Log);
36+
var expectedOutput =
37+
(noWarning ? "" : "WARNING : " + string.Format(Resources.SourceControlInformationIsNotAvailableGeneratedSourceLinkEmpty) + Environment.NewLine) +
38+
string.Format(Resources.SourceLinkEmptyNoExistingFile, sourceLinkFilePath);
39+
40+
AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedOutput, engine.Log);
41+
42+
Assert.Null(task.SourceLink);
43+
Assert.Null(task.FileWrite);
44+
}
45+
46+
[Theory]
47+
[CombinatorialData]
48+
public void NoRepositoryUrl(bool noWarning)
49+
{
50+
var sourceLinkFilePath = Path.Combine(TempRoot.Root, Guid.NewGuid().ToString());
51+
52+
var engine = new MockEngine();
53+
54+
var task = new GenerateSourceLinkFile()
55+
{
56+
BuildEngine = engine,
57+
OutputFile = sourceLinkFilePath,
58+
SourceRoots = new[]
59+
{
60+
new MockItem("/1/", KVP("MappedPath", "/1/")),
61+
new MockItem("/2/", KVP("MappedPath", "/2/"), KVP("RevisionId", "f3dbcdfdd5b1f75613c7692f969d8df121fc3731"), KVP("SourceControl", "git")),
62+
new MockItem("/3/", KVP("MappedPath", "/3/"), KVP("RevisionId", "f3dbcdfdd5b1f75613c7692f969d8df121fc3731"), KVP("SourceControl", "git"), KVP("RepositoryUrl", "")),
63+
},
64+
NoWarnOnMissingSourceControlInformation = noWarning,
65+
};
66+
67+
Assert.True(task.Execute());
68+
69+
var expectedOutput =
70+
(noWarning ? "" : "WARNING : " + string.Format(Resources.SourceControlInformationIsNotAvailableGeneratedSourceLinkEmpty) + Environment.NewLine) +
71+
string.Format(Resources.SourceLinkEmptyNoExistingFile, sourceLinkFilePath);
72+
73+
AssertEx.AssertEqualToleratingWhitespaceDifferences(expectedOutput, engine.Log);
3974

4075
Assert.Null(task.SourceLink);
4176
Assert.Null(task.FileWrite);
@@ -61,7 +96,8 @@ public void Empty_DeleteExistingFile()
6196

6297
Assert.True(task.Execute());
6398

64-
AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log);
99+
AssertEx.AssertEqualToleratingWhitespaceDifferences(
100+
string.Format(Resources.SourceLinkEmptyDeletingExistingFile, sourceLinkFile.Path), engine.Log);
65101

66102
Assert.Null(task.SourceLink);
67103
Assert.Equal(sourceLinkFile.Path, task.FileWrite);

src/SourceLink.Common/GenerateSourceLinkFile.cs

+6
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ private void WriteSourceLinkFile(string? content)
124124
{
125125
if (content == null)
126126
{
127+
Log.LogMessage(Resources.SourceLinkEmptyDeletingExistingFile, OutputFile);
128+
127129
File.Delete(OutputFile);
128130
FileWrite = OutputFile;
129131
return;
@@ -133,6 +135,8 @@ private void WriteSourceLinkFile(string? content)
133135
if (originalContent == content)
134136
{
135137
// Don't rewrite the file if the contents is the same, just pass it to the compiler.
138+
Log.LogMessage(Resources.SourceLinkFileUpToDate, OutputFile);
139+
136140
SourceLink = OutputFile;
137141
return;
138142
}
@@ -141,9 +145,11 @@ private void WriteSourceLinkFile(string? content)
141145
{
142146
// File doesn't exist and the output is empty:
143147
// Do not write the file and don't pass it to the compiler.
148+
Log.LogMessage(Resources.SourceLinkEmptyNoExistingFile, OutputFile);
144149
return;
145150
}
146151

152+
Log.LogMessage(Resources.SourceLinkFileUpdated, OutputFile);
147153
File.WriteAllText(OutputFile, content);
148154
FileWrite = SourceLink = OutputFile;
149155
}

src/SourceLink.Common/Resources.resx

+12
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,18 @@
129129
<data name="SourceControlInformationIsNotAvailableGeneratedSourceLinkEmpty" xml:space="preserve">
130130
<value>Source control information is not available - the generated source link is empty.</value>
131131
</data>
132+
<data name="SourceLinkEmptyDeletingExistingFile" xml:space="preserve">
133+
<value>Source Link is empty, deleting existing file: '{0}'.</value>
134+
</data>
135+
<data name="SourceLinkEmptyNoExistingFile" xml:space="preserve">
136+
<value>Source Link is empty, file '{0}' does not exist.</value>
137+
</data>
138+
<data name="SourceLinkFileUpToDate" xml:space="preserve">
139+
<value>Source Link file '{0}' is up-to-date.</value>
140+
</data>
141+
<data name="SourceLinkFileUpdated" xml:space="preserve">
142+
<value>Updating Source Link file '{0}'.</value>
143+
</data>
132144
<data name="IsEmpty" xml:space="preserve">
133145
<value>{0} is empty: '{1}'</value>
134146
</data>

0 commit comments

Comments
 (0)