private string GetDefaultRepositoryPath(
            IExecutionContext executionContext,
            RepositoryResource repository,
            TrackingConfig newConfig
            )
        {
            string repoPath      = String.Empty;
            string workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);

            if (RepositoryUtil.HasMultipleCheckouts(executionContext.JobSettings))
            {
                // If we have multiple checkouts they should all be rooted to the sources directory (_work/1/s/repo1)
                var repoSourceDirectory = newConfig?.RepositoryTrackingInfo.Where(item => string.Equals(item.Identifier, repository.Alias, StringComparison.OrdinalIgnoreCase)).Select(item => item.SourcesDirectory).FirstOrDefault();
                if (repoSourceDirectory != null)
                {
                    repoPath = Path.Combine(workDirectory, repoSourceDirectory);
                }
                else
                {
                    repoPath = Path.Combine(workDirectory, newConfig.SourcesDirectory, RepositoryUtil.GetCloneDirectory(repository));
                }
            }
            else
            {
                // For single checkouts, the repository is rooted to the sources folder (_work/1/s)
                repoPath = Path.Combine(workDirectory, newConfig.SourcesDirectory);
            }

            return(repoPath);
        }
        private string GetDefaultRepoLocalPathValue(IExecutionContext executionContext, IList<Pipelines.JobStep> steps, TrackingConfig trackingConfig, RepositoryInfo repoInfo)
        {
            string selfRepoPath = null;
            // For saving backward compatibility with the behavior of the Build.RepoLocalPath that was before this PR https://github.com/microsoft/azure-pipelines-agent/pull/3237
            // We need to change how we set the default value of this variable
            // We need to allow the setting of paths from RepositoryTrackingInfo for checkout tasks where path input was provided by the user
            // and this input is not point to the default location for this repository
            // This is the only case where the value of Build.RepoLocalPath variable is not pointing to the root of sources directory /s.
            // The new logic is not affecting single checkout jobs and jobs with multiple checkouts and default paths for Self repository
            if (RepositoryUtil.HasMultipleCheckouts(executionContext.JobSettings))
            {
                // get checkout task for self repo
                var selfCheckoutTask = GetSelfCheckoutTask(steps);

                // Check if the task has path input with custom path, if so we need to set as a value of selfRepoPath the value of SourcesDirectory from RepositoryTrackingInfo
                if (IsCheckoutToCustomPath(trackingConfig, repoInfo, selfCheckoutTask))
                {
                    selfRepoPath = trackingConfig.RepositoryTrackingInfo
                        .Where(repo => RepositoryUtil.IsPrimaryRepositoryName(repo.Identifier))
                        .Select(props => props.SourcesDirectory).FirstOrDefault(); 
                }
            }
            // For single checkout jobs and multicheckout jobs with default paths set selfRepoPath to the default sources directory
            if (selfRepoPath == null)
            {
                selfRepoPath = trackingConfig.SourcesDirectory;
            }

            return selfRepoPath;
        }
Пример #3
0
        public void Execute(IExecutionContext context, Command command)
        {
            var eventProperties = command.Properties;
            var data            = command.Data;

            String alias;

            if (!eventProperties.TryGetValue(PluginInternalUpdateRepositoryEventProperties.Alias, out alias) || String.IsNullOrEmpty(alias))
            {
                throw new Exception(StringUtil.Loc("MissingRepositoryAlias"));
            }

            var repository = context.Repositories.FirstOrDefault(x => string.Equals(x.Alias, alias, StringComparison.OrdinalIgnoreCase));

            if (repository == null)
            {
                throw new Exception(StringUtil.Loc("RepositoryNotExist"));
            }

            if (string.IsNullOrEmpty(data))
            {
                throw new Exception(StringUtil.Loc("MissingRepositoryPath"));
            }

            var currentPath = repository.Properties.Get <string>(RepositoryPropertyNames.Path);

            if (!string.Equals(data.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), currentPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), IOUtil.FilePathStringComparison))
            {
                string repositoryPath = data.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
                repository.Properties.Set <string>(RepositoryPropertyNames.Path, repositoryPath);

                bool isSelfRepo           = RepositoryUtil.IsPrimaryRepositoryName(repository.Alias);
                bool hasMultipleCheckouts = RepositoryUtil.HasMultipleCheckouts(context.JobSettings);

                if (isSelfRepo || !hasMultipleCheckouts)
                {
                    var    directoryManager = context.GetHostContext().GetService <IBuildDirectoryManager>();
                    string _workDirectory   = context.GetHostContext().GetDirectory(WellKnownDirectory.Work);

                    var trackingConfig = directoryManager.UpdateDirectory(context, repository);
                    if (hasMultipleCheckouts)
                    {
                        // In Multi-checkout, we don't want to reset sources dir or default working dir.
                        // So, we will just reset the repo local path
                        string buildDirectory   = context.Variables.Get(Constants.Variables.Pipeline.Workspace);
                        string repoRelativePath = directoryManager.GetRelativeRepositoryPath(buildDirectory, repositoryPath);
                        context.SetVariable(Constants.Variables.Build.RepoLocalPath, Path.Combine(_workDirectory, repoRelativePath), isFilePath: true);
                    }
                    else
                    {
                        // If we only have a single repository, then update all the paths to point to it.
                        context.SetVariable(Constants.Variables.Build.SourcesDirectory, repositoryPath, isFilePath: true);
                        context.SetVariable(Constants.Variables.Build.RepoLocalPath, repositoryPath, isFilePath: true);
                        context.SetVariable(Constants.Variables.System.DefaultWorkingDirectory, repositoryPath, isFilePath: true);
                    }
                }
            }

            repository.Properties.Set("__AZP_READY", bool.TrueString);
        }
Пример #4
0
        public void HasMultipleCheckouts_should_not_throw()
        {
            using (TestHostContext hc = new TestHostContext(this))
            {
                Tracing trace = hc.GetTrace();

                Assert.Equal(false, RepositoryUtil.HasMultipleCheckouts(null));
                var dict = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);
                Assert.Equal(false, RepositoryUtil.HasMultipleCheckouts(dict));
                dict.Add("x", "y");
                Assert.Equal(false, RepositoryUtil.HasMultipleCheckouts(dict));
                dict.Add(WellKnownJobSettings.HasMultipleCheckouts, "burger");
                Assert.Equal(false, RepositoryUtil.HasMultipleCheckouts(dict));
            }
        }
Пример #5
0
        public void HasMultipleCheckouts_should_return_true_when_set_correctly()
        {
            using (TestHostContext hc = new TestHostContext(this))
            {
                Tracing trace = hc.GetTrace();

                Assert.Equal(false, RepositoryUtil.HasMultipleCheckouts(null));
                var dict = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);
                dict[WellKnownJobSettings.HasMultipleCheckouts] = "true";
                Assert.Equal(true, RepositoryUtil.HasMultipleCheckouts(dict));
                dict[WellKnownJobSettings.HasMultipleCheckouts] = "TRUE";
                Assert.Equal(true, RepositoryUtil.HasMultipleCheckouts(dict));
                dict[WellKnownJobSettings.HasMultipleCheckouts] = "True";
                Assert.Equal(true, RepositoryUtil.HasMultipleCheckouts(dict));
            }
        }
Пример #6
0
 private string GetDefaultRepositoryPath(
     IExecutionContext executionContext,
     RepositoryResource repository,
     string defaultSourcesDirectory)
 {
     if (RepositoryUtil.HasMultipleCheckouts(executionContext.JobSettings))
     {
         // If we have multiple checkouts they should all be rooted to the sources directory (_work/1/s/repo1)
         return(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), defaultSourcesDirectory, RepositoryUtil.GetCloneDirectory(repository)));
     }
     else
     {
         // For single checkouts, the repository is rooted to the sources folder (_work/1/s)
         return(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), defaultSourcesDirectory));
     }
 }
        private void ProcessPluginInternalUpdateRepositoryPathCommand(IExecutionContext context, Dictionary <string, string> eventProperties, string data)
        {
            String alias;

            if (!eventProperties.TryGetValue(PluginInternalUpdateRepositoryEventProperties.Alias, out alias) || String.IsNullOrEmpty(alias))
            {
                throw new Exception(StringUtil.Loc("MissingRepositoryAlias"));
            }

            var repository = context.Repositories.FirstOrDefault(x => string.Equals(x.Alias, alias, StringComparison.OrdinalIgnoreCase));

            if (repository == null)
            {
                throw new Exception(StringUtil.Loc("RepositoryNotExist"));
            }

            if (string.IsNullOrEmpty(data))
            {
                throw new Exception(StringUtil.Loc("MissingRepositoryPath"));
            }

            var currentPath = repository.Properties.Get <string>(RepositoryPropertyNames.Path);

            if (!string.Equals(data.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), currentPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), IOUtil.FilePathStringComparison))
            {
                repository.Properties.Set <string>(RepositoryPropertyNames.Path, data.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar));

                var directoryManager = HostContext.GetService <IBuildDirectoryManager>();
                var trackingConfig   = directoryManager.UpdateDirectory(context, repository);

                // Set the directory variables.
                string _workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);
                context.SetVariable(Constants.Variables.Build.SourcesDirectory, Path.Combine(_workDirectory, trackingConfig.SourcesDirectory), isFilePath: true);
                context.SetVariable(Constants.Variables.Build.RepoLocalPath, Path.Combine(_workDirectory, trackingConfig.SourcesDirectory), isFilePath: true);

                // Only set the working directory here if we are NOT in Multi-checkout
                if (!RepositoryUtil.HasMultipleCheckouts(context.JobSettings))
                {
                    context.SetVariable(Constants.Variables.System.DefaultWorkingDirectory, Path.Combine(_workDirectory, trackingConfig.SourcesDirectory), isFilePath: true);
                }
            }

            repository.Properties.Set("__AZP_READY", bool.TrueString);
        }
        // 1. use source provide to solve path, if solved result is rooted, return full path.
        // 2. prefix default path root (build.sourcesDirectory), if result is rooted, return full path.
        public override string GetRootedPath(IExecutionContext context, string path)
        {
            string rootedPath = null;

            TryGetRepositoryInfo(context, out RepositoryInfo repoInfo);

            if (repoInfo.SourceProvider != null &&
                repoInfo.Repository != null &&
                StringUtil.ConvertToBoolean(repoInfo.Repository.Properties.Get <string>("__AZP_READY")))
            {
                path = repoInfo.SourceProvider.GetLocalPath(context, repoInfo.Repository, path) ?? string.Empty;
                Trace.Info($"Build JobExtension resolving path use source provide: {path}");

                if (!string.IsNullOrEmpty(path) &&
                    path.IndexOfAny(Path.GetInvalidPathChars()) < 0 &&
                    Path.IsPathRooted(path))
                {
                    try
                    {
                        rootedPath = Path.GetFullPath(path);
                        Trace.Info($"Path resolved by source provider is a rooted path, return absolute path: {rootedPath}");
                        return(rootedPath);
                    }
                    catch (Exception ex)
                    {
                        Trace.Info($"Path resolved by source provider is a rooted path, but it is not a full qualified path: {path}");
                        Trace.Error(ex);
                    }
                }
            }

            string defaultPathRoot = null;

            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}");
            }
            else if (repoInfo.Repository != null)
            {
                // If there is only one checkout/repository, set it to the repository path
                defaultPathRoot = repoInfo.Repository.Properties.Get <string>(Pipelines.RepositoryPropertyNames.Path);
                Trace.Info($"The Default Path Root of Build JobExtension is repository.path: {defaultPathRoot}");
            }

            if (defaultPathRoot != null && defaultPathRoot.IndexOfAny(Path.GetInvalidPathChars()) < 0 &&
                path != null && path.IndexOfAny(Path.GetInvalidPathChars()) < 0)
            {
                path = Path.Combine(defaultPathRoot, path);
                Trace.Info($"After prefix Default Path Root provide by JobExtension: {path}");
                if (Path.IsPathRooted(path))
                {
                    try
                    {
                        rootedPath = Path.GetFullPath(path);
                        Trace.Info($"Return absolute path after prefix DefaultPathRoot: {rootedPath}");
                        return(rootedPath);
                    }
                    catch (Exception ex)
                    {
                        Trace.Error(ex);
                        Trace.Info($"After prefix Default Path Root provide by JobExtension, the Path is a rooted path, but it is not full qualified, return the path: {path}.");
                        return(path);
                    }
                }
            }

            return(rootedPath);
        }
        public TrackingConfig PrepareDirectory(
            IExecutionContext executionContext,
            IList <RepositoryResource> repositories,
            WorkspaceOptions workspace)
        {
            // Validate parameters.
            Trace.Entering();
            ArgUtil.NotNull(executionContext, nameof(executionContext));
            ArgUtil.NotNull(executionContext.Variables, nameof(executionContext.Variables));
            ArgUtil.NotNull(repositories, nameof(repositories));

            var trackingManager = HostContext.GetService <ITrackingManager>();

            // Create the tracking config for this execution of the pipeline
            var agentSettings = HostContext.GetService <IConfigurationStore>().GetSettings();
            var newConfig     = trackingManager.Create(executionContext, repositories, ShouldOverrideBuildDirectory(repositories, agentSettings));

            // Load the tracking config from the last execution of the pipeline
            var existingConfig = trackingManager.LoadExistingTrackingConfig(executionContext);

            // If there aren't any major changes, merge the configurations and use the same workspace
            if (trackingManager.AreTrackingConfigsCompatible(executionContext, newConfig, existingConfig))
            {
                newConfig = trackingManager.MergeTrackingConfigs(executionContext, newConfig, existingConfig);
            }
            else if (existingConfig != null)
            {
                // If the previous config had different repos, get a new workspace folder and mark the old one for clean up
                trackingManager.MarkForGarbageCollection(executionContext, existingConfig);

                // If the config file was updated to a new config, we need to delete the legacy artifact/staging directories.
                // DeleteDirectory will check for the existence of the folders first.
                DeleteDirectory(
                    executionContext,
                    description: "legacy artifacts directory",
                    path: Path.Combine(existingConfig.BuildDirectory, Constants.Build.Path.LegacyArtifactsDirectory));
                DeleteDirectory(
                    executionContext,
                    description: "legacy staging directory",
                    path: Path.Combine(existingConfig.BuildDirectory, Constants.Build.Path.LegacyStagingDirectory));
            }

            // Save any changes to the config file
            trackingManager.UpdateTrackingConfig(executionContext, newConfig);

            // Prepare the build directory.
            // There are 2 ways to provide build directory clean policy.
            //     1> set definition variable build.clean or agent.clean.buildDirectory. (on-prem user need to use this, since there is no Web UI in TFS 2016)
            //     2> select source clean option in definition repository tab. (VSTS will have this option in definition designer UI)
            BuildCleanOption cleanOption = GetBuildDirectoryCleanOption(executionContext, workspace);

            CreateDirectory(
                executionContext,
                description: "build directory",
                path: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.BuildDirectory),
                deleteExisting: cleanOption == BuildCleanOption.All);
            CreateDirectory(
                executionContext,
                description: "artifacts directory",
                path: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.ArtifactsDirectory),
                deleteExisting: true);
            CreateDirectory(
                executionContext,
                description: "test results directory",
                path: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.TestResultsDirectory),
                deleteExisting: true);
            CreateDirectory(
                executionContext,
                description: "binaries directory",
                path: Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.BuildDirectory, Constants.Build.Path.BinariesDirectory),
                deleteExisting: cleanOption == BuildCleanOption.Binary);

            var defaultSourceDirectory = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.SourcesDirectory);

            CreateDirectory(
                executionContext,
                description: "source directory",
                path: defaultSourceDirectory,
                deleteExisting: cleanOption == BuildCleanOption.Source);

            // Set the default clone path for each repository (the Checkout task may override this later)
            foreach (var repository in repositories)
            {
                String repoPath;
                if (RepositoryUtil.HasMultipleCheckouts(executionContext.JobSettings))
                {
                    // If we have multiple checkouts they should all be rooted to the sources directory (_work/1/s/repo1)
                    var repoSourceDirectory = newConfig?.RepositoryTrackingInfo.Where(item => string.Equals(item.Identifier, repository.Alias, StringComparison.OrdinalIgnoreCase)).Select(item => item.SourcesDirectory).FirstOrDefault();
                    if (repoSourceDirectory != null)
                    {
                        repoPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), repoSourceDirectory);
                    }
                    else
                    {
                        repoPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.SourcesDirectory, RepositoryUtil.GetCloneDirectory(repository));
                    }
                }
                else
                {
                    // For single checkouts, the repository is rooted to the sources folder (_work/1/s)
                    repoPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Work), newConfig.SourcesDirectory);
                }

                if (!string.Equals(repoPath, defaultSourceDirectory, StringComparison.Ordinal))
                {
                    CreateDirectory(
                        executionContext,
                        description: "repository source directory",
                        path: repoPath,
                        deleteExisting: cleanOption == BuildCleanOption.Source);
                }

                Trace.Info($"Set repository path for repository {repository.Alias} to '{repoPath}'");
                repository.Properties.Set <string>(RepositoryPropertyNames.Path, repoPath);
            }

            return(newConfig);
        }
Пример #10
0
 protected bool HasMultipleCheckouts(AgentTaskPluginExecutionContext executionContext)
 {
     return(executionContext != null && RepositoryUtil.HasMultipleCheckouts(executionContext.JobSettings));
 }
        public void Execute(IExecutionContext context, Command command)
        {
            ArgUtil.NotNull(context, nameof(context));
            ArgUtil.NotNull(command, nameof(command));

            var eventProperties = command.Properties;
            var data            = command.Data;

            String alias;

            if (!eventProperties.TryGetValue(PluginInternalUpdateRepositoryEventProperties.Alias, out alias) || String.IsNullOrEmpty(alias))
            {
                throw new ArgumentNullException(StringUtil.Loc("MissingRepositoryAlias"));
            }

            var repository = context.Repositories.FirstOrDefault(x => string.Equals(x.Alias, alias, StringComparison.OrdinalIgnoreCase));

            if (repository == null)
            {
                throw new ArgumentNullException(StringUtil.Loc("RepositoryNotExist"));
            }

            if (string.IsNullOrEmpty(data))
            {
                throw new ArgumentNullException(StringUtil.Loc("MissingRepositoryPath"));
            }

            var currentPath = repository.Properties.Get <string>(RepositoryPropertyNames.Path);

            if (!string.Equals(data.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), currentPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), IOUtil.FilePathStringComparison))
            {
                string repositoryPath = data.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
                repository.Properties.Set <string>(RepositoryPropertyNames.Path, repositoryPath);

                bool isSelfRepo           = RepositoryUtil.IsPrimaryRepositoryName(repository.Alias);
                bool hasMultipleCheckouts = RepositoryUtil.HasMultipleCheckouts(context.JobSettings);

                var    directoryManager = context.GetHostContext().GetService <IBuildDirectoryManager>();
                string _workDirectory   = context.GetHostContext().GetDirectory(WellKnownDirectory.Work);
                var    trackingConfig   = directoryManager.UpdateDirectory(context, repository);

                if (isSelfRepo || !hasMultipleCheckouts)
                {
                    if (hasMultipleCheckouts)
                    {
                        // In Multi-checkout, we don't want to reset sources dir or default working dir.
                        // So, we will just reset the repo local path
                        string buildDirectory   = context.Variables.Get(Constants.Variables.Pipeline.Workspace);
                        string repoRelativePath = directoryManager.GetRelativeRepositoryPath(buildDirectory, repositoryPath);

                        string sourcesDirectory = context.Variables.Get(Constants.Variables.Build.SourcesDirectory);
                        string repoLocalPath    = context.Variables.Get(Constants.Variables.Build.RepoLocalPath);
                        string newRepoLocation  = Path.Combine(_workDirectory, repoRelativePath);
                        // For saving backward compatibility with the behavior of the Build.RepoLocalPath that was before this PR https://github.com/microsoft/azure-pipelines-agent/pull/3237
                        // we need to deny updating of the variable in case the new path is the default location for the repository that is equal to sourcesDirectory/repository.Name
                        // since the variable already has the right value in this case and pointing to the default sources location
                        if (repoLocalPath == null ||
                            !string.Equals(newRepoLocation, Path.Combine(sourcesDirectory, repository.Name), IOUtil.FilePathStringComparison))
                        {
                            context?.SetVariable(Constants.Variables.Build.RepoLocalPath, newRepoLocation, isFilePath: true);
                        }
                    }
                    else
                    {
                        // If we only have a single repository, then update all the paths to point to it.
                        context.SetVariable(Constants.Variables.Build.SourcesDirectory, repositoryPath, isFilePath: true);
                        context.SetVariable(Constants.Variables.Build.RepoLocalPath, repositoryPath, isFilePath: true);
                        context.SetVariable(Constants.Variables.System.DefaultWorkingDirectory, repositoryPath, isFilePath: true);
                    }
                }
            }

            repository.Properties.Set("__AZP_READY", bool.TrueString);
        }
        // Prepare build directory
        // Set all build related variables
        public override void InitializeJobExtension(IExecutionContext executionContext, IList <Pipelines.JobStep> steps, Pipelines.WorkspaceOptions workspace)
        {
            // Validate args.
            Trace.Entering();
            ArgUtil.NotNull(executionContext, nameof(executionContext));

            // This flag can be false for jobs like cleanup artifacts.
            // If syncSources = false, we will not set source related build variable, not create build folder, not sync source.
            bool syncSources = executionContext.Variables.Build_SyncSources ?? true;

            if (!syncSources)
            {
                Trace.Info($"{Constants.Variables.Build.SyncSources} = false, we will not set source related build variable, not create build folder and not sync source");
                return;
            }

            // We set the variables based on the 'self' repository
            if (!TryGetRepositoryInfo(executionContext, out RepositoryInfo repoInfo))
            {
                throw new Exception(StringUtil.Loc("SupportedRepositoryEndpointNotFound"));
            }

            executionContext.Debug($"Primary repository: {repoInfo.Repository.Properties.Get<string>(Pipelines.RepositoryPropertyNames.Name)}. repository type: {repoInfo.Repository.Type}");

            // Set the repo variables.
            if (!string.IsNullOrEmpty(repoInfo.Repository.Id)) // TODO: Move to const after source artifacts PR is merged.
            {
                executionContext.Variables.Set(Constants.Variables.Build.RepoId, repoInfo.Repository.Id);
            }

            executionContext.Variables.Set(Constants.Variables.Build.RepoName, repoInfo.Repository.Properties.Get <string>(Pipelines.RepositoryPropertyNames.Name));
            executionContext.Variables.Set(Constants.Variables.Build.RepoProvider, ConvertToLegacyRepositoryType(repoInfo.Repository.Type));
            executionContext.Variables.Set(Constants.Variables.Build.RepoUri, repoInfo.Repository.Url?.AbsoluteUri);

            // There may be more than one Checkout task, but for back compat we will simply pay attention to the first checkout task here
            var checkoutTask = steps.FirstOrDefault(x => x.IsCheckoutTask()) as Pipelines.TaskStep;

            if (checkoutTask != null)
            {
                if (checkoutTask.Inputs.ContainsKey(Pipelines.PipelineConstants.CheckoutTaskInputs.Submodules))
                {
                    executionContext.Variables.Set(Constants.Variables.Build.RepoGitSubmoduleCheckout, Boolean.TrueString);
                }
                else
                {
                    executionContext.Variables.Set(Constants.Variables.Build.RepoGitSubmoduleCheckout, Boolean.FalseString);
                }

                // overwrite primary repository's clean value if build.repository.clean is sent from server. this is used by tfvc gated check-in
                bool?repoClean = executionContext.Variables.GetBoolean(Constants.Variables.Build.RepoClean);
                if (repoClean != null)
                {
                    checkoutTask.Inputs[Pipelines.PipelineConstants.CheckoutTaskInputs.Clean] = repoClean.Value.ToString();
                }
                else
                {
                    if (checkoutTask.Inputs.ContainsKey(Pipelines.PipelineConstants.CheckoutTaskInputs.Clean))
                    {
                        executionContext.Variables.Set(Constants.Variables.Build.RepoClean, checkoutTask.Inputs[Pipelines.PipelineConstants.CheckoutTaskInputs.Clean]);
                    }
                    else
                    {
                        executionContext.Variables.Set(Constants.Variables.Build.RepoClean, Boolean.FalseString);
                    }
                }
            }


            // Prepare the build directory.
            executionContext.Output(StringUtil.Loc("PrepareBuildDir"));
            var            directoryManager = HostContext.GetService <IBuildDirectoryManager>();
            TrackingConfig trackingConfig   = directoryManager.PrepareDirectory(
                executionContext,
                executionContext.Repositories,
                workspace);

            // Set the directory variables.
            executionContext.Output(StringUtil.Loc("SetBuildVars"));
            string _workDirectory = HostContext.GetDirectory(WellKnownDirectory.Work);

            executionContext.SetVariable(Constants.Variables.Agent.BuildDirectory, Path.Combine(_workDirectory, trackingConfig.BuildDirectory), isFilePath: true);
            executionContext.SetVariable(Constants.Variables.System.ArtifactsDirectory, Path.Combine(_workDirectory, trackingConfig.ArtifactsDirectory), 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);
            executionContext.SetVariable(Constants.Variables.Build.StagingDirectory, Path.Combine(_workDirectory, trackingConfig.ArtifactsDirectory), isFilePath: true);
            executionContext.SetVariable(Constants.Variables.Build.ArtifactStagingDirectory, Path.Combine(_workDirectory, trackingConfig.ArtifactsDirectory), isFilePath: true);
            executionContext.SetVariable(Constants.Variables.Build.RepoLocalPath, Path.Combine(_workDirectory, trackingConfig.SourcesDirectory), isFilePath: true);
            executionContext.SetVariable(Constants.Variables.Pipeline.Workspace, Path.Combine(_workDirectory, trackingConfig.BuildDirectory), isFilePath: true);

            if (RepositoryUtil.HasMultipleCheckouts(executionContext.JobSettings))
            {
                // If there are multiple checkouts, set the working directory to root folder (_work/1)
                executionContext.SetVariable(Constants.Variables.System.DefaultWorkingDirectory, Path.Combine(_workDirectory, trackingConfig.BuildDirectory), isFilePath: true);
            }
            else
            {
                // If there is only one (or none) checkout, set to the normal default sources path (_work/1/s)
                executionContext.SetVariable(Constants.Variables.System.DefaultWorkingDirectory, Path.Combine(_workDirectory, trackingConfig.SourcesDirectory), isFilePath: true);
            }
        }