Skip to content
Open
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
2 changes: 1 addition & 1 deletion NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<clear />
<!--Begin: Package sources managed by Dependency Flow automation. Do not edit the sources below.-->
<!-- Begin: Package sources from dotnet-dotnet -->
<add key="darc-pub-dotnet-dotnet-493580a" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-dotnet-493580a5/nuget/v3/index.json" />
<add key="darc-pub-dotnet-dotnet-5549d45" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/darc-pub-dotnet-dotnet-5549d455/nuget/v3/index.json" />
<!-- End: Package sources from dotnet-dotnet -->
<!--End: Package sources managed by Dependency Flow automation. Do not edit the sources above.-->
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
Expand Down
2 changes: 1 addition & 1 deletion eng/Version.Details.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This file should be imported by eng/Versions.props
<Project>
<PropertyGroup>
<!-- dotnet-dotnet dependencies -->
<MicrosoftDotNetArcadeSdkPackageVersion>10.0.0-beta.26412.103</MicrosoftDotNetArcadeSdkPackageVersion>
<MicrosoftDotNetArcadeSdkPackageVersion>10.0.0-beta.26413.116</MicrosoftDotNetArcadeSdkPackageVersion>
<SystemCommandLinePackageVersion>2.0.12</SystemCommandLinePackageVersion>
<SystemSecurityCryptographyXmlPackageVersion>10.0.12</SystemSecurityCryptographyXmlPackageVersion>
</PropertyGroup>
Expand Down
10 changes: 5 additions & 5 deletions eng/Version.Details.xml
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Dependencies>
<Source Uri="https://github.com/dotnet/dotnet" Mapping="sourcelink" Sha="493580a515a01970cfe1da2c7dd589efbaf36996" BarId="326844" />
<Source Uri="https://github.com/dotnet/dotnet" Mapping="sourcelink" Sha="5549d455e17cefdd7e202cb31fce756a0847efc9" BarId="327141" />
<ProductDependencies>
<Dependency Name="System.CommandLine" Version="2.0.12">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>493580a515a01970cfe1da2c7dd589efbaf36996</Sha>
<Sha>5549d455e17cefdd7e202cb31fce756a0847efc9</Sha>
</Dependency>
<Dependency Name="System.Security.Cryptography.Xml" Version="10.0.12">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>493580a515a01970cfe1da2c7dd589efbaf36996</Sha>
<Sha>5549d455e17cefdd7e202cb31fce756a0847efc9</Sha>
</Dependency>
</ProductDependencies>
<ToolsetDependencies>
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="10.0.0-beta.26412.103">
<Dependency Name="Microsoft.DotNet.Arcade.Sdk" Version="10.0.0-beta.26413.116">
<Uri>https://github.com/dotnet/dotnet</Uri>
<Sha>493580a515a01970cfe1da2c7dd589efbaf36996</Sha>
<Sha>5549d455e17cefdd7e202cb31fce756a0847efc9</Sha>
</Dependency>
</ToolsetDependencies>
</Dependencies>
18 changes: 14 additions & 4 deletions eng/common/Get-GitHubAppToken.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,13 @@ try {
$installations = @()
$page = 1
do {
$pageInstallations = @(Invoke-RestMethod `
# Assign the response before wrapping it in @(). PowerShell otherwise
# preserves a top-level JSON array as one nested pipeline object.
$pageResponse = Invoke-RestMethod `
-Uri "https://api.github.com/app/installations?per_page=100&page=$page" `
-Headers $headers `
-Method Get)
-Method Get
$pageInstallations = @($pageResponse)
$installations += $pageInstallations
$page++
} while ($pageInstallations.Count -eq 100)
Expand All @@ -125,12 +128,19 @@ catch {
Write-PipelineTelemetryError -Category 'Build' -Message "Failed to list GitHub App installations: $_. The signed JWT may be invalid or the App's Client ID ('$AppClientId') may be incorrect."
exit 1
}
$installation = $installations | Where-Object { $_.account.login -ieq $InstallationOwner } | Select-Object -First 1
if (-not $installation) {
$matchingInstallations = @($installations | Where-Object { $_.account.login -ieq $InstallationOwner })
if ($matchingInstallations.Count -eq 0) {
$found = ($installations | ForEach-Object { $_.account.login }) -join ', '
Write-PipelineTelemetryError -Category 'Build' -Message "No installation found for '$InstallationOwner'. App is installed on: $found"
exit 1
}
if ($matchingInstallations.Count -ne 1) {
$matchingIds = ($matchingInstallations | ForEach-Object { $_.id }) -join ', '
Write-PipelineTelemetryError -Category 'Build' -Message "Found multiple installations for '$InstallationOwner': $matchingIds"
exit 1
}
$installation = $matchingInstallations[0]
Write-Host "Using installation $($installation.id) for '$($installation.account.login)'."

try {
$tokenResponse = Invoke-RestMethod `
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dotnet": "10.0.111"
},
"msbuild-sdks": {
"Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26412.103",
"Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26413.116",
"Microsoft.Build.NoTargets": "3.7.0"
}
}
21 changes: 13 additions & 8 deletions src/Common/AzureDevOps/AzureDevOpsUrlParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,17 @@ public static bool TryParseOnPremHttp(string relativeUrl, string virtualDirector
return true;
}

public static bool TryParseHostedSsh(Uri uri, [NotNullWhen(true)]out string? account, [NotNullWhen(true)]out string? repositoryPath, [NotNullWhen(true)]out string? repositoryName)
public static bool TryParseHostedSsh(
Uri uri,
[NotNullWhen(true)] out string? account,
[NotNullWhen(true)] out string? repositoryPath,
[NotNullWhen(true)] out string? repositoryName,
out bool isUnsupportedFormat)
{
NullableDebug.Assert(uri != null);

account = repositoryPath = repositoryName = null;
isUnsupportedFormat = false;

// {"DefaultCollection"|""}/{repositoryPath}/"_ssh"/{"_full"|"_optimized"}/{repositoryName}
if (!UriUtilities.TrySplitRelativeUrl(uri.GetPath(), out var parts) || parts.Length == 0)
Expand All @@ -125,7 +131,7 @@ public static bool TryParseHostedSsh(Uri uri, [NotNullWhen(true)]out string? acc
// Check for v3 url format
if (parts[0] == "v3" &&
parts.Length >= 3 &&
TryParsePath(parts, 2, type: null, out repositoryPath, out repositoryName) &&
TryParsePath(parts, startIndex: 2, type: null, out repositoryPath, out repositoryName) &&
repositoryPath != "")
{
// ssh://{user}@{domain}:{port}/v3/{account}/{repositoryPath}/{'_full'|'_optimized'|''}/{repositoryName}
Expand All @@ -136,8 +142,6 @@ public static bool TryParseHostedSsh(Uri uri, [NotNullWhen(true)]out string? acc
// ssh v1/v2 url formats
// ssh://{account}@vs-ssh.visualstudio.com/

account = uri.UserInfo;

int index = 0;
if (StringComparer.OrdinalIgnoreCase.Equals(parts[0], "DefaultCollection"))
{
Expand All @@ -149,14 +153,15 @@ public static bool TryParseHostedSsh(Uri uri, [NotNullWhen(true)]out string? acc
// Failed to parse path
return false;
}
}

if (account.Length == 0)
{
// The format uses SSH connection user name as an account name.
// It is no longer supported since GitOperations.GetRepositoryUrl strips the user info
// to prevent leaking credentials.
isUnsupportedFormat = true;
return false;
}

return true;
return account.Length > 0;
}

public static bool TryParseOnPremSsh(Uri uri, [NotNullWhen(true)]out string? repositoryPath, [NotNullWhen(true)]out string? repositoryName)
Expand Down
41 changes: 22 additions & 19 deletions src/Microsoft.Build.Tasks.Git.UnitTests/GitOperationsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -224,23 +224,26 @@ public void GetRepositoryUrl_UnsupportedUrl(string kind)
}

[Theory]
[InlineData("https://github.com/org/repo")]
[InlineData("http://github.com/org/repo")]
[InlineData("http://github.com:102/org/repo")]
[InlineData("ssh://user@github.com/org/repo")]
[InlineData("abc://user@github.com/org/repo")]
public void NormalizeUrl_PlatformAgnostic1(string url)
[InlineData("https://github.com/org/repo", "https://github.com/org/repo")]
[InlineData("http://github.com/org/repo", "http://github.com/org/repo")]
[InlineData("http://github.com:102/org/repo", "http://github.com:102/org/repo")]
[InlineData("ssh://user@github.com/org/repo", "ssh://git@github.com/org/repo")] // "user" replaced with "git" in SSH URL
[InlineData("abc://user@github.com/org/repo", "abc://github.com/org/repo")]
public void NormalizeUrl_PlatformAgnostic1(string url, string expected)
{
AssertEx.AreEqual(url, GitOperations.NormalizeUrl(url, s_root)?.AbsoluteUri);
AssertEx.AreEqual(expected, GitOperations.NormalizeUrl(url, s_root)?.AbsoluteUri);
}

[Theory]
[InlineData("http://?", null)]
[InlineData("https://github.com/org/repo/./.", "https://github.com/org/repo/")]
[InlineData("http://github.com/org/" + TestStrings.RepoName, "http://github.com/org/" + TestStrings.RepoNameFullyEscaped)]
[InlineData("ssh://github.com/org/../repo", "ssh://github.com/repo")]
[InlineData("ssh://github.com/%32/repo", "ssh://github.com/2/repo")]
[InlineData("ssh://github.com/%3F/repo", "ssh://github.com/%3F/repo")]
[InlineData("ssh://github.com/org/../repo", "ssh://git@github.com/repo")]
[InlineData("ssh://github.com/%32/repo", "ssh://git@github.com/2/repo")]
[InlineData("ssh://github.com/%3F/repo", "ssh://git@github.com/%3F/repo")]
[InlineData(@"../.:./../../relative/path", null)]
[InlineData(@".:/../../relative/path", null)]
[InlineData(@"..:/../../relative/path", null)]
public void NormalizeUrl_PlatformAgnostic2(string url, string? expectedUrl)
{
AssertEx.AreEqual(expectedUrl, GitOperations.NormalizeUrl(url, s_root)?.AbsoluteUri);
Expand Down Expand Up @@ -269,14 +272,14 @@ public void NormalizeUrl_Unix(string url, string expectedUrl)
}

[Theory]
[InlineData("abc:org/repo", "ssh://abc/org/repo")]
[InlineData("abc:org/x%20y", "ssh://abc/org/x%20y")]
[InlineData("ABC:ORG/REPO/X/Y", "ssh://abc/ORG/REPO/X/Y")]
[InlineData("github.com:org/repo", "ssh://github.com/org/repo")]
[InlineData("git@github.com:org/repo", "ssh://git@github.com/org/repo")]
[InlineData("@github.com:org/repo", "ssh://@github.com/org/repo")]
[InlineData("http:x//y", "ssh://http/x//y")]
public void GetRepositoryUrl_ScpSyntax(string url, string expectedUrl)
[InlineData("abc:org/repo", "ssh://git@abc/org/repo")]
[InlineData("abc:org/x%20y", "ssh://git@abc/org/x%20y")]
[InlineData("ABC:ORG/REPO/X/Y", "ssh://git@abc/ORG/REPO/X/Y")]
[InlineData("github.com:org/repo", "ssh://git@github.com/org/repo")]
[InlineData("user@github.com:org/repo", "ssh://git@github.com/org/repo")] // "user" replaced with "git" in SSH URL
[InlineData("@github.com:org/repo", "ssh://git@github.com/org/repo")]
[InlineData("http:x//y", "ssh://git@http/x//y")]
public void NormalizeUrl_ScpSyntax(string url, string expectedUrl)
{
Assert.Equal(expectedUrl, GitOperations.NormalizeUrl(url, s_root)?.AbsoluteUri);
}
Expand Down Expand Up @@ -398,7 +401,7 @@ public void GetSourceRoots_RepoWithoutCommitsWithSubmodules()
// URLs listed in .submodules are ignored (they are used by git submodule initialize to generate URLs stored in config).
AssertEx.Equal(new[]
{
$@"'{_workingDir}{s}sub{s}1{s}' SourceControl='git' RevisionId='1111111111111111111111111111111111111111' NestedRoot='sub/1/' ContainingRoot='{_workingDir}{s}' ScmRepositoryUrl='ssh://github.com/sub-1'",
$@"'{_workingDir}{s}sub{s}1{s}' SourceControl='git' RevisionId='1111111111111111111111111111111111111111' NestedRoot='sub/1/' ContainingRoot='{_workingDir}{s}' ScmRepositoryUrl='ssh://git@github.com/sub-1'",
$@"'{_workingDir}{s}sub{s}3{s}' SourceControl='git' RevisionId='3333333333333333333333333333333333333333' NestedRoot='sub/3/' ContainingRoot='{_workingDir}{s}' ScmRepositoryUrl='https://github.com/sub-3'",
$@"'{_workingDir}{s}sub{s}6{s}' SourceControl='git' RevisionId='6666666666666666666666666666666666666666' NestedRoot='sub/6/' ContainingRoot='{_workingDir}{s}' ScmRepositoryUrl='https://github.com/sub-6'",
}, items.Select(TestUtilities.InspectSourceRoot));
Expand Down
28 changes: 28 additions & 0 deletions src/Microsoft.Build.Tasks.Git/GitOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,35 @@ internal static string ApplyInsteadOfUrlMapping(GitConfig config, string url)
private static bool IsSupportedScheme(string scheme)
=> scheme is "http" or "https" or "ssh" or "git";

// internal for testing
internal static Uri? NormalizeUrl(string url, string root)
{
var normalizedUrl = NormalizeUrlImpl(url, root);
if (normalizedUrl == null)
{
return null;
}

// remove user info to avoid embedding access tokens to build artifacts:
var builder = new UriBuilder(normalizedUrl)
{
// If user name is not specified in SSH URL, the local user name is used for connecting to the repo.
// Use "git" placeholder instead of a specific user name.
UserName = normalizedUrl.Scheme is "ssh" ? "git" : null,
Password = null,
};

try
{
return builder.Uri;
}
catch
{
return null;
}
}

private static Uri? NormalizeUrlImpl(string url, string root)
{
// Since git supports scp-like syntax for SSH URLs we convert it here,
// so that RepositoryUrl is actually a valid URL in that case.
Expand Down
Loading
Loading