Exemple #1
0
        /// <summary>
        /// Adds a new build history for a stream
        /// </summary>
        /// <param name="Issue">The issue to add a build to</param>
        /// <param name="InputJob">The job containing the error</param>
        /// <param name="InputJobStep">The job step containing the error</param>
        /// <param name="InputErrorUrl">Url of the error</param>
        /// <param name="State">Current persistent state. Used to find previous build history.</param>
        void AddFailureToIssue(BuildHealthIssue Issue, InputJob InputJob, InputJobStep InputJobStep, string InputErrorUrl, BuildHealthState State)
        {
            // Find or add a step name to history mapping
            Dictionary <string, BuildHealthJobHistory> StepNameToHistory;

            if (!Issue.Streams.TryGetValue(InputJob.Stream, out StepNameToHistory))
            {
                StepNameToHistory = new Dictionary <string, BuildHealthJobHistory>();
                Issue.Streams.Add(InputJob.Stream, StepNameToHistory);
            }

            // Find or add a history for this step
            BuildHealthJobHistory History;

            if (!StepNameToHistory.TryGetValue(InputJobStep.Name, out History))
            {
                History = new BuildHealthJobHistory(State.FindBuildBefore(InputJob.Stream, InputJob.Change, InputJobStep.Name));
                StepNameToHistory.Add(InputJobStep.Name, History);
            }

            // Add the new build
            History.AddFailedBuild(CreateBuildForJobStep(InputJob, InputJobStep, InputErrorUrl));
        }
Exemple #2
0
        /// <summary>
        /// Finds or adds an issue for a particular issue
        /// </summary>
        /// <param name="Perforce">Perforce connection used to find possible causers</param>
        /// <param name="State">The current set of tracked issues</param>
        /// <param name="Build">The new build</param>
        /// <param name="PreviousChange">The last changelist that was built before this one</param>
        /// <param name="InputJob">Job containing the step to add</param>
        /// <param name="InputJobStep">The job step to add</param>
        BuildHealthIssue MergeIntoExistingIssue(PerforceConnection Perforce, BuildHealthState State, InputJob InputJob, InputJobStep InputJobStep, BuildHealthIssue InputIssue, Lazy <IReadOnlyList <ChangeInfo> > LazyChanges)
        {
            // Find the pattern matcher for this fingerprint
            PatternMatcher Matcher = CategoryToMatcher[InputIssue.Category];

            // Check if it can be added to an existing open issue
            foreach (BuildHealthIssue Issue in State.Issues)
            {
                // Check this issue already exists in the current stream
                Dictionary <string, BuildHealthJobHistory> StepNameToHistory;
                if (!Issue.Streams.TryGetValue(InputJob.Stream, out StepNameToHistory))
                {
                    continue;
                }

                // Check that this issue has not already been closed
                BuildHealthJobHistory History;
                if (StepNameToHistory.TryGetValue(InputJobStep.Name, out History))
                {
                    if (!History.CanAddFailedBuild(InputJob.Change))
                    {
                        continue;
                    }
                }
                else
                {
                    if (!StepNameToHistory.Values.Any(x => x.CanAddFailedBuild(InputJob.Change)))
                    {
                        continue;
                    }
                }

                // Try to merge the fingerprint
                if (!Matcher.CanMerge(InputIssue, Issue))
                {
                    continue;
                }

                // Add the new build
                Matcher.Merge(InputIssue, Issue);
                return(Issue);
            }

            // Check if this issue can be merged with an issue built in another stream
            IReadOnlyList <ChangeInfo> Changes = LazyChanges.Value;

            if (Changes != null && Changes.Count > 0)
            {
                SortedSet <int> SourceChanges = new SortedSet <int>(Changes.SelectMany(x => x.SourceChanges));
                foreach (BuildHealthIssue Issue in State.Issues)
                {
                    // Check if this issue does not already contain this stream, but contains one of the causing changes
                    if (Issue.Streams.ContainsKey(InputJob.Stream))
                    {
                        continue;
                    }
                    if (!SourceChanges.Any(x => Issue.SourceChanges.Contains(x)))
                    {
                        continue;
                    }
                    if (!Matcher.CanMerge(InputIssue, Issue))
                    {
                        continue;
                    }

                    // Merge the issue
                    Matcher.Merge(InputIssue, Issue);
                    return(Issue);
                }
            }

            // Check if it can be merged into an issue that's been created for this job. We only do this after exhausting all other options.
            foreach (BuildHealthIssue Issue in State.Issues)
            {
                if (Issue.InitialJobUrl == InputIssue.InitialJobUrl && Matcher.CanMergeInitialJob(InputIssue, Issue))
                {
                    Matcher.Merge(InputIssue, Issue);
                    return(Issue);
                }
            }

            return(null);
        }
Exemple #3
0
        /// <summary>
        /// Finds or adds an issue for a particular fingerprint
        /// </summary>
        /// <param name="Perforce">Perforce connection used to find possible causers</param>
        /// <param name="State">The current set of tracked issues</param>
        /// <param name="Build">The new build</param>
        /// <param name="PreviousChange">The last changelist that was built before this one</param>
        /// <param name="InputJob">Job containing the step to add</param>
        /// <param name="InputJobStep">The job step to add</param>
        BuildHealthIssue FindOrAddIssueForFingerprint(PerforceConnection Perforce, BuildHealthState State, InputJob InputJob, InputJobStep InputJobStep, BuildHealthIssue InputIssue)
        {
            // Find the pattern matcher for this fingerprint
            PatternMatcher Matcher = CategoryToMatcher[InputIssue.Category];

            // Check if it can be added to an existing open issue
            foreach (BuildHealthIssue Issue in State.Issues)
            {
                // Check this issue already exists in the current stream
                Dictionary <string, BuildHealthJobHistory> StepNameToHistory;
                if (!Issue.Streams.TryGetValue(InputJob.Stream, out StepNameToHistory))
                {
                    continue;
                }

                // Check that this issue has not already been closed
                BuildHealthJobHistory History;
                if (StepNameToHistory.TryGetValue(InputJobStep.Name, out History))
                {
                    if (!History.CanAddFailedBuild(InputJob.Change))
                    {
                        continue;
                    }
                }
                else
                {
                    if (!StepNameToHistory.Values.Any(x => x.CanAddFailedBuild(InputJob.Change)))
                    {
                        continue;
                    }
                }

                // Try to merge the fingerprint
                if (!Matcher.CanMerge(InputIssue, Issue))
                {
                    continue;
                }

                // Add the new build
                Matcher.Merge(InputIssue, Issue);
                return(Issue);
            }

            // List of changes since the last successful build in this stream
            IReadOnlyList <ChangeInfo> Changes = null;

            // Find the previous changelist that was built in this stream
            List <BuildHealthJobStep> StreamBuilds;

            if (State.Streams.TryGetValue(InputJob.Stream, out StreamBuilds))
            {
                // Find the last change submitted to this stream before it started failing
                int LastChange = -1;
                for (int Idx = 0; Idx < StreamBuilds.Count && StreamBuilds[Idx].Change < InputJob.Change; Idx++)
                {
                    LastChange = StreamBuilds[Idx].Change;
                }

                // Allow adding to any open issue that contains changes merged from other branches
                if (LastChange != -1)
                {
                    // Query for all the changes since then
                    Changes = FindChanges(Perforce, InputJob.Stream, LastChange, InputJob.Change);
                    if (Changes.Count > 0)
                    {
                        SortedSet <int> SourceChanges = new SortedSet <int>(Changes.SelectMany(x => x.SourceChanges));
                        foreach (BuildHealthIssue Issue in State.Issues)
                        {
                            // Check if this issue does not already contain this stream, but contains one of the causing changes
                            if (Issue.Streams.ContainsKey(InputJob.Stream))
                            {
                                continue;
                            }
                            if (!SourceChanges.Any(x => Issue.SourceChanges.Contains(x)))
                            {
                                continue;
                            }
                            if (!Matcher.CanMerge(InputIssue, Issue))
                            {
                                continue;
                            }

                            // Merge the issue
                            Matcher.Merge(InputIssue, Issue);
                            return(Issue);
                        }
                    }
                }
            }

            // Create new issues for everything else in this stream
            if (Changes != null)
            {
                List <ChangeInfo> Causers = Matcher.FindCausers(Perforce, InputIssue, Changes);
                foreach (ChangeInfo Causer in Causers)
                {
                    InputIssue.SourceChanges.UnionWith(Causer.SourceChanges);
                    InputIssue.PendingWatchers.Add(Causer.Record.User);
                }
            }
            State.Issues.Add(InputIssue);
            return(InputIssue);
        }