Exemple #1
0
        public async Task ProcessBuildAsync(BuildKey buildKey)
        {
            var modelBuildAttempts = await TriageContextUtil
                                     .GetModelBuildAttemptsQuery(buildKey)
                                     .Include(x => x.ModelBuild)
                                     .ToListAsync()
                                     .ConfigureAwait(false);

            var modelBuild = modelBuildAttempts.FirstOrDefault()?.ModelBuild;

            if (modelBuild is null)
            {
                // This happens when we have no data on the build at all
                Logger.LogWarning($"No model for the build {buildKey}");
                return;
            }

            var failed = modelBuild.BuildResult == BuildResult.Failed || modelBuild.BuildResult == BuildResult.Canceled;

            if (!failed)
            {
                Logger.LogWarning($"Build did not fail so no retry is needed");
                return;
            }

            await RetryOsxDeprovisionAsync(modelBuild, modelBuildAttempts);
        }
Exemple #2
0
        /// <summary>
        /// Get the report for builds that are associated with the given GitHub Issue Key
        /// </summary>
        public async Task <string> GetAssociatedIssueReportAsync(GitHubIssueKey issueKey)
        {
            var query = TriageContextUtil
                        .GetModelGitHubIssuesQuery(issueKey)
                        .Select(x => new
            {
                x.ModelBuild.AzureOrganization,
                x.ModelBuild.AzureProject,
                x.ModelBuild.BuildNumber,
                x.ModelBuild.QueueTime,
                x.ModelBuild.PullRequestNumber,
                x.ModelBuild.GitHubOrganization,
                x.ModelBuild.GitHubRepository,
                x.ModelBuild.GitHubTargetBranch,
            });
            var builds = await query.ToListAsync().ConfigureAwait(false);

            var results = builds
                          .Select(x => (new BuildInfo(
                                            x.AzureOrganization,
                                            x.AzureProject,
                                            x.BuildNumber,
                                            new GitHubBuildInfo(x.GitHubOrganization, x.GitHubRepository, x.PullRequestNumber, x.GitHubTargetBranch)),
                                        x.QueueTime));
            var report = ReportBuilder.BuildManual(results);

            return(WrapInStartEndMarkers(report));
        }
Exemple #3
0
 public StatusPageUtil(
     IGitHubClientFactory gitHubClientFactory,
     TriageContext context,
     ILogger logger)
 {
     GitHubClientFactory = gitHubClientFactory;
     TriageContextUtil   = new TriageContextUtil(context);
     Logger = logger;
 }
Exemple #4
0
 public BuildRetryUtil(
     DevOpsServer server,
     TriageContext context,
     ILogger logger)
 {
     Server            = server;
     TriageContextUtil = new TriageContextUtil(context);
     Logger            = logger;
 }
Exemple #5
0
 public ModelDataUtil(
     DotNetQueryUtil queryUtil,
     TriageContextUtil triageContextUtil,
     ILogger logger)
 {
     Server            = queryUtil.Server;
     QueryUtil         = queryUtil;
     TriageContextUtil = triageContextUtil;
     Logger            = logger;
 }
Exemple #6
0
 public TrackingGitHubUtil(
     IGitHubClientFactory gitHubClientFactory,
     TriageContext context,
     SiteLinkUtil siteLinkUtil,
     ILogger logger)
 {
     GitHubClientFactory = gitHubClientFactory;
     TriageContextUtil   = new TriageContextUtil(context);
     SiteLinkUtil        = siteLinkUtil;
     Logger = logger;
 }
Exemple #7
0
 public TrackingIssueUtil(
     HelixServer helixServer,
     DotNetQueryUtil queryUtil,
     TriageContextUtil triageContextUtil,
     ILogger logger)
 {
     HelixServer       = helixServer;
     QueryUtil         = queryUtil;
     TriageContextUtil = triageContextUtil;
     Logger            = logger;
 }
Exemple #8
0
        public async Task TriageAsync(BuildKey buildKey, int modelTrackingIssueId)
        {
            var attempts = await TriageContextUtil
                           .GetModelBuildAttemptsQuery(buildKey)
                           .Include(x => x.ModelBuild)
                           .ToListAsync()
                           .ConfigureAwait(false);

            foreach (var attempt in attempts)
            {
                await TriageAsync(attempt.GetBuildAttemptKey(), modelTrackingIssueId).ConfigureAwait(false);
            }
        }
Exemple #9
0
        public async Task <BuildAttemptKey> EnsureModelInfoAsync(Build build, bool includeTests = true)
        {
            var buildInfo  = build.GetBuildResultInfo();
            var modelBuild = await TriageContextUtil.EnsureBuildAsync(buildInfo).ConfigureAwait(false);

            await TriageContextUtil.EnsureResultAsync(modelBuild, build).ConfigureAwait(false);

            var modelBuildAttempt = await EnsureTimeline().ConfigureAwait(false);

            if (includeTests)
            {
                await EnsureTestRuns().ConfigureAwait(false);
            }

            return(new BuildAttemptKey(new BuildKey(build), modelBuildAttempt.Attempt));

            async Task <ModelBuildAttempt> EnsureTimeline()
            {
                try
                {
                    var timeline = await Server.GetTimelineAsync(buildInfo.Project, buildInfo.Number).ConfigureAwait(false);

                    if (timeline is null)
                    {
                        Logger.LogWarning("No timeline");
                    }
                    else
                    {
                        return(await TriageContextUtil.EnsureBuildAttemptAsync(buildInfo, timeline).ConfigureAwait(false));
                    }
                }
                catch (Exception ex)
                {
                    Logger.LogWarning($"Error getting timeline: {ex.Message}");
                }

                return(await TriageContextUtil.EnsureBuildAttemptWithoutTimelineAsync(modelBuild, build).ConfigureAwait(false));
            }

            async Task EnsureTestRuns()
            {
                TestRun[] testRuns;
                try
                {
                    testRuns = await Server.ListTestRunsAsync(buildInfo.Project, buildInfo.Number).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    Logger.LogWarning($"Error getting test runs: {ex.Message}");
                    return;
                }

                var attempt = modelBuildAttempt?.Attempt ?? 1;

                foreach (var testRun in testRuns)
                {
                    await EnsureTestRun(testRun, attempt).ConfigureAwait(false);
                }
            }

            async Task EnsureTestRun(TestRun testRun, int attempt)
            {
                try
                {
                    var modelTestRun = await TriageContextUtil.FindModelTestRunAsync(modelBuild.GetBuildKey(), testRun.Id).ConfigureAwait(false);

                    if (modelTestRun is object)
                    {
                        return;
                    }

                    // TODO: Need to record when the maximum test results are exceeded. The limit here is to
                    // protect us from a catastrophic run that has say several million failures (this is a real
                    // possibility
                    const int maxTestCaseResultCount = 200;
                    var       dotNetTestRun          = await QueryUtil.GetDotNetTestRunAsync(
                        build,
                        testRun,
                        DotNetUtil.FailedTestOutcomes,
                        includeSubResults : true,
                        onError : ex => Logger.LogWarning($"Error fetching test data {ex.Message}")).ConfigureAwait(false);

                    if (dotNetTestRun.TestCaseResults.Count > maxTestCaseResultCount)
                    {
                        dotNetTestRun = new DotNetTestRun(
                            dotNetTestRun.TestRunInfo,
                            dotNetTestRun.TestCaseResults.Take(maxTestCaseResultCount).ToReadOnlyCollection());
                    }
                    var helixMap = await Server.GetHelixMapAsync(dotNetTestRun).ConfigureAwait(false);

                    await TriageContextUtil.EnsureTestRunAsync(modelBuild, attempt, dotNetTestRun, helixMap).ConfigureAwait(false);
                }
                catch (Exception ex)
                {
                    Logger.LogWarning($"Error uploading test run: {ex.Message}");
                    return;
                }
            }
        }
Exemple #10
0
        public async Task <string> GetStatusIssueTextAsync(IGitHubClient gitHubClient)
        {
            var header = new StringBuilder();
            var body   = new StringBuilder();
            var footer = new StringBuilder();

            header.AppendLine("## Overview");
            header.AppendLine("Please use these queries to discover issues");

            await BuildOne("Blocking CI", "blocking-clean-ci", DotNetUtil.GetDefinitionKeyFromFriendlyName("runtime")).ConfigureAwait(false);
            await BuildOne("Blocking Official Build", "blocking-official-build", DotNetUtil.GetDefinitionKeyFromFriendlyName("runtime-official")).ConfigureAwait(false);
            await BuildOne("Blocking CI Optional", "blocking-clean-ci-optional", DotNetUtil.GetDefinitionKeyFromFriendlyName("runtime"));
            await BuildOne("Blocking Outerloop", "blocking-outerloop", null);

            // Blank line to move past the table
            header.AppendLine("");
            BuildFooter();

            return(header.ToString() + body.ToString() + footer.ToString());

            void BuildFooter()
            {
                footer.AppendLine(@"## Goals

1. A minimum 95% passing rate for the `runtime` pipeline

## Resources

1. [runtime pipeline analytics](https://dnceng.visualstudio.com/public/_build?definitionId=686&view=ms.vss-pipelineanalytics-web.new-build-definition-pipeline-analytics-view-cardmetrics)");
            }

            async Task BuildOne(string title, string label, DefinitionKey?definitionKey)
            {
                header.AppendLine($"- [{title}](https://github.com/dotnet/runtime/issues?q=is%3Aopen+is%3Aissue+label%3A{label})");

                body.AppendLine($"## {title}");
                body.AppendLine("|Status|Issue|Build Count|");
                body.AppendLine("|---|---|---|");

                var query = (await DoSearchAsync(gitHubClient, label).ConfigureAwait(false))
                            .OrderByDescending(x => x.Count);

                foreach (var(issue, count) in query)
                {
                    var emoji = issue.Labels.Any(x => x.Name == "intermittent")
                        ? ":warning:"
                        : ":fire:";
                    var titleLimit = 75;
                    var issueText  = issue.Title.Length >= titleLimit
                        ? issue.Title.Substring(0, titleLimit - 5) + " ..."
                        : issue.Title;

                    var issueEntry = $"[{issueText}]({issue.HtmlUrl})";
                    var countStr   = count.HasValue ? count.ToString() : "N/A";

                    body.AppendLine($"|{emoji}|{issueEntry}|{countStr}|");
                }
            }

            async Task <List <(Octokit.Issue Issue, int?Count)> > DoSearchAsync(IGitHubClient gitHubClient, string label)
            {
                var request = new SearchIssuesRequest()
                {
                    Labels = new [] { label },
                    State  = ItemState.Open,
                    Type   = IssueTypeQualifier.Issue,
                    Repos  = { { "dotnet", "runtime" } },
                };
                var result = await gitHubClient.Search.SearchIssues(request).ConfigureAwait(false);

                var list = new List <(Octokit.Issue Issue, int?Count)>();

                foreach (var issue in result.Items)
                {
                    var count = await GetImpactedBuildsCountAsync(issue.GetIssueKey()).ConfigureAwait(false);

                    list.Add((issue, count));
                }
                return(list);
            }

            async Task <int?> GetImpactedBuildsCountAsync(GitHubIssueKey issueKey)
            {
                var modelTrackingIssue = await TriageContextUtil
                                         .GetModelTrackingIssuesQuery(issueKey)
                                         .FirstOrDefaultAsync()
                                         .ConfigureAwait(false);

                if (modelTrackingIssue is null)
                {
                    return(null);
                }

                var count = await Context
                            .ModelTrackingIssueResults
                            .Where(x => x.IsPresent && x.ModelTrackingIssueId == modelTrackingIssue.Id)
                            .CountAsync()
                            .ConfigureAwait(false);

                return(count);;
            }
        }
Exemple #11
0
 public static ModelBuildKind GetModelBuildKind(this ModelBuild modelBuild) =>
 TriageContextUtil.GetModelBuildKind(modelBuild.IsMergedPullRequest, modelBuild.PullRequestNumber);
Exemple #12
0
 private Task <ModelBuildAttempt> GetModelBuildAttemptAsync(BuildAttemptKey attemptKey) => TriageContextUtil
 .GetModelBuildAttemptQuery(attemptKey)
 .Include(x => x.ModelBuild)
 .SingleAsync();
Exemple #13
0
        public async Task TriageAsync(ModelBuildAttempt modelBuildAttempt, ModelTrackingIssue modelTrackingIssue)
        {
            if (modelBuildAttempt.ModelBuild is null)
            {
                throw new Exception("The attempt must include the build");
            }

            if (modelTrackingIssue.ModelBuildDefinitionId is { } definitionId&&
                definitionId != modelBuildAttempt.ModelBuild.ModelBuildDefinitionId)
            {
                return;
            }

            // Quick spot check to avoid doing extra work if we've already triaged this attempt against this
            // issue
            if (await WasTriaged().ConfigureAwait(false))
            {
                return;
            }

            bool isPresent;

            switch (modelTrackingIssue.TrackingKind)
            {
            case TrackingKind.Test:
                isPresent = await TriageTestAsync(modelBuildAttempt, modelTrackingIssue).ConfigureAwait(false);

                break;

            case TrackingKind.Timeline:
                isPresent = await TriageTimelineAsync(modelBuildAttempt, modelTrackingIssue).ConfigureAwait(false);

                break;

            case TrackingKind.HelixLogs:
                isPresent = await TriageHelixLogsAsync(modelBuildAttempt, modelTrackingIssue).ConfigureAwait(false);

                break;

#pragma warning disable 618
            case TrackingKind.HelixConsole:
            case TrackingKind.HelixRunClient:
                // TODO: delete this once the DB is cleaned up
                // These are old data types that we ignore.
                isPresent = false;
                break;

#pragma warning restore 618
            default:
                throw new Exception($"Unknown value {modelTrackingIssue.TrackingKind}");
            }

            var result = new ModelTrackingIssueResult()
            {
                ModelBuildAttempt  = modelBuildAttempt,
                ModelTrackingIssue = modelTrackingIssue,
                IsPresent          = isPresent
            };
            Context.ModelTrackingIssueResults.Add(result);

            // This can race with other attempts to associate issues here. That is okay though because triage attempts are
            // retried because they assume races with other operations can happen.
            if (isPresent && modelTrackingIssue.GetGitHubIssueKey() is { } issueKey)
            {
                await TriageContextUtil.EnsureGitHubIssueAsync(modelBuildAttempt.ModelBuild, issueKey, saveChanges : false).ConfigureAwait(false);
            }

            await Context.SaveChangesAsync().ConfigureAwait(false);

            async Task <bool> WasTriaged()
            {
                var query = Context
                            .ModelTrackingIssueResults
                            .Where(x => x.ModelBuildAttemptId == modelBuildAttempt.Id && x.ModelTrackingIssueId == modelTrackingIssue.Id);

                return(await query.AnyAsync().ConfigureAwait(false));
            }
        }