public IEnumerable <SdkComponent> Scan(DirectoryInfo path, string searchPattern) { string variantPattern = searchPattern.Replace(".yml", "\\.(?<variant>([a-z]+))\\.yml"); Regex variantExtractionExpression = new Regex($"^{variantPattern}$"); Logger.LogDebug($"Scanning directory '{path.FullName}' for components with search pattern '{searchPattern}' variant pattern '{variantPattern}'"); if (!path.Exists) { throw new ArgumentException(nameof(path), "Path does not exist."); } var pipelineYamlFiles = path.EnumerateFiles(searchPattern, SearchOption.AllDirectories); pipelineYamlFiles = pipelineYamlFiles.Concat(path.EnumerateFiles(searchPattern.Replace(".yml", ".*.yml"), SearchOption.AllDirectories)); if (pipelineYamlFiles.Count() == 0) { Logger.LogWarning("Did not find any YAML files with search pattern '{0}' in path '{1}'.", searchPattern, path.FullName); } Logger.LogDebug("Finding repository root from '{0}'.", path.FullName); var repositoryHelper = new RepositoryHelper(); var root = repositoryHelper.GetRepositoryRoot(path); Logger.LogDebug("Found repository root at: {0}", root); foreach (var pipelineYamlFile in pipelineYamlFiles) { var relativePath = Path.GetRelativePath(root, pipelineYamlFile.FullName); Logger.LogDebug("Repository root relative path for '{0}' is: {1}", pipelineYamlFile, relativePath); var component = new SdkComponent() { Name = pipelineYamlFile.Directory.Name, Path = pipelineYamlFile.Directory, RelativeYamlPath = relativePath }; // Append variant information. if (variantExtractionExpression.IsMatch(pipelineYamlFile.Name)) { var match = variantExtractionExpression.Match(pipelineYamlFile.Name); var variant = match.Groups["variant"].Value; component.Variant = variant; Logger.LogDebug($"variant = {variant}"); } yield return(component); } }
public async Task <BuildDefinition> CreateOrUpdateDefinitionAsync(SdkComponent component, CancellationToken cancellationToken) { var definitionName = GetDefinitionName(component); Logger.LogDebug("Checking to see if definition '{0}' exists prior to create/update.", definitionName); var definition = await GetExistingDefinitionAsync(definitionName, cancellationToken); if (definition == null) { Logger.LogDebug("Definition '{0}' was not found.", definitionName); definition = await CreateDefinitionAsync(definitionName, component, cancellationToken); } Logger.LogDebug("Applying convention to '{0}' definition.", definitionName); var hasChanges = await ApplyConventionAsync(definition, component); if (hasChanges) { if (!Context.WhatIf) { Logger.LogInformation("Convention had changes, updating '{0}' definition.", definitionName); var buildClient = await Context.GetBuildHttpClientAsync(cancellationToken); definition = await buildClient.UpdateDefinitionAsync( definition : definition, cancellationToken : cancellationToken ); } else { Logger.LogWarning("Skipping update to definition '{0}' (--whatif).", definitionName); } } else { Logger.LogDebug("No changes for definition '{0}'.", definitionName); } return(definition); }
public IEnumerable <SdkComponent> Scan(DirectoryInfo path, string searchPattern) { Logger.LogDebug("Scanning directory '{0}' for components with search pattern '{1}'.", path.FullName, searchPattern); if (!path.Exists) { throw new ArgumentException(nameof(path), "Path does not exist."); } var pipelineYamlFiles = path.EnumerateFiles(searchPattern, SearchOption.AllDirectories); if (pipelineYamlFiles.Count() == 0) { Logger.LogWarning("Did not find any YAML files with search pattern '{0}' in path '{1}'.", searchPattern, path.FullName); } Logger.LogDebug("Finding repository root from '{0}'.", path.FullName); var repositoryHelper = new RepositoryHelper(); var root = repositoryHelper.GetRepositoryRoot(path); Logger.LogDebug("Found repository root at: {0}", root); foreach (var pipelineYamlFile in pipelineYamlFiles) { var relativePath = Path.GetRelativePath(root, pipelineYamlFile.FullName); Logger.LogDebug("Repository root relative path for '{0}' is: {1}", pipelineYamlFile, relativePath); var component = new SdkComponent() { Name = pipelineYamlFile.Directory.Name, Path = pipelineYamlFile.Directory, RelativeYamlPath = relativePath }; yield return(component); } }
public async Task <BuildDefinition> DeleteDefinitionAsync(SdkComponent component, CancellationToken cancellationToken) { var definitionName = GetDefinitionName(component); Logger.LogDebug("Checking to see if definition '{0}' exists prior to deleting.", definitionName); var definition = await GetExistingDefinitionAsync(definitionName, cancellationToken); if (definition != null) { Logger.LogDebug("Found definition called '{0}' at '{1}'.", definitionName, definition.GetWebUrl()); if (!Context.WhatIf) { Logger.LogWarning("Deleting definition '{0}'.", definitionName); var projectReference = await Context.GetProjectReferenceAsync(cancellationToken); var buildClient = await Context.GetBuildHttpClientAsync(cancellationToken); await buildClient.DeleteDefinitionAsync( project : projectReference.Id, definitionId : definition.Id, cancellationToken : cancellationToken ); } else { Logger.LogWarning("Skipping deleting definition '{0}' (--whatif).", definitionName); } return(definition); } else { Logger.LogDebug("No definition called '{0}' existed.", definitionName); return(null); } }
protected async override Task <bool> ApplyConventionAsync(BuildDefinition definition, SdkComponent component) { // Daniel - your custom logic goes here.! return(false); }
protected override string GetDefinitionName(SdkComponent component) { return($"{Context.Prefix} - {component.Name} - tests"); }
protected async override Task <bool> ApplyConventionAsync(BuildDefinition definition, SdkComponent component) { // NOTE: Not happy with this code at all, I'm going to look for a reasonable // API that can do equality comparisons (without having to do all the checks myself). var hasChanges = false; var ciTrigger = definition.Triggers.OfType <ContinuousIntegrationTrigger>().SingleOrDefault(); if (ciTrigger == null) { definition.Triggers.Add(new ContinuousIntegrationTrigger() { SettingsSourceType = 2 // HACK: This is editor invisible, but this is required to inherit branch filters from YAML file. }); hasChanges = true; } else { if (ciTrigger.SettingsSourceType != 2) { ciTrigger.SettingsSourceType = 2; hasChanges = true; } } var prTrigger = definition.Triggers.OfType <PullRequestTrigger>().SingleOrDefault(); if (prTrigger == null) { // TODO: We should probably be more complete here. definition.Triggers.Add(new PullRequestTrigger() { SettingsSourceType = 2, // HACK: See above. Forks = new Forks() { AllowSecrets = false, Enabled = true } }); hasChanges = true; } else { // TODO: We should probably be more complete here. if (prTrigger.SettingsSourceType != 2 || prTrigger.Forks.AllowSecrets != false || prTrigger.Forks.Enabled != true) { prTrigger.SettingsSourceType = 2; prTrigger.Forks.AllowSecrets = false; prTrigger.Forks.Enabled = true; hasChanges = true; } } return(hasChanges); }
protected abstract string GetDefinitionName(SdkComponent component);
protected abstract Task <bool> ApplyConventionAsync(BuildDefinition definition, SdkComponent component);
private async Task <BuildDefinition> CreateDefinitionAsync(string definitionName, SdkComponent component, CancellationToken cancellationToken) { var sourceRepository = await Context.GetSourceRepositoryAsync(cancellationToken); var buildRepository = new BuildRepository() { DefaultBranch = Context.Branch, Id = sourceRepository.Id, Name = sourceRepository.FullName, Type = "GitHub", Url = new Uri(sourceRepository.Properties["cloneUrl"]), }; buildRepository.Properties.AddRangeIfRangeNotNull(sourceRepository.Properties); var projectReference = await Context.GetProjectReferenceAsync(cancellationToken); var agentPoolQueue = await Context.GetAgentPoolQueue(cancellationToken); var definition = new BuildDefinition() { Name = definitionName, Project = projectReference, Repository = buildRepository, Process = new YamlProcess() { YamlFilename = component.RelativeYamlPath }, Queue = agentPoolQueue }; if (!Context.WhatIf) { Logger.LogDebug("Creating definition named '{0}'.", definitionName); var buildClient = await Context.GetBuildHttpClientAsync(cancellationToken); definition = await buildClient.CreateDefinitionAsync( definition : definition, cancellationToken : cancellationToken ); Logger.LogInformation("Created definition '{0}' at: {1}", definitionName, definition.GetWebUrl()); } else { Logger.LogWarning("Skipping creating definition '{0}' (--whatif).", definitionName); } return(definition); }