public bool IsProductDependency( string sourceRepositoryName, string sourceBranchName, string targetRepositoryName, string targetBranchName) { // Take latest target builds ordered by DateProduced var latestTargetBuildsQuery = Builds .Where(b => (b.GitHubRepository == targetRepositoryName && b.GitHubBranch == targetBranchName) || (b.AzureDevOpsRepository == targetRepositoryName && b.AzureDevOpsBranch == targetBranchName)) .OrderByDescending(b => b.DateProduced); // For each build return its Id and HasProductDependencies flag saying // if there are any build dependencies in this build that come from source // and have IsProduct flag set. var buildDependenciesQuery = latestTargetBuildsQuery .Select(b => new { Id = b.Id, HasProductDependencies = BuildDependencies .Any(d => ((d.DependentBuild.GitHubRepository == sourceRepositoryName && d.DependentBuild.GitHubBranch == sourceBranchName) || (d.DependentBuild.AzureDevOpsRepository == sourceRepositoryName && d.DependentBuild.AzureDevOpsBranch == sourceBranchName)) && d.Build == b && d.IsProduct) }); // Take the most recent build. Can be null if there are no target builds yet. var latestBuild = buildDependenciesQuery.FirstOrDefault(); return(latestBuild?.HasProductDependencies ?? false); }
/// Returns all the affected projects of the given list of projects. /// Since the roots may refer to each other, the roots are included in the result set. public static Project[] AffectedProjects(this BuildDependencies dependencies, Project[] all, Project[] roots) { var dependentMap = DependentMap(dependencies, all); var allKeys = all.ToDictionary(GetProjectKey); var rootGuids = roots.Select(GetProjectKey).ToArray(); var todo = new Queue <string>(rootGuids); var affected = new HashSet <string>(rootGuids); while (todo.Count != 0) { var next = todo.Dequeue(); if (!dependentMap.TryGetValue(next, out var dependents)) { continue; } dependents.ForEach(dep => { if (affected.Add(dep)) { todo.Enqueue(dep); } }); } return(affected.Select(g => allKeys[g]).ToArray()); }
public BuildRequest( BuildDependencies dependencies, Project[] primaryProjects, Project[] skippedProjects, string solutionConfiguration, string solutionPlatform, SolutionContexts solutionContexts, (string, string)[] solutionProperties
public async Task <IList <Build> > GetBuildGraphAsync(int buildId) { var dependencyEntity = Model.FindEntityType(typeof(BuildDependency)); var buildIdColumnName = dependencyEntity.FindProperty(nameof(BuildDependency.BuildId)).Relational().ColumnName; var dependencyIdColumnName = dependencyEntity.FindProperty(nameof(BuildDependency.DependentBuildId)).Relational().ColumnName; var isProductColumnName = dependencyEntity.FindProperty(nameof(BuildDependency.IsProduct)).Relational().ColumnName; var edgeTable = dependencyEntity.Relational().TableName; var edges = BuildDependencies.FromSql($@" WITH traverse AS ( SELECT {buildIdColumnName}, {dependencyIdColumnName}, {isProductColumnName}, 0 as Depth from {edgeTable} WHERE {buildIdColumnName} = @id UNION ALL SELECT {edgeTable}.{buildIdColumnName}, {edgeTable}.{dependencyIdColumnName}, {edgeTable}.{isProductColumnName}, traverse.Depth + 1 FROM {edgeTable} INNER JOIN traverse ON {edgeTable}.{buildIdColumnName} = traverse.{dependencyIdColumnName} WHERE traverse.{isProductColumnName} = 1 -- The thing we previously traversed was a product dependency AND traverse.Depth < 10 -- Don't load all the way back because of incorrect isProduct columns ) SELECT DISTINCT {buildIdColumnName}, {dependencyIdColumnName}, {isProductColumnName} FROM traverse;", new SqlParameter("id", buildId)); List <BuildDependency> things = await edges.ToListAsync(); var buildIds = new HashSet <int>(things.SelectMany(t => new[] { t.BuildId, t.DependentBuildId })); buildIds.Add(buildId); // Make sure we always include the requested build, even if it has no edges. IQueryable <Build> builds = from build in Builds where buildIds.Contains(build.Id) select build; Dictionary <int, Build> dict = await builds.ToDictionaryAsync(b => b.Id, b => { b.DependentBuildIds = new List <BuildDependency>(); return(b); }); foreach (var edge in things) { dict[edge.BuildId].DependentBuildIds.Add(edge); } return(dict.Values.ToList()); }
/// Returns the keys of the direct dependencies of a project. public static string[] DependentProjectKeys(BuildDependencies dependencies, Project project) { var dependency = dependencies.Item(project.UniqueName); return(((IEnumerable)dependency.RequiredProjects) .Cast <Project>() .Select(rp => rp.UniqueName) .ToArray()); }
public bool IsProductDependency(int buildId, string repositoryName, string branchName) { return(BuildDependencies .Any(d => d.IsProduct && d.DependentBuildId == buildId && ((d.Build.GitHubRepository == repositoryName && d.Build.GitHubBranch == branchName) || (d.Build.AzureDevOpsRepository == repositoryName && d.Build.AzureDevOpsBranch == branchName)))); }
public static Project[] SortByBuildOrder(BuildDependencies dependencies, Project[] instances) { var rootProjects = instances.ToDictionary(GetProjectKey); var ordered = rootProjects.Keys.SortTopologicallyReverse(key => DependentProjectKeys(dependencies, rootProjects[key]) .Where(rootProjects.ContainsKey)); return(ordered.Select(key => rootProjects[key]).ToArray()); }
public BuildRequest( BuildDependencies dependencies, Project[] primaryProjects, Project[] skippedProjects, string solutionConfiguration, string solutionPlatform, SolutionContexts solutionContexts, string SolutionDir) { var allProjects = primaryProjects.Concat(skippedProjects).ToArray(); var allOrdered = Projects.SortByBuildOrder(dependencies, allProjects); Dictionary <string, (string, string)[]> fixedProjectProperties = new Dictionary <string, (string, string)[]>();
/// Returns the keys of the direct dependencies of a project. public static string[] DependentProjectKeys(BuildDependencies dependencies, Project project) { var uniqueName = project.UniqueName; var dependency = dependencies.Item(uniqueName); // dependency might be null, see #52. if (dependency == null) { return(new string[0]); } return(((IEnumerable)dependency.RequiredProjects) .Cast <Project>() .Select(rp => rp.UniqueName) .ToArray()); }
// dependencies calculated during built // This will be updated when it rebuilt public ResultCode AddBuildDependency(Asset dependency) { // Skip if they are already depend on each other if (Dependencies.Contains(dependency.Dependencies)) { return(new ResultCode(ResultCode.SUCCESS)); } var result = BuildDependencies.AddDependency(dependency.BuildDependencies); if (!result.IsSuccessed()) { ToolDebug.Error("Add dependency is failed: {0} <-> {1}", Name, dependency.Name); return(result); } return(new ResultCode(ResultCode.SUCCESS)); }
public static Dictionary <string, HashSet <string> > DependentMap(BuildDependencies dependencies, Project[] all) { var allKeys = all.ToDictionary(GetProjectKey); var dict = new Dictionary <string, HashSet <string> >(); foreach (var project in all) { var key = project.GetProjectKey(); var deps = DependentProjectKeys(dependencies, project).Where(allKeys.ContainsKey).ToArray(); foreach (var dep in deps) { if (!dict.TryGetValue(dep, out var dependents)) { dependents = new HashSet <string>(); dict.Add(dep, dependents); } dependents.Add(key); } } return(dict); }
/// Returns all the dependencies of a number of projects (direct and transitive). /// Never returns a root, even if roots hold references to each other. public static Project[] Dependencies(BuildDependencies buildDependencies, Project[] allProjects, Project[] roots) { var allKeys = allProjects.ToDictionary(GetProjectKey); var todo = new Queue <string>(roots.Select(GetProjectKey)); var rootSet = new HashSet <string>(roots.Select(GetProjectKey)); var dependencies = new HashSet <string>(); while (todo.Count != 0) { var next = todo.Dequeue(); DependentProjectKeys(buildDependencies, allKeys[next]) .Where(g => !dependencies.Contains(g) && !rootSet.Contains(g) && allKeys.ContainsKey(g)) .ForEach(g => { todo.Enqueue(g); dependencies.Add(g); }); } return(dependencies .Select(g => allKeys[g]) .ToArray()); }
public async Task <IList <Build> > GetBuildGraphAsync(int buildId) { var dependencyEntity = Model.FindEntityType(typeof(BuildDependency)); var buildIdColumnName = dependencyEntity.FindProperty(nameof(BuildDependency.BuildId)).Relational().ColumnName; var dependencyIdColumnName = dependencyEntity.FindProperty(nameof(BuildDependency.DependentBuildId)).Relational().ColumnName; var isProductColumnName = dependencyEntity.FindProperty(nameof(BuildDependency.IsProduct)).Relational().ColumnName; var timeToInclusionInMinutesColumnName = dependencyEntity.FindProperty(nameof(BuildDependency.TimeToInclusionInMinutes)).Relational().ColumnName; var edgeTable = dependencyEntity.Relational().TableName; var edges = BuildDependencies.FromSql($@" WITH traverse AS ( SELECT {buildIdColumnName}, {dependencyIdColumnName}, {isProductColumnName}, {timeToInclusionInMinutesColumnName}, 0 as Depth from {edgeTable} WHERE {buildIdColumnName} = @id UNION ALL SELECT {edgeTable}.{buildIdColumnName}, {edgeTable}.{dependencyIdColumnName}, {edgeTable}.{isProductColumnName}, {edgeTable}.{timeToInclusionInMinutesColumnName}, traverse.Depth + 1 FROM {edgeTable} INNER JOIN traverse ON {edgeTable}.{buildIdColumnName} = traverse.{dependencyIdColumnName} WHERE traverse.{isProductColumnName} = 1 -- The thing we previously traversed was a product dependency AND traverse.Depth < 10 -- Don't load all the way back because of incorrect isProduct columns ) SELECT DISTINCT {buildIdColumnName}, {dependencyIdColumnName}, {isProductColumnName}, {timeToInclusionInMinutesColumnName} FROM traverse;", new SqlParameter("id", buildId)); List <BuildDependency> things = await edges.ToListAsync(); var buildIds = new HashSet <int>(things.SelectMany(t => new[] { t.BuildId, t.DependentBuildId })); buildIds.Add(buildId); // Make sure we always include the requested build, even if it has no edges. IQueryable <Build> builds = from build in Builds where buildIds.Contains(build.Id) select build; Dictionary <int, Build> dict = await builds.ToDictionaryAsync(b => b.Id, b => { b.DependentBuildIds = new List <BuildDependency>(); return(b); }); foreach (var edge in things) { dict[edge.BuildId].DependentBuildIds.Add(edge); } // Gather subscriptions used by this build. Build primaryBuild = Builds.First(b => b.Id == buildId); var validSubscriptions = await Subscriptions.Where(s => (s.TargetRepository == primaryBuild.AzureDevOpsRepository || s.TargetRepository == primaryBuild.GitHubRepository) && (s.TargetBranch == primaryBuild.AzureDevOpsBranch || s.TargetBranch == primaryBuild.GitHubBranch || $"refs/heads/{s.TargetBranch}" == primaryBuild.AzureDevOpsBranch || $"refs/heads/{s.TargetBranch}" == primaryBuild.GitHubBranch)).ToListAsync(); // Use the subscriptions to determine what channels are relevant for this build, so just grab the unique channel ID's from valid suscriptions var channelIds = validSubscriptions.GroupBy(x => x.ChannelId).Select(y => y.First()).Select(s => s.ChannelId); // Acquire list of builds in valid channels var channelBuildIds = await BuildChannels.Where(b => channelIds.Any(c => c == b.ChannelId)).Select(s => s.BuildId).ToListAsync(); var possibleBuilds = await Builds.Where(b => channelBuildIds.Any(c => c == b.Id)).ToListAsync(); // Calculate total number of builds that are newer. foreach (var id in dict.Keys) { var build = dict[id]; // Get newer builds data for this channel. var newer = possibleBuilds.Where(b => b.GitHubRepository == build.GitHubRepository && b.AzureDevOpsRepository == build.AzureDevOpsRepository && b.DateProduced > build.DateProduced); dict[id].Staleness = newer.Count(); } return(dict.Values.ToList()); }
public BuildDependenciesNodeFactory(BuildDependencies buildDependencies) { _buildDependencies = buildDependencies; }