private TaskStep GetSelfCheckoutTask(IList<JobStep> steps) { return steps.Select(x => x as TaskStep) .Where(task => task.IsCheckoutTask() && task.Inputs.TryGetValue(PipelineConstants.CheckoutTaskInputs.Repository, out string repositoryAlias) && RepositoryUtil.IsPrimaryRepositoryName(repositoryAlias)).FirstOrDefault(); }
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; }
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); }
public void IsPrimaryRepositoryName_should_work_correctly() { using (TestHostContext hc = new TestHostContext(this)) { Assert.Equal(false, RepositoryUtil.IsPrimaryRepositoryName(null)); Assert.Equal(false, RepositoryUtil.IsPrimaryRepositoryName("")); Assert.Equal(false, RepositoryUtil.IsPrimaryRepositoryName("none")); Assert.Equal(false, RepositoryUtil.IsPrimaryRepositoryName("some random string")); Assert.Equal(true, RepositoryUtil.IsPrimaryRepositoryName("self")); Assert.Equal(true, RepositoryUtil.IsPrimaryRepositoryName("SELF")); Assert.Equal(true, RepositoryUtil.IsPrimaryRepositoryName("Self")); Assert.Equal(true, RepositoryUtil.IsPrimaryRepositoryName("sELF")); } }
private void UpdateCheckoutTasksAndVariables(IExecutionContext executionContext, IList <JobStep> steps, RepositoryInfo repoInfo, string pipelineWorkspaceDirectory) { bool?submoduleCheckout = null; // RepoClean may be set from the server, so start with the server value bool?repoClean = executionContext.Variables.GetBoolean(Constants.Variables.Build.RepoClean); foreach (var checkoutTask in steps.Where(x => x.IsCheckoutTask()).Select(x => x as TaskStep)) { if (!checkoutTask.Inputs.TryGetValue(PipelineConstants.CheckoutTaskInputs.Repository, out string repositoryAlias)) { // If the checkout task isn't associated with a repo, just skip it Trace.Info($"Checkout task {checkoutTask.Name} does not have a repository property."); continue; } // Update the checkout "Clean" property for all repos, if the variable was set by the server. if (repoClean != null) { checkoutTask.Inputs[PipelineConstants.CheckoutTaskInputs.Clean] = repoClean.Value.ToString(); } // If this is the primary repository, use it to get the variable values if (RepositoryUtil.IsPrimaryRepositoryName(repositoryAlias)) { submoduleCheckout = checkoutTask.Inputs.ContainsKey(PipelineConstants.CheckoutTaskInputs.Submodules); repoClean = repoClean ?? checkoutTask.Inputs.ContainsKey(PipelineConstants.CheckoutTaskInputs.Clean); } // Update the checkout task display name if not already set if (string.IsNullOrEmpty(checkoutTask.DisplayName) || string.Equals(checkoutTask.DisplayName, "Checkout", StringComparison.OrdinalIgnoreCase)) { var repository = RepositoryUtil.GetRepository(executionContext.Repositories, repositoryAlias); if (repository != null) { string repoName = repository.Properties.Get <string>(RepositoryPropertyNames.Name); string version = RepositoryUtil.TrimStandardBranchPrefix(repository.Properties.Get <string>(RepositoryPropertyNames.Ref)); string path = null; if (checkoutTask.Inputs.ContainsKey(PipelineConstants.CheckoutTaskInputs.Path)) { path = checkoutTask.Inputs[PipelineConstants.CheckoutTaskInputs.Path]; } else { path = IOUtil.MakeRelative(repository.Properties.Get <string>(RepositoryPropertyNames.Path), pipelineWorkspaceDirectory); } checkoutTask.DisplayName = StringUtil.Loc("CheckoutTaskDisplayNameFormat", repoName, version, path); } else { Trace.Info($"Checkout task {checkoutTask.Name} has a repository property {repositoryAlias} that does not match any repository resource."); } } } // Set variables if (submoduleCheckout.HasValue) { executionContext.SetVariable(Constants.Variables.Build.RepoGitSubmoduleCheckout, submoduleCheckout.Value.ToString()); } if (repoClean.HasValue) { executionContext.SetVariable(Constants.Variables.Build.RepoClean, repoClean.Value.ToString()); } }
private void UpdateCheckoutTasksAndVariables(IExecutionContext executionContext, IList <JobStep> steps, string pipelineWorkspaceDirectory) { bool?submoduleCheckout = null; // RepoClean may be set from the server, so start with the server value bool?repoCleanFromServer = executionContext.Variables.GetBoolean(Constants.Variables.Build.RepoClean); // The value for the global clean option will be set in this variable based on Self repository clean input if the global value weren't set by the server bool?repoCleanFromSelf = null; var checkoutTasks = steps.Where(x => x.IsCheckoutTask()).Select(x => x as TaskStep).ToList(); var hasOnlyOneCheckoutTask = checkoutTasks.Count == 1; foreach (var checkoutTask in checkoutTasks) { if (!checkoutTask.Inputs.TryGetValue(PipelineConstants.CheckoutTaskInputs.Repository, out string repositoryAlias)) { // If the checkout task isn't associated with a repo, just skip it Trace.Info($"Checkout task {checkoutTask.Name} does not have a repository property."); continue; } // Update the checkout "Clean" property for all repos, if the variable was set by the server. if (repoCleanFromServer.HasValue) { checkoutTask.Inputs[PipelineConstants.CheckoutTaskInputs.Clean] = repoCleanFromServer.Value.ToString(); } Trace.Info($"Checking repository name {repositoryAlias}"); // If this is the primary repository, use it to get the variable values // A repository is considered the primary one if the name is 'self' or if there is only // one checkout task. This is because Designer builds set the name of the repository something // other than 'self' if (hasOnlyOneCheckoutTask || RepositoryUtil.IsPrimaryRepositoryName(repositoryAlias)) { submoduleCheckout = checkoutTask.Inputs.ContainsKey(PipelineConstants.CheckoutTaskInputs.Submodules); if (!repoCleanFromServer.HasValue && checkoutTask.Inputs.TryGetValue(PipelineConstants.CheckoutTaskInputs.Clean, out string cleanInputValue)) { repoCleanFromSelf = Boolean.TryParse(cleanInputValue, out bool cleanValue) ? cleanValue : true; } } // Update the checkout task display name if not already set if (string.IsNullOrEmpty(checkoutTask.DisplayName) || string.Equals(checkoutTask.DisplayName, "Checkout", StringComparison.OrdinalIgnoreCase) || // this is the default for jobs string.Equals(checkoutTask.DisplayName, checkoutTask.Name, StringComparison.OrdinalIgnoreCase)) // this is the default for deployment jobs { var repository = RepositoryUtil.GetRepository(executionContext.Repositories, repositoryAlias); if (repository != null) { string repoName = repository.Properties.Get <string>(RepositoryPropertyNames.Name); string version = RepositoryUtil.TrimStandardBranchPrefix(repository.Properties.Get <string>(RepositoryPropertyNames.Ref)); string path = null; if (checkoutTask.Inputs.ContainsKey(PipelineConstants.CheckoutTaskInputs.Path)) { path = checkoutTask.Inputs[PipelineConstants.CheckoutTaskInputs.Path]; } else { path = IOUtil.MakeRelative(repository.Properties.Get <string>(RepositoryPropertyNames.Path), pipelineWorkspaceDirectory); } checkoutTask.DisplayName = StringUtil.Loc("CheckoutTaskDisplayNameFormat", repoName, version, path); } else { Trace.Info($"Checkout task {checkoutTask.Name} has a repository property {repositoryAlias} that does not match any repository resource."); } } } // Set variables if (submoduleCheckout.HasValue) { executionContext.SetVariable(Constants.Variables.Build.RepoGitSubmoduleCheckout, submoduleCheckout.Value.ToString()); } if (repoCleanFromSelf.HasValue) { executionContext.SetVariable(Constants.Variables.Build.RepoClean, repoCleanFromSelf.Value.ToString()); } }
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); }
protected bool ShouldUpdateRepositoryPath(AgentTaskPluginExecutionContext executionContext, string repositoryAlias) { // Only update the repo path if this is the only checkout task or this checkout task is for the 'self' repo return(!HasMultipleCheckouts(executionContext) || RepositoryUtil.IsPrimaryRepositoryName(repositoryAlias)); }