Skip to content

Commit

Permalink
Add the credential environment configuration to the Git checkout comm…
Browse files Browse the repository at this point in the history
…and (#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>
  • Loading branch information
ivanduplenskikh committed Sep 17, 2024
1 parent a340031 commit 3d3822c
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 51 deletions.
50 changes: 4 additions & 46 deletions src/Agent.Plugins/GitCliManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ public async Task<int> GitInit(AgentTaskPluginExecutionContext context, string r
}

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

// parse filter and only include valid options
List<string> filters = new List<String>();

if (AgentKnobs.UseFetchFilterInCheckoutTask.GetValue(context).AsBoolean())
{
List<string> splitFilter = fetchFilter.Split('+').Where(filter => !String.IsNullOrWhiteSpace(filter)).ToList();

foreach (string filter in splitFilter)
{
List<string> parsedFilter = filter.Split(':')
.Where(filter => !String.IsNullOrWhiteSpace(filter))
.Select(filter => filter.Trim())
.ToList();

if (parsedFilter.Count == 2)
{
switch (parsedFilter[0].ToLower())
{
case "tree":
// currently only supporting treeless filter
if (int.TryParse(parsedFilter[1], out int treeSize) && treeSize == 0)
{
filters.Add($"{parsedFilter[0]}:{treeSize}");
}
break;

case "blob":
// currently only supporting blobless filter
if (parsedFilter[1].Equals("none", StringComparison.OrdinalIgnoreCase))
{
filters.Add($"{parsedFilter[0]}:{parsedFilter[1]}");
}
break;

default:
// either invalid or unsupported git object
break;
}
}
}
}

//define options for fetch
string options = $"{forceTag} {tags} --prune {pruneTags} {progress} --no-recurse-submodules {remoteName} {depth} {String.Join(" ", filters.Select(filter => "--filter=" + filter))} {string.Join(" ", refSpec)}";
string options = $"{forceTag} {tags} --prune {pruneTags} {progress} --no-recurse-submodules {remoteName} {depth} {string.Join(" ", filters.Select(filter => "--filter=" + filter))} {string.Join(" ", refSpec)}";
int retryCount = 0;
int fetchExitCode = 0;
while (retryCount < 3)
Expand Down Expand Up @@ -363,7 +321,7 @@ public async Task<int> GitLFSFetch(AgentTaskPluginExecutionContext context, stri
}

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

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

return await ExecuteGitCommandAsync(context, repositoryPath, "checkout", options, cancellationToken);
return await ExecuteGitCommandAsync(context, repositoryPath, "checkout", options, additionalCommandLine, cancellationToken);
}

// git clean -ffdx
Expand Down
70 changes: 65 additions & 5 deletions src/Agent.Plugins/GitSourceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -718,8 +718,10 @@ public async Task GetSourceAsync(
await RemoveGitConfig(executionContext, gitCommandManager, targetPath, $"http.proxy", string.Empty);
}

List<string> additionalFetchArgs = new List<string>();
List<string> additionalLfsFetchArgs = new List<string>();
var additionalFetchFilterOptions = ParseFetchFilterOptions(executionContext, fetchFilter);
var additionalFetchArgs = new List<string>();
var additionalLfsFetchArgs = new List<string>();
var additionalCheckoutArgs = new List<string>();

// Force Git to HTTP/1.1. Otherwise IIS will reject large pushes to Azure Repos due to the large content-length header
// This is caused by these header limits - https://docs.microsoft.com/en-us/iis/configuration/system.webserver/security/requestfiltering/requestlimits/headerlimits/
Expand All @@ -738,6 +740,11 @@ public async Task GetSourceAsync(
string configKey = "http.extraheader";
string args = ComposeGitArgs(executionContext, gitCommandManager, configKey, username, password, useBearerAuthType);
additionalFetchArgs.Add(args);

if (additionalFetchFilterOptions.Any() && AgentKnobs.AddForceCredentialsToGitCheckout.GetValue(executionContext).AsBoolean())
{
additionalCheckoutArgs.Add(args);
}
}
else
{
Expand Down Expand Up @@ -899,7 +906,7 @@ public async Task GetSourceAsync(
}
}

int exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, fetchFilter, fetchTags, additionalFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
int exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, additionalFetchFilterOptions, fetchTags, additionalFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
if (exitCode_fetch != 0)
{
throw new InvalidOperationException($"Git fetch failed with exit code: {exitCode_fetch}");
Expand All @@ -911,7 +918,7 @@ public async Task GetSourceAsync(
if (fetchByCommit && !string.IsNullOrEmpty(sourceVersion))
{
List<string> commitFetchSpecs = new List<string>() { $"+{sourceVersion}" };
exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, fetchFilter, fetchTags, commitFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
exitCode_fetch = await gitCommandManager.GitFetch(executionContext, targetPath, "origin", fetchDepth, additionalFetchFilterOptions, fetchTags, commitFetchSpecs, string.Join(" ", additionalFetchArgs), cancellationToken);
if (exitCode_fetch != 0)
{
throw new InvalidOperationException($"Git fetch failed with exit code: {exitCode_fetch}");
Expand Down Expand Up @@ -963,7 +970,7 @@ public async Task GetSourceAsync(
}

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

private IEnumerable<string> ParseFetchFilterOptions(AgentTaskPluginExecutionContext context, string fetchFilter)
{
if (!AgentKnobs.UseFetchFilterInCheckoutTask.GetValue(context).AsBoolean())
{
return Enumerable.Empty<string>();
}

if (string.IsNullOrEmpty(fetchFilter))
{
return Enumerable.Empty<string>();
}

// parse filter and only include valid options
var filters = new List<string>();
var splitFilter = fetchFilter.Split('+').Where(filter => !string.IsNullOrWhiteSpace(filter)).ToList();

foreach (string filter in splitFilter)
{
var parsedFilter = filter.Split(':')
.Where(filter => !string.IsNullOrWhiteSpace(filter))
.Select(filter => filter.Trim())
.ToList();

if (parsedFilter.Count == 2)
{
switch (parsedFilter[0].ToLower())
{
case "tree":
// currently only supporting treeless filter
if (int.TryParse(parsedFilter[1], out int treeSize) && treeSize == 0)
{
filters.Add($"{parsedFilter[0]}:{treeSize}");
}
break;

case "blob":
// currently only supporting blobless filter
if (parsedFilter[1].Equals("none", StringComparison.OrdinalIgnoreCase))
{
filters.Add($"{parsedFilter[0]}:{parsedFilter[1]}");
}
break;

default:
// either invalid or unsupported git object
break;
}
}
}

return filters;
}

private async Task RunGitStatusIfSystemDebug(AgentTaskPluginExecutionContext executionContext, GitCliManager gitCommandManager, string targetPath)
{
if (executionContext.IsSystemDebugTrue())
Expand Down
7 changes: 7 additions & 0 deletions src/Agent.Sdk/Knob/AgentKnobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,13 @@ public class AgentKnobs
new PipelineFeatureSource("UsePSScriptWrapper"),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob AddForceCredentialsToGitCheckout = new Knob(
nameof(AddForceCredentialsToGitCheckout),
"If true, the credentials will be forcibly added to the Git checkout command.",
new RuntimeKnobSource("ADD_FORCE_CREDENTIALS_TO_GIT_CHECKOUT"),
new PipelineFeatureSource(nameof(AddForceCredentialsToGitCheckout)),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob InstallLegacyTfExe = new Knob(
nameof(InstallLegacyTfExe),
"If true, the agent will install the legacy versions of TF, vstsom and vstshost",
Expand Down

0 comments on commit 3d3822c

Please sign in to comment.