Skip to content

Commit 3d3822c

Browse files
Add the credential environment configuration to the Git checkout command (#4965)
* Add the credential environment configuration to the Git checkout command The credential environment config property is added to the Git checkout command when either filter fetch options or the agent knob ADD_FORCE_CREDENTIALS_TO_GIT_CHECKOUT is provided * Add the argument to add when both conditions are met * Refactor GitCliManager and GitSourceProvider - Variables are now declared using the var keyword - The GitFetch method now accepts an IEnumerable<string> for the filters parameter instead of a List<string>
1 parent a340031 commit 3d3822c

File tree

3 files changed

+76
-51
lines changed

3 files changed

+76
-51
lines changed

src/Agent.Plugins/GitCliManager.cs

Lines changed: 4 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ public async Task<int> GitInit(AgentTaskPluginExecutionContext context, string r
195195
}
196196

197197
// git fetch --tags --prune --progress --no-recurse-submodules [--depth=15] origin [+refs/pull/*:refs/remote/pull/*]
198-
public async Task<int> GitFetch(AgentTaskPluginExecutionContext context, string repositoryPath, string remoteName, int fetchDepth, string fetchFilter, bool fetchTags, List<string> refSpec, string additionalCommandLine, CancellationToken cancellationToken)
198+
public async Task<int> GitFetch(AgentTaskPluginExecutionContext context, string repositoryPath, string remoteName, int fetchDepth, IEnumerable<string> filters, bool fetchTags, List<string> refSpec, string additionalCommandLine, CancellationToken cancellationToken)
199199
{
200200
context.Debug($"Fetch git repository at: {repositoryPath} remote: {remoteName}.");
201201
if (refSpec != null && refSpec.Count > 0)
@@ -233,50 +233,8 @@ public async Task<int> GitFetch(AgentTaskPluginExecutionContext context, string
233233
// add --unshallow to convert the shallow repository to a complete repository
234234
string depth = fetchDepth > 0 ? $"--depth={fetchDepth}" : (File.Exists(Path.Combine(repositoryPath, ".git", "shallow")) ? "--unshallow" : string.Empty);
235235

236-
// parse filter and only include valid options
237-
List<string> filters = new List<String>();
238-
239-
if (AgentKnobs.UseFetchFilterInCheckoutTask.GetValue(context).AsBoolean())
240-
{
241-
List<string> splitFilter = fetchFilter.Split('+').Where(filter => !String.IsNullOrWhiteSpace(filter)).ToList();
242-
243-
foreach (string filter in splitFilter)
244-
{
245-
List<string> parsedFilter = filter.Split(':')
246-
.Where(filter => !String.IsNullOrWhiteSpace(filter))
247-
.Select(filter => filter.Trim())
248-
.ToList();
249-
250-
if (parsedFilter.Count == 2)
251-
{
252-
switch (parsedFilter[0].ToLower())
253-
{
254-
case "tree":
255-
// currently only supporting treeless filter
256-
if (int.TryParse(parsedFilter[1], out int treeSize) && treeSize == 0)
257-
{
258-
filters.Add($"{parsedFilter[0]}:{treeSize}");
259-
}
260-
break;
261-
262-
case "blob":
263-
// currently only supporting blobless filter
264-
if (parsedFilter[1].Equals("none", StringComparison.OrdinalIgnoreCase))
265-
{
266-
filters.Add($"{parsedFilter[0]}:{parsedFilter[1]}");
267-
}
268-
break;
269-
270-
default:
271-
// either invalid or unsupported git object
272-
break;
273-
}
274-
}
275-
}
276-
}
277-
278236
//define options for fetch
279-
string options = $"{forceTag} {tags} --prune {pruneTags} {progress} --no-recurse-submodules {remoteName} {depth} {String.Join(" ", filters.Select(filter => "--filter=" + filter))} {string.Join(" ", refSpec)}";
237+
string options = $"{forceTag} {tags} --prune {pruneTags} {progress} --no-recurse-submodules {remoteName} {depth} {string.Join(" ", filters.Select(filter => "--filter=" + filter))} {string.Join(" ", refSpec)}";
280238
int retryCount = 0;
281239
int fetchExitCode = 0;
282240
while (retryCount < 3)
@@ -363,7 +321,7 @@ public async Task<int> GitLFSFetch(AgentTaskPluginExecutionContext context, stri
363321
}
364322

365323
// git checkout -f --progress <commitId/branch>
366-
public async Task<int> GitCheckout(AgentTaskPluginExecutionContext context, string repositoryPath, string committishOrBranchSpec, CancellationToken cancellationToken)
324+
public async Task<int> GitCheckout(AgentTaskPluginExecutionContext context, string repositoryPath, string committishOrBranchSpec, string additionalCommandLine, CancellationToken cancellationToken)
367325
{
368326
context.Debug($"Checkout {committishOrBranchSpec}.");
369327

@@ -378,7 +336,7 @@ public async Task<int> GitCheckout(AgentTaskPluginExecutionContext context, stri
378336
options = StringUtil.Format("--force {0}", committishOrBranchSpec);
379337
}
380338

381-
return await ExecuteGitCommandAsync(context, repositoryPath, "checkout", options, cancellationToken);
339+
return await ExecuteGitCommandAsync(context, repositoryPath, "checkout", options, additionalCommandLine, cancellationToken);
382340
}
383341

384342
// git clean -ffdx

src/Agent.Plugins/GitSourceProvider.cs

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -718,8 +718,10 @@ public async Task GetSourceAsync(
718718
await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.proxy", string.Empty);
719719
}
720720

721-
List<string> additionalFetchArgs = new List<string>();
722-
List<string> additionalLfsFetchArgs = new List<string>();
721+
var additionalFetchFilterOptions = ParseFetchFilterOptions(executionContext, fetchFilter);
722+
var additionalFetchArgs = new List<string>();
723+
var additionalLfsFetchArgs = new List<string>();
724+
var additionalCheckoutArgs = new List<string>();
723725

724726
// Force Git to HTTP/1.1. Otherwise IIS will reject large pushes to Azure Repos due to the large content-length header
725727
// This is caused by these header limits - https://docs.microsoft.com/en-us/iis/configuration/system.webserver/security/requestfiltering/requestlimits/headerlimits/
@@ -738,6 +740,11 @@ public async Task GetSourceAsync(
738740
string configKey = "http.extraheader";
739741
string args = ComposeGitArgs(executionContext, gitCommandManager, configKey, username, password, useBearerAuthType);
740742
additionalFetchArgs.Add(args);
743+
744+
if (additionalFetchFilterOptions.Any() && AgentKnobs.AddForceCredentialsToGitCheckout.GetValue(executionContext).AsBoolean())
745+
{
746+
additionalCheckoutArgs.Add(args);
747+
}
741748
}
742749
else
743750
{
@@ -899,7 +906,7 @@ public async Task GetSourceAsync(
899906
}
900907
}
901908

902-
int exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, fetchFilter, fetchTags, additionalFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
909+
int exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, additionalFetchFilterOptions, fetchTags, additionalFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
903910
if (exitCode_fetch != 0)
904911
{
905912
throw new InvalidOperationException($"Git fetch failed with exit code: {exitCode_fetch}");
@@ -911,7 +918,7 @@ public async Task GetSourceAsync(
911918
if (fetchByCommit && !string.IsNullOrEmpty(sourceVersion))
912919
{
913920
List<string> commitFetchSpecs = new List<string>() { $"+{sourceVersion}" };
914-
exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, fetchFilter, fetchTags, commitFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
921+
exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, additionalFetchFilterOptions, fetchTags, commitFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
915922
if (exitCode_fetch != 0)
916923
{
917924
throw new InvalidOperationException($"Git fetch failed with exit code: {exitCode_fetch}");
@@ -963,7 +970,7 @@ public async Task GetSourceAsync(
963970
}
964971

965972
// Finally, checkout the sourcesToBuild (if we didn't find a valid git object this will throw)
966-
int exitCode_checkout = await gitCommandManager.GitCheckout(executionContext, targetPath, sourcesToBuild, cancellationToken);
973+
int exitCode_checkout = await gitCommandManager.GitCheckout(executionContext, targetPath, sourcesToBuild, string.Join(" ", additionalCheckoutArgs), cancellationToken);
967974
if (exitCode_checkout != 0)
968975
{
969976
// local repository is shallow repository, checkout may fail due to lack of commits history.
@@ -1374,6 +1381,59 @@ private async Task<bool> IsRepositoryOriginUrlMatch(AgentTaskPluginExecutionCont
13741381
}
13751382
}
13761383

1384+
private IEnumerable<string> ParseFetchFilterOptions(AgentTaskPluginExecutionContext context, string fetchFilter)
1385+
{
1386+
if (!AgentKnobs.UseFetchFilterInCheckoutTask.GetValue(context).AsBoolean())
1387+
{
1388+
return Enumerable.Empty<string>();
1389+
}
1390+
1391+
if (string.IsNullOrEmpty(fetchFilter))
1392+
{
1393+
return Enumerable.Empty<string>();
1394+
}
1395+
1396+
// parse filter and only include valid options
1397+
var filters = new List<string>();
1398+
var splitFilter = fetchFilter.Split('+').Where(filter => !string.IsNullOrWhiteSpace(filter)).ToList();
1399+
1400+
foreach (string filter in splitFilter)
1401+
{
1402+
var parsedFilter = filter.Split(':')
1403+
.Where(filter => !string.IsNullOrWhiteSpace(filter))
1404+
.Select(filter => filter.Trim())
1405+
.ToList();
1406+
1407+
if (parsedFilter.Count == 2)
1408+
{
1409+
switch (parsedFilter[0].ToLower())
1410+
{
1411+
case "tree":
1412+
// currently only supporting treeless filter
1413+
if (int.TryParse(parsedFilter[1], out int treeSize) && treeSize == 0)
1414+
{
1415+
filters.Add($"{parsedFilter[0]}:{treeSize}");
1416+
}
1417+
break;
1418+
1419+
case "blob":
1420+
// currently only supporting blobless filter
1421+
if (parsedFilter[1].Equals("none", StringComparison.OrdinalIgnoreCase))
1422+
{
1423+
filters.Add($"{parsedFilter[0]}:{parsedFilter[1]}");
1424+
}
1425+
break;
1426+
1427+
default:
1428+
// either invalid or unsupported git object
1429+
break;
1430+
}
1431+
}
1432+
}
1433+
1434+
return filters;
1435+
}
1436+
13771437
private async Task RunGitStatusIfSystemDebug(AgentTaskPluginExecutionContext executionContext, GitCliManager gitCommandManager, string targetPath)
13781438
{
13791439
if (executionContext.IsSystemDebugTrue())

src/Agent.Sdk/Knob/AgentKnobs.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,13 @@ public class AgentKnobs
759759
new PipelineFeatureSource("UsePSScriptWrapper"),
760760
new BuiltInDefaultKnobSource("false"));
761761

762+
public static readonly Knob AddForceCredentialsToGitCheckout = new Knob(
763+
nameof(AddForceCredentialsToGitCheckout),
764+
"If true, the credentials will be forcibly added to the Git checkout command.",
765+
new RuntimeKnobSource("ADD_FORCE_CREDENTIALS_TO_GIT_CHECKOUT"),
766+
new PipelineFeatureSource(nameof(AddForceCredentialsToGitCheckout)),
767+
new BuiltInDefaultKnobSource("false"));
768+
762769
public static readonly Knob InstallLegacyTfExe = new Knob(
763770
nameof(InstallLegacyTfExe),
764771
"If true, the agent will install the legacy versions of TF, vstsom and vstshost",

0 commit comments

Comments
 (0)