@@ -35,6 +35,7 @@ internal class DefaultPhysicalGitCheckout : IPhysicalGitCheckout
3535 private readonly IPackageManager _packageManager ;
3636 private readonly ConcurrentDictionary < string , IReservationManager > _sharedReservationManagers ;
3737 private readonly IGlobalMutexReservationManager _globalMutexReservationManager ;
38+ private readonly IGitCredentialHelperProvider ? _gitCredentialHelperProvider ;
3839
3940 public DefaultPhysicalGitCheckout (
4041 ILogger < DefaultPhysicalGitCheckout > logger ,
@@ -44,7 +45,8 @@ public DefaultPhysicalGitCheckout(
4445 ICredentialDiscovery credentialDiscovery ,
4546 IReservationManagerFactory reservationManagerFactory ,
4647 IWorldPermissionApplier worldPermissionApplier ,
47- IPackageManager packageManager )
48+ IPackageManager packageManager ,
49+ IGitCredentialHelperProvider ? gitCredentialHelperProvider = null )
4850 {
4951 _logger = logger ;
5052 _pathResolver = pathResolver ;
@@ -56,6 +58,7 @@ public DefaultPhysicalGitCheckout(
5658 _packageManager = packageManager ;
5759 _sharedReservationManagers = new ConcurrentDictionary < string , IReservationManager > ( ) ;
5860 _globalMutexReservationManager = _reservationManagerFactory . CreateGlobalMutexReservationManager ( ) ;
61+ _gitCredentialHelperProvider = gitCredentialHelperProvider ;
5962 }
6063
6164 /// <summary>
@@ -947,59 +950,62 @@ private async Task CheckoutTargetCommitAsync(
947950 int exitCode ;
948951
949952 _logger . LogInformation ( $ "Checking out target commit { resolvedReference . TargetCommit } ...") ;
950- exitCode = await FaultTolerantGitAsync (
951- new ProcessSpecification
952- {
953- FilePath = gitContext . Git ,
954- Arguments =
955- [
956- "-C" ,
957- repositoryPath ,
958- "-c" ,
959- "advice.detachedHead=false" ,
960- "checkout" ,
961- "--progress" ,
962- "-f" ,
963- resolvedReference . TargetCommit ,
964- ] ,
965- WorkingDirectory = repositoryPath ,
966- EnvironmentVariables = gitContext . GitEnvs ,
967- } ,
968- CaptureSpecification . Sanitized ,
969- cancellationToken ) . ConfigureAwait ( false ) ;
970- if ( exitCode != 0 )
953+ using ( var fetchEnvVars = gitContext . FetchEnvironmentVariablesFactory ( ) )
971954 {
972- // Attempt to re-fetch LFS files, in case that was the error.
973- if ( await DetectGitLfsAndReconfigureIfNecessaryAsync (
974- workspaceDescriptor ,
975- repositoryPath ,
976- repositoryUri ,
977- resolvedReference ,
978- gitContext ,
979- cancellationToken ) . ConfigureAwait ( false ) == GitLfsMode . Detected )
955+ exitCode = await FaultTolerantGitAsync (
956+ new ProcessSpecification
957+ {
958+ FilePath = gitContext . Git ,
959+ Arguments =
960+ [
961+ "-C" ,
962+ repositoryPath ,
963+ "-c" ,
964+ "advice.detachedHead=false" ,
965+ "checkout" ,
966+ "--progress" ,
967+ "-f" ,
968+ resolvedReference . TargetCommit ,
969+ ] ,
970+ WorkingDirectory = repositoryPath ,
971+ EnvironmentVariables = fetchEnvVars . EnvironmentVariables ,
972+ } ,
973+ CaptureSpecification . Sanitized ,
974+ cancellationToken ) . ConfigureAwait ( false ) ;
975+ if ( exitCode != 0 )
980976 {
981- // Re-attempt checkout...
982- _logger . LogInformation ( $ "Re-attempting check out of target commit { resolvedReference . TargetCommit } ...") ;
983- exitCode = await FaultTolerantGitAsync (
984- new ProcessSpecification
985- {
986- FilePath = gitContext . Git ,
987- Arguments =
988- [
989- "-C" ,
990- repositoryPath ,
991- "-c" ,
992- "advice.detachedHead=false" ,
993- "checkout" ,
994- "--progress" ,
995- "-f" ,
996- resolvedReference . TargetCommit ,
997- ] ,
998- WorkingDirectory = repositoryPath ,
999- EnvironmentVariables = gitContext . GitEnvs ,
1000- } ,
1001- CaptureSpecification . Sanitized ,
1002- cancellationToken ) . ConfigureAwait ( false ) ;
977+ // Attempt to re-fetch LFS files, in case that was the error.
978+ if ( await DetectGitLfsAndReconfigureIfNecessaryAsync (
979+ workspaceDescriptor ,
980+ repositoryPath ,
981+ repositoryUri ,
982+ resolvedReference ,
983+ gitContext ,
984+ cancellationToken ) . ConfigureAwait ( false ) == GitLfsMode . Detected )
985+ {
986+ // Re-attempt checkout...
987+ _logger . LogInformation ( $ "Re-attempting check out of target commit { resolvedReference . TargetCommit } ...") ;
988+ exitCode = await FaultTolerantGitAsync (
989+ new ProcessSpecification
990+ {
991+ FilePath = gitContext . Git ,
992+ Arguments =
993+ [
994+ "-C" ,
995+ repositoryPath ,
996+ "-c" ,
997+ "advice.detachedHead=false" ,
998+ "checkout" ,
999+ "--progress" ,
1000+ "-f" ,
1001+ resolvedReference . TargetCommit ,
1002+ ] ,
1003+ WorkingDirectory = repositoryPath ,
1004+ EnvironmentVariables = fetchEnvVars . EnvironmentVariables ,
1005+ } ,
1006+ CaptureSpecification . Sanitized ,
1007+ cancellationToken ) . ConfigureAwait ( false ) ;
1008+ }
10031009 }
10041010 }
10051011 if ( exitCode != 0 )
@@ -1428,6 +1434,9 @@ private async Task CheckoutSubmoduleAsync(
14281434 _logger . LogInformation ( $ "Submodule directory: { submoduleContentPath } ") ;
14291435 _logger . LogInformation ( $ "Submodule exclude on macOS: { ( submodule . ExcludeOnMac ? "yes" : "no" ) } ") ;
14301436
1437+ // Get the submodule URI and environment variables factory.
1438+ var ( uri , fetchEnvironmentVariablesFactory ) = ComputeRepositoryUriAndCredentials ( submoduleUrl ) ;
1439+
14311440 // Check if we already have the target commit in history. If we do, skip fetch.
14321441 var gitTypeStringBuilder = new StringBuilder ( ) ;
14331442 _ = await _processExecutor . ExecuteAsync (
@@ -1450,7 +1459,6 @@ private async Task CheckoutSubmoduleAsync(
14501459 {
14511460 // Fetch the commit that we need.
14521461 _logger . LogInformation ( $ "Fetching submodule { submodule . Path } from remote server...") ;
1453- var ( uri , fetchEnvironmentVariablesFactory ) = ComputeRepositoryUriAndCredentials ( submoduleUrl ) ;
14541462 using ( var fetchEnvVars = fetchEnvironmentVariablesFactory ( ) )
14551463 {
14561464 exitCode = await FaultTolerantGitAsync (
@@ -1482,24 +1490,27 @@ private async Task CheckoutSubmoduleAsync(
14821490
14831491 // Checkout the target commit.
14841492 _logger . LogInformation ( $ "Checking out submodule { submodule . Path } target commit { submoduleCommit } ...") ;
1485- exitCode = await FaultTolerantGitAsync (
1486- new ProcessSpecification
1487- {
1488- FilePath = git ,
1489- Arguments =
1490- [
1491- "-c" ,
1492- "advice.detachedHead=false" ,
1493- "checkout" ,
1494- "--progress" ,
1495- "-f" ,
1496- submoduleCommit ,
1497- ] ,
1498- WorkingDirectory = submoduleContentPath ,
1499- EnvironmentVariables = gitEnvs ,
1500- } ,
1501- CaptureSpecification . Sanitized ,
1502- cancellationToken ) . ConfigureAwait ( false ) ;
1493+ using ( var fetchEnvVars = fetchEnvironmentVariablesFactory ( ) )
1494+ {
1495+ exitCode = await FaultTolerantGitAsync (
1496+ new ProcessSpecification
1497+ {
1498+ FilePath = git ,
1499+ Arguments =
1500+ [
1501+ "-c" ,
1502+ "advice.detachedHead=false" ,
1503+ "checkout" ,
1504+ "--progress" ,
1505+ "-f" ,
1506+ submoduleCommit ,
1507+ ] ,
1508+ WorkingDirectory = submoduleContentPath ,
1509+ EnvironmentVariables = fetchEnvVars . EnvironmentVariables ,
1510+ } ,
1511+ CaptureSpecification . Sanitized ,
1512+ cancellationToken ) . ConfigureAwait ( false ) ;
1513+ }
15031514 if ( exitCode != 0 )
15041515 {
15051516 throw new InvalidOperationException ( $ "'git checkout' for { submodule . Path } exited with non-zero exit code { exitCode } ") ;
@@ -1716,6 +1727,39 @@ private async Task<IReservation> GetSharedGitDependenciesPath(GitWorkspaceDescri
17161727 {
17171728 // Parse the repository URL.
17181729 var uri = new Uri ( repositoryUrl ) ;
1730+
1731+ // If this is a HTTPS URL, and we have a credential helper provider, use that
1732+ // to provide credentials.
1733+ if ( _gitCredentialHelperProvider != null &&
1734+ ( uri . Scheme == Uri . UriSchemeHttps || uri . Scheme == Uri . UriSchemeHttp ) )
1735+ {
1736+ var envVars = new Dictionary < string , string >
1737+ {
1738+ { "GIT_ASK_YESNO" , "false" } ,
1739+ { "GIT_CONFIG_COUNT" , "1" } ,
1740+ { "GIT_CONFIG_KEY_0" , "credential.helper" } ,
1741+ { "GIT_CONFIG_VALUE_0" , $ "{ _gitCredentialHelperProvider . FilePath } { _gitCredentialHelperProvider . Arguments . Select ( x => x . LogicalValue ) } " } ,
1742+ } ;
1743+ var uriComponents = uri . UserInfo . Split ( ':' , 2 ) ;
1744+ var uriHost = uri . Host . Replace ( "." , "_" , StringComparison . Ordinal ) ;
1745+ if ( uriComponents . Length == 1 )
1746+ {
1747+ envVars [ $ "REDPOINT_CREDENTIAL_DISCOVERY_USERNAME_{ uriHost } "] = uriComponents [ 0 ] ;
1748+ }
1749+ else if ( uriComponents . Length == 2 )
1750+ {
1751+ envVars [ $ "REDPOINT_CREDENTIAL_DISCOVERY_USERNAME_{ uriHost } "] = uriComponents [ 0 ] ;
1752+ envVars [ $ "REDPOINT_CREDENTIAL_DISCOVERY_PASSWORD_{ uriHost } "] = uriComponents [ 1 ] ;
1753+ }
1754+
1755+ var builder = new UriBuilder ( uri ) ;
1756+ builder . UserName = null ;
1757+ builder . Password = null ;
1758+ uri = builder . Uri ;
1759+ return ( uri , ( ) => new GitTemporaryEnvVarsForFetch ( envVars ) ) ;
1760+ }
1761+
1762+ // Fallback to direct credential lookup.
17191763 try
17201764 {
17211765 var uriCredential = _credentialDiscovery . GetGitCredential ( repositoryUrl ) ;
0 commit comments