Esempio n. 1
0
        private static async Task <IEnumerable <string> > GetInProgressBuildsAsync(IBuildHttpClient client, int pipelineId, Guid projectId)
        {
            IPagedList <WebApi.Build> builds = await client.GetBuildsAsync(
                projectId, definitions : new int[] { pipelineId }, statusFilter : WebApi.BuildStatus.InProgress);

            return(builds.Select(build => build.GetWebLink()));
        }
Esempio n. 2
0
        private async Task <bool> HasInProgressBuildAsync(IBuildHttpClient client, int pipelineId, Guid projectId)
        {
            IPagedList <Build> builds = await client.GetBuildsAsync(
                projectId, definitions : new int[] { pipelineId }, statusFilter : BuildStatus.InProgress);

            return(builds.Any());
        }
Esempio n. 3
0
        private async Task QueueBuildForStaleImages(Subscription subscription, IEnumerable <string> pathsToRebuild)
        {
            if (!pathsToRebuild.Any())
            {
                this.loggerService.WriteMessage($"All images for subscription '{subscription}' are using up-to-date base images. No rebuild necessary.");
                return;
            }

            string formattedPathsToRebuild = pathsToRebuild
                                             .Select(path => $"{ManifestFilterOptions.FormattedPathOption} '{path}'")
                                             .Aggregate((p1, p2) => $"{p1} {p2}");

            string parameters = "{\"" + subscription.PipelineTrigger.PathVariable + "\": \"" + formattedPathsToRebuild + "\"}";

            this.loggerService.WriteMessage($"Queueing build for subscription {subscription} with parameters {parameters}.");

            if (Options.IsDryRun)
            {
                return;
            }

            using (IVssConnection connection = this.connectionFactory.Create(
                       new Uri($"https://dev.azure.com/{Options.BuildOrganization}"),
                       new VssBasicCredential(String.Empty, Options.BuildPersonalAccessToken)))
                using (IProjectHttpClient projectHttpClient = connection.GetProjectHttpClient())
                    using (IBuildHttpClient client = connection.GetBuildHttpClient())
                    {
                        TeamProject project = await projectHttpClient.GetProjectAsync(Options.BuildProject);

                        Build build = new Build
                        {
                            Project = new TeamProjectReference {
                                Id = project.Id
                            },
                            Definition = new BuildDefinitionReference {
                                Id = subscription.PipelineTrigger.Id
                            },
                            SourceBranch = subscription.RepoInfo.Branch,
                            Parameters   = parameters
                        };

                        if (await HasInProgressBuildAsync(client, subscription.PipelineTrigger.Id, project.Id))
                        {
                            this.loggerService.WriteMessage(
                                $"An in-progress build was detected on the pipeline for subscription '{subscription.ToString()}'. Queueing the build will be skipped.");
                            return;
                        }

                        await client.QueueBuildAsync(build);
                    }
        }
Esempio n. 4
0
        private async Task QueueBuildForStaleImages(Subscription subscription, IEnumerable <string> pathsToRebuild)
        {
            if (!pathsToRebuild.Any())
            {
                _loggerService.WriteMessage($"All images for subscription '{subscription}' are using up-to-date base images. No rebuild necessary.");
                return;
            }

            string formattedPathsToRebuild = pathsToRebuild
                                             .Select(path => $"{ManifestFilterOptions.FormattedPathOption} '{path}'")
                                             .Aggregate((p1, p2) => $"{p1} {p2}");

            string parameters = "{\"" + subscription.PipelineTrigger.PathVariable + "\": \"" + formattedPathsToRebuild + "\"}";

            _loggerService.WriteMessage($"Queueing build for subscription {subscription} with parameters {parameters}.");

            if (Options.IsDryRun)
            {
                return;
            }

            (Uri baseUrl, VssCredentials credentials) = Options.AzdoOptions.GetConnectionDetails();

            using (IVssConnection connection = _connectionFactory.Create(baseUrl, credentials))
                using (IProjectHttpClient projectHttpClient = connection.GetProjectHttpClient())
                    using (IBuildHttpClient client = connection.GetBuildHttpClient())
                    {
                        TeamProject project = await projectHttpClient.GetProjectAsync(Options.AzdoOptions.Project);

                        Build build = new Build
                        {
                            Project = new TeamProjectReference {
                                Id = project.Id
                            },
                            Definition = new BuildDefinitionReference {
                                Id = subscription.PipelineTrigger.Id
                            },
                            SourceBranch = subscription.Manifest.Branch,
                            Parameters   = parameters
                        };

                        if (await HasInProgressBuildAsync(client, subscription.PipelineTrigger.Id, project.Id))
                        {
                            _loggerService.WriteMessage(
                                $"An in-progress build was detected on the pipeline for subscription '{subscription}'. Queueing the build will be skipped.");
                            return;
                        }

                        await client.QueueBuildAsync(build);
                    }
        }
Esempio n. 5
0
        private static async Task <(bool ShouldSkipBuild, IEnumerable <string> RecentFailedBuilds)> ShouldDisallowBuildDueToRecentFailuresAsync(
            IBuildHttpClient client, int pipelineId, Guid projectId)
        {
            List <WebApi.Build> autoBuilderBuilds = (await client.GetBuildsAsync(projectId, definitions: new int[] { pipelineId }))
                                                    .Where(build => build.Tags.Contains(AzdoTags.AutoBuilder))
                                                    .OrderByDescending(build => build.QueueTime)
                                                    .Take(BuildFailureLimit)
                                                    .ToList();

            if (autoBuilderBuilds.Count == BuildFailureLimit &&
                autoBuilderBuilds.All(build => build.Status == WebApi.BuildStatus.Completed && build.Result == WebApi.BuildResult.Failed))
            {
                return(true, autoBuilderBuilds.Select(build => build.GetWebLink()));
            }

            return(false, Enumerable.Empty <string>());
        }
Esempio n. 6
0
        private async Task QueueBuildForStaleImages(Subscription subscription, IEnumerable <string> pathsToRebuild)
        {
            if (!pathsToRebuild.Any())
            {
                _loggerService.WriteMessage($"All images for subscription '{subscription}' are using up-to-date base images. No rebuild necessary.");
                return;
            }

            string formattedPathsToRebuild = pathsToRebuild
                                             .Select(path => $"{CliHelper.FormatAlias(ManifestFilterOptionsBuilder.PathOptionName)} '{path}'")
                                             .Aggregate((p1, p2) => $"{p1} {p2}");

            string parameters = "{\"" + subscription.PipelineTrigger.PathVariable + "\": \"" + formattedPathsToRebuild + "\"}";

            _loggerService.WriteMessage($"Queueing build for subscription {subscription} with parameters {parameters}.");

            if (Options.IsDryRun)
            {
                return;
            }

            WebApi.Build?        queuedBuild        = null;
            Exception?           exception          = null;
            IEnumerable <string>?inProgressBuilds   = null;
            IEnumerable <string>?recentFailedBuilds = null;

            try
            {
                (Uri baseUrl, VssCredentials credentials) = Options.AzdoOptions.GetConnectionDetails();

                using (IVssConnection connection = _connectionFactory.Create(baseUrl, credentials))
                    using (IProjectHttpClient projectHttpClient = connection.GetProjectHttpClient())
                        using (IBuildHttpClient client = connection.GetBuildHttpClient())
                        {
                            TeamProject project = await projectHttpClient.GetProjectAsync(Options.AzdoOptions.Project);

                            WebApi.Build build = new()
                            {
                                Project = new TeamProjectReference {
                                    Id = project.Id
                                },
                                Definition = new WebApi.BuildDefinitionReference {
                                    Id = subscription.PipelineTrigger.Id
                                },
                                SourceBranch = subscription.Manifest.Branch,
                                Parameters   = parameters
                            };

                            inProgressBuilds = await GetInProgressBuildsAsync(client, subscription.PipelineTrigger.Id, project.Id);

                            if (!inProgressBuilds.Any())
                            {
                                (bool shouldDisallowBuild, IEnumerable <string> recentFailedBuildsLocal) =
                                    await ShouldDisallowBuildDueToRecentFailuresAsync(client, subscription.PipelineTrigger.Id, project.Id);

                                recentFailedBuilds = recentFailedBuildsLocal;
                                if (shouldDisallowBuild)
                                {
                                    _loggerService.WriteMessage(
                                        PipelineHelper.FormatErrorCommand("Unable to queue build due to too many recent build failures."));
                                }
                                else
                                {
                                    queuedBuild = await client.QueueBuildAsync(build);

                                    await client.AddBuildTagAsync(project.Id, queuedBuild.Id, AzdoTags.AutoBuilder);
                                }
                            }
                        }
            }
            catch (Exception ex)
            {
                exception = ex;
                throw;
            }
            finally
            {
                await LogAndNotifyResultsAsync(
                    subscription, pathsToRebuild, queuedBuild, exception, inProgressBuilds, recentFailedBuilds);
            }
        }
        public override async Task ExecuteAsync()
        {
            StringBuilder notificationMarkdown           = new();
            string        buildUrl                       = string.Empty;
            Dictionary <string, TaskResult?> taskResults = Options.TaskNames
                                                           .ToDictionary(name => name, name => (TaskResult?)null);
            Dictionary <string, string> buildParameters = new();
            BuildResult overallResult = BuildResult.Succeeded;
            BuildReason buildReason   = BuildReason.None;
            string?     correlatedQueueNotificationUrl = null;

            if (!Options.IsDryRun)
            {
                (Uri baseUrl, VssCredentials credentials) = Options.AzdoOptions.GetConnectionDetails();
                using (IVssConnection connection = _connectionFactory.Create(baseUrl, credentials))
                    using (IProjectHttpClient projectHttpClient = connection.GetProjectHttpClient())
                        using (IBuildHttpClient buildClient = connection.GetBuildHttpClient())
                        {
                            TeamProject project = await projectHttpClient.GetProjectAsync(Options.AzdoOptions.Project);

                            TeamFoundation.Build.WebApi.Build build = await buildClient.GetBuildAsync(project.Id, Options.BuildId);

                            buildUrl    = build.GetWebLink();
                            buildReason = build.Reason;

                            // Get the build's queue-time parameters
                            if (build.Parameters is not null)
                            {
                                JObject parametersJson = JsonConvert.DeserializeObject <JObject>(build.Parameters);
                                foreach (KeyValuePair <string, JToken?> pair in parametersJson)
                                {
                                    buildParameters.Add(pair.Key, pair.Value?.ToString() ?? string.Empty);
                                }
                            }

                            overallResult = await GetBuildTaskResultsAsync(taskResults, buildClient, project);

                            correlatedQueueNotificationUrl = await GetCorrelatedQueueNotificationUrlAsync();
                        }
            }

            notificationMarkdown.AppendLine($"# Publish Results");
            notificationMarkdown.AppendLine();

            WriteSummaryMarkdown(notificationMarkdown, buildUrl, overallResult, buildReason, correlatedQueueNotificationUrl);
            notificationMarkdown.AppendLine();

            WriteTaskStatusesMarkdown(taskResults, notificationMarkdown);
            notificationMarkdown.AppendLine();

            WriteBuildParameters(buildParameters, notificationMarkdown);
            notificationMarkdown.AppendLine();

            WriteImagesMarkdown(notificationMarkdown);

            await _notificationService.PostAsync(
                $"Publish Result - {Options.SourceRepo}/{Options.SourceBranch}",
                notificationMarkdown.ToString(),
                new string[]
            {
                NotificationLabels.Publish,
                NotificationLabels.GetRepoLocationLabel(Options.SourceRepo, Options.SourceBranch)
            }.AppendIf(NotificationLabels.Failure, () => overallResult == BuildResult.Failed),
                Options.GitOptions.GetRepoUrl().ToString(),
                Options.GitOptions.AuthToken,
                Options.IsDryRun);
        }
        private async Task <BuildResult> GetBuildTaskResultsAsync(Dictionary <string, TaskResult?> taskResults, IBuildHttpClient buildClient, TeamProject project)
        {
            BuildResult overallResult = BuildResult.None;
            Timeline    timeline      = await buildClient.GetBuildTimelineAsync(project.Id, Options.BuildId);

            foreach (string task in Options.TaskNames)
            {
                TimelineRecord?record = timeline.Records.FirstOrDefault(rec => rec.Name == task);
                if (record is null)
                {
                    throw new InvalidOperationException(
                              $"Build task with name '{task}' could not be found in the build timeline.");
                }

                taskResults[task] = record.Result;

                if (record.Result is not null)
                {
                    switch (record.Result.Value)
                    {
                    case TaskResult.Succeeded:
                        if (overallResult == BuildResult.None)
                        {
                            overallResult = BuildResult.Succeeded;
                        }
                        break;

                    case TaskResult.SucceededWithIssues:
                        if (overallResult == BuildResult.None || overallResult == BuildResult.Succeeded)
                        {
                            overallResult = BuildResult.PartiallySucceeded;
                        }
                        break;

                    case TaskResult.Failed:
                        overallResult = BuildResult.Failed;
                        break;

                    case TaskResult.Canceled:
                        overallResult = BuildResult.Canceled;
                        break;
                    }
                }
            }

            return(overallResult);
        }