Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added option to use a checkout path of any repository as the default working directory (System.DefaultWorkingDirectory) #3479

Merged
Merged
Show file tree
Hide file tree
Changes from 9 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
1 change: 1 addition & 0 deletions src/Agent.Sdk/TaskPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class WellKnownJobSettings
{
public static readonly string HasMultipleCheckouts = "HasMultipleCheckouts";
public static readonly string FirstRepositoryCheckedOut = "FirstRepositoryCheckedOut";
public static readonly string DefaultWorkingDirectoryRepository = "DefaultWorkingDirectoryRepository";
public static readonly string WorkspaceIdentifier = "WorkspaceIdentifier";
public static readonly string CommandCorrelationId = "CommandCorrelationId";
}
Expand Down
30 changes: 29 additions & 1 deletion src/Agent.Sdk/Util/RepositoryUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public static class RepositoryUtil
{
public static readonly string IsPrimaryRepository = "system.isprimaryrepository";
public static readonly string IsTriggeringRepository = "system.istriggeringrepository";
public static readonly string IsDefaultWorkingDirectoryRepository = "system.isdefaultworkingdirectoryrepository";
public static readonly string DefaultPrimaryRepositoryName = "self";
public static readonly string GitStandardBranchPrefix = "refs/heads/";

Expand Down Expand Up @@ -59,6 +60,17 @@ public static RepositoryResource GetPrimaryRepository(IList<RepositoryResource>
return GetWellKnownRepository(repositories, RepositoryUtil.IsPrimaryRepository);
}

/// <summary>
/// This method returns the repo from the list that is considered the default working directory repository.
/// If the list only contains 1 repo, then that is the default working directory repository.
/// If the list contains more than one, then we look for the repository marked as the default working directory repo.
/// It returns null, if no default working directory repository can be found.
/// </summary>
public static RepositoryResource GetDefaultWorkingDirectoryRepository(IList<RepositoryResource> repositories)
{
return GetWellKnownRepository(repositories, RepositoryUtil.IsDefaultWorkingDirectoryRepository, true);
}

/// <summary>
/// This method returns the repo from the list that is considered the triggering repository.
/// If the list only contains 1 repo, then that is the triggering repository.
Expand All @@ -71,6 +83,11 @@ public static RepositoryResource GetTriggeringRepository(IList<RepositoryResourc
}

private static RepositoryResource GetWellKnownRepository(IList<RepositoryResource> repositories, string repositoryFlagName)
{
return GetWellKnownRepository(repositories, repositoryFlagName, false);
}

private static RepositoryResource GetWellKnownRepository(IList<RepositoryResource> repositories, string repositoryFlagName, bool returnNullIfNoneFound)
{
if (repositories == null || !repositories.Any())
{
Expand All @@ -84,7 +101,7 @@ private static RepositoryResource GetWellKnownRepository(IList<RepositoryResourc

// Look for any repository marked with the expected flag name
var repo = repositories.Where(r => r.Properties.Get<bool>(repositoryFlagName, false)).FirstOrDefault();
if (repo != null)
if (returnNullIfNoneFound || repo != null)
{
return repo;
}
Expand All @@ -95,6 +112,17 @@ private static RepositoryResource GetWellKnownRepository(IList<RepositoryResourc
}
}

public static bool IsWellKnownRepository(RepositoryResource repository, string repositoryFlagName)
{
if (repository == null)
{
return false;
}

// Look for flag in repository
return repository.Properties.Get<bool>(repositoryFlagName, false);
}

/// <summary>
/// This method returns the repository from the list that has a 'Path' that the localPath is parented to.
/// If the localPath is not part of any of the repo paths, null is returned.
Expand Down
43 changes: 40 additions & 3 deletions src/Agent.Worker/Build/BuildJobExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ public override string GetRootedPath(IExecutionContext context, string path)
if (RepositoryUtil.HasMultipleCheckouts(context.JobSettings))
{
// If there are multiple checkouts, set the default directory to the sources root folder (_work/1/s)
defaultPathRoot = context.Variables.Get(Constants.Variables.Build.SourcesDirectory);
Trace.Info($"The Default Path Root of Build JobExtension is build.sourcesDirectory: {defaultPathRoot}");
defaultPathRoot = context.Variables.Get(Constants.Variables.System.DefaultWorkingDirectory);
Trace.Info($"The Default Path Root of Build JobExtension is system.defaultworkingdirectory: {defaultPathRoot}");
}
else if (repoInfo.PrimaryRepository != null)
{
Expand Down Expand Up @@ -174,11 +174,14 @@ public override void InitializeJobExtension(IExecutionContext executionContext,
// Get default value for RepoLocalPath variable
string selfRepoPath = GetDefaultRepoLocalPathValue(executionContext, steps, trackingConfig, repoInfo);

// Get the default value for working directory
string defaultWorkingDirectoryPath = GetDefaultWorkingDirectoryRepoLocalPathValue(executionContext, steps, trackingConfig);

// Set the directory variables.
executionContext.Output(StringUtil.Loc("SetBuildVars"));
executionContext.SetVariable(Constants.Variables.Agent.BuildDirectory, pipelineWorkspaceDirectory, isFilePath: true);
executionContext.SetVariable(Constants.Variables.System.ArtifactsDirectory, Path.Combine(_workDirectory, trackingConfig.ArtifactsDirectory), isFilePath: true);
executionContext.SetVariable(Constants.Variables.System.DefaultWorkingDirectory, Path.Combine(_workDirectory, trackingConfig.SourcesDirectory), isFilePath: true);
executionContext.SetVariable(Constants.Variables.System.DefaultWorkingDirectory, Path.Combine(_workDirectory, defaultWorkingDirectoryPath), isFilePath: true);
executionContext.SetVariable(Constants.Variables.Common.TestResultsDirectory, Path.Combine(_workDirectory, trackingConfig.TestResultsDirectory), isFilePath: true);
executionContext.SetVariable(Constants.Variables.Build.BinariesDirectory, Path.Combine(_workDirectory, trackingConfig.BuildDirectory, Constants.Build.Path.BinariesDirectory), isFilePath: true);
executionContext.SetVariable(Constants.Variables.Build.SourcesDirectory, Path.Combine(_workDirectory, trackingConfig.SourcesDirectory), isFilePath: true);
Expand Down Expand Up @@ -386,6 +389,32 @@ private string GetDefaultRepoLocalPathValue(IExecutionContext executionContext,
return selfRepoPath;
}

private string GetDefaultWorkingDirectoryRepoLocalPathValue(IExecutionContext executionContext, IList<Pipelines.JobStep> steps, TrackingConfig trackingConfig)
{
string defaultWorkingDirectoryRepoPath = null;

if (RepositoryUtil.HasMultipleCheckouts(executionContext.JobSettings))
{
// get checkout task for default working director repo
var defaultWorkingDirectoryCheckoutTask = GetDefaultWorkingDirectoryCheckoutTask(steps);

// Check if a task was found
if (defaultWorkingDirectoryCheckoutTask != null && defaultWorkingDirectoryCheckoutTask.Inputs.TryGetValue(Pipelines.PipelineConstants.CheckoutTaskInputs.Repository, out string defaultWorkingDirectoryRepoAlias))
{
defaultWorkingDirectoryRepoPath = trackingConfig.RepositoryTrackingInfo
.Where(repo => string.Equals(repo.Identifier, defaultWorkingDirectoryRepoAlias, StringComparison.OrdinalIgnoreCase))
.Select(props => props.SourcesDirectory).FirstOrDefault();
}
}
// For single checkout jobs and multicheckout jobs with default paths set defaultWorkingDirectoryRepoPath to the default sources directory
if (defaultWorkingDirectoryRepoPath == null)
{
defaultWorkingDirectoryRepoPath = trackingConfig.SourcesDirectory;
}

return defaultWorkingDirectoryRepoPath;
}

private bool IsCheckoutToCustomPath(TrackingConfig trackingConfig, RepositoryInfo repoInfo, TaskStep selfCheckoutTask)
{
string path;
Expand All @@ -407,6 +436,14 @@ private TaskStep GetSelfCheckoutTask(IList<JobStep> steps)
&& RepositoryUtil.IsPrimaryRepositoryName(repositoryAlias)).FirstOrDefault();
}

public static TaskStep GetDefaultWorkingDirectoryCheckoutTask(IList<JobStep> steps)
{
return steps.Select(x => x as TaskStep)
.Where(task => task.IsCheckoutTask()
&& task.Inputs.TryGetValue(Pipelines.PipelineConstants.CheckoutTaskInputs.WorkspaceRepo, out string isDefaultWorkingDirectoryCheckout)
&& StringUtil.ConvertToBoolean(isDefaultWorkingDirectoryCheckout)).FirstOrDefault();
}

private class RepositoryInfo
{
public Pipelines.RepositoryResource PrimaryRepository { set; get; }
Expand Down
13 changes: 13 additions & 0 deletions src/Agent.Worker/ExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,19 @@ public void InitializeJob(Pipelines.AgentJobRequestMessage message, Cancellation
repo.Properties.Set<bool>(RepositoryUtil.IsPrimaryRepository, true);
}
}

var defaultWorkingDirectoryCheckout = Build.BuildJobExtension.GetDefaultWorkingDirectoryCheckoutTask(message.Steps);
if (Repositories != null && defaultWorkingDirectoryCheckout != null && defaultWorkingDirectoryCheckout.Inputs.TryGetValue(Pipelines.PipelineConstants.CheckoutTaskInputs.Repository, out string defaultWorkingDirectoryRepoAlias))
{
var defaultWorkingDirectoryRepo = Repositories.Find(r => String.Equals(r.Alias, defaultWorkingDirectoryRepoAlias, StringComparison.OrdinalIgnoreCase));
if (defaultWorkingDirectoryRepo != null)
{
defaultWorkingDirectoryRepo.Properties.Set<bool>(RepositoryUtil.IsDefaultWorkingDirectoryRepository, true);
JobSettings[WellKnownJobSettings.DefaultWorkingDirectoryRepository] = defaultWorkingDirectoryRepoAlias;

Trace.Info($"Will set the path of the following repository to be the System.DefaultWorkingDirectory: {defaultWorkingDirectoryRepoAlias}");
}
}
}

// Variables (constructor performs initial recursive expansion)
Expand Down
11 changes: 11 additions & 0 deletions src/Agent.Worker/PluginInternalCommandExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public void Execute(IExecutionContext context, Command command)

bool isSelfRepo = RepositoryUtil.IsPrimaryRepositoryName(repository.Alias);
bool hasMultipleCheckouts = RepositoryUtil.HasMultipleCheckouts(context.JobSettings);
bool isDefaultWorkingDirectoryRepo = RepositoryUtil.IsWellKnownRepository(repository, RepositoryUtil.IsDefaultWorkingDirectoryRepository);

var directoryManager = context.GetHostContext().GetService<IBuildDirectoryManager>();
string _workDirectory = context.GetHostContext().GetDirectory(WellKnownDirectory.Work);
Expand Down Expand Up @@ -92,6 +93,16 @@ public void Execute(IExecutionContext context, Command command)
context.SetVariable(Constants.Variables.System.DefaultWorkingDirectory, repositoryPath, isFilePath: true);
}
}

// Set the default working directory to the new location of this repo if this repo was marked as the one being the default working directory
if(isDefaultWorkingDirectoryRepo)
{
string buildDirectory = context.Variables.Get(Constants.Variables.Pipeline.Workspace);
string repoRelativePath = directoryManager.GetRelativeRepositoryPath(buildDirectory, repositoryPath);
string repoLocation = Path.Combine(_workDirectory, repoRelativePath);

context.SetVariable(Constants.Variables.System.DefaultWorkingDirectory, repoLocation, isFilePath: true);
}
}

repository.Properties.Set("__AZP_READY", bool.TrueString);
Expand Down
89 changes: 89 additions & 0 deletions src/Test/L0/Worker/Build/BuildJobExtensionL0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ public void CheckSingleRepoWithoutPathInput()
var repoLocalPath = _ec.Object.Variables.Get(Constants.Variables.Build.RepoLocalPath);
Assert.NotNull(repoLocalPath);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), repoLocalPath);

var sourcesDir = _ec.Object.Variables.Get(Constants.Variables.Build.SourcesDirectory);
Assert.NotNull(sourcesDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), sourcesDir);

var defaultWorkingDir = _ec.Object.Variables.Get(Constants.Variables.System.DefaultWorkingDirectory);
Assert.NotNull(defaultWorkingDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), defaultWorkingDir);
}
}

Expand All @@ -61,6 +69,14 @@ public void CheckSingleRepoWithCustomPaths()
var repoLocalPath = _ec.Object.Variables.Get(Constants.Variables.Build.RepoLocalPath);
Assert.NotNull(repoLocalPath);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), repoLocalPath);

var sourcesDir = _ec.Object.Variables.Get(Constants.Variables.Build.SourcesDirectory);
Assert.NotNull(sourcesDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), sourcesDir);

var defaultWorkingDir = _ec.Object.Variables.Get(Constants.Variables.System.DefaultWorkingDirectory);
Assert.NotNull(defaultWorkingDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), defaultWorkingDir);
}
}

Expand All @@ -75,6 +91,36 @@ public void CheckMultiRepoWithoutPathInput()
var repoLocalPath = _ec.Object.Variables.Get(Constants.Variables.Build.RepoLocalPath);
Assert.NotNull(repoLocalPath);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), repoLocalPath);

var sourcesDir = _ec.Object.Variables.Get(Constants.Variables.Build.SourcesDirectory);
Assert.NotNull(sourcesDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), sourcesDir);

var defaultWorkingDir = _ec.Object.Variables.Get(Constants.Variables.System.DefaultWorkingDirectory);
Assert.NotNull(defaultWorkingDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), defaultWorkingDir);
}
}

[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void CheckMultiRepoWithoutPathInputAndWithDefaultWorkingRepo()
{
using (TestHostContext tc = Setup(createWorkDirectory: false, checkOutConfig: CheckoutConfigType.MultiCheckoutDefaultPath, defaultWorkingDirRepo: true))
{
buildJobExtension.InitializeJobExtension(_ec.Object, steps, _workspaceOptions);
var repoLocalPath = _ec.Object.Variables.Get(Constants.Variables.Build.RepoLocalPath);
Assert.NotNull(repoLocalPath);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), repoLocalPath);

var sourcesDir = _ec.Object.Variables.Get(Constants.Variables.Build.SourcesDirectory);
Assert.NotNull(sourcesDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), sourcesDir);

var defaultWorkingDir = _ec.Object.Variables.Get(Constants.Variables.System.DefaultWorkingDirectory);
Assert.NotNull(defaultWorkingDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s{directorySeparator}App"), defaultWorkingDir);
}
}

Expand All @@ -89,6 +135,36 @@ public void CheckMultiRepoWithPathInputToCustomPath()
var repoLocalPath = _ec.Object.Variables.Get(Constants.Variables.Build.RepoLocalPath);
Assert.NotNull(repoLocalPath);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s{directorySeparator}App"), repoLocalPath);

var sourcesDir = _ec.Object.Variables.Get(Constants.Variables.Build.SourcesDirectory);
Assert.NotNull(sourcesDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), sourcesDir);

var defaultWorkingDir = _ec.Object.Variables.Get(Constants.Variables.System.DefaultWorkingDirectory);
Assert.NotNull(defaultWorkingDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), defaultWorkingDir);
}
}

[Fact]
[Trait("Level", "L0")]
[Trait("Category", "Worker")]
public void CheckMultiRepoWithPathInputToCustomPathAndWithDefaultWorkingRepo()
{
using (TestHostContext tc = Setup(createWorkDirectory: false, checkOutConfig: CheckoutConfigType.MultiCheckoutCustomPath, pathToSelfRepo: "s/CustomApplicationFolder", defaultWorkingDirRepo: true))
{
buildJobExtension.InitializeJobExtension(_ec.Object, steps, _workspaceOptions);
var repoLocalPath = _ec.Object.Variables.Get(Constants.Variables.Build.RepoLocalPath);
Assert.NotNull(repoLocalPath);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s{directorySeparator}App"), repoLocalPath);

var sourcesDir = _ec.Object.Variables.Get(Constants.Variables.Build.SourcesDirectory);
Assert.NotNull(sourcesDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), sourcesDir);

var defaultWorkingDir = _ec.Object.Variables.Get(Constants.Variables.System.DefaultWorkingDirectory);
Assert.NotNull(defaultWorkingDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s{directorySeparator}App"), defaultWorkingDir);
}
}

Expand All @@ -103,11 +179,20 @@ public void CheckMultiRepoWithPathInputToDefaultPath()
var repoLocalPath = _ec.Object.Variables.Get(Constants.Variables.Build.RepoLocalPath);
Assert.NotNull(repoLocalPath);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), repoLocalPath);

var sourcesDir = _ec.Object.Variables.Get(Constants.Variables.Build.SourcesDirectory);
Assert.NotNull(sourcesDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), sourcesDir);

var defaultWorkingDir = _ec.Object.Variables.Get(Constants.Variables.System.DefaultWorkingDirectory);
Assert.NotNull(defaultWorkingDir);
Assert.Equal(Path.Combine(stubWorkFolder, $"1{directorySeparator}s"), defaultWorkingDir);
}
}

private TestHostContext Setup([CallerMemberName] string name = "",
bool createWorkDirectory = true,
bool defaultWorkingDirRepo = false,
CheckoutConfigType checkOutConfig = CheckoutConfigType.SingleCheckoutDefaultPath,
string pathToSelfRepo = "")
{
Expand Down Expand Up @@ -141,6 +226,10 @@ private TestHostContext Setup([CallerMemberName] string name = "",
}
};
selfCheckoutTask.Inputs.Add("repository", "self");
if (defaultWorkingDirRepo)
{
selfCheckoutTask.Inputs.Add("workspaceRepo", "true");
}
if (isCustomPathScenario)
{
selfCheckoutTask.Inputs.Add("path", pathToSelfRepo);
Expand Down
Loading
Loading