示例#1
0
        public IActionResult List(string repository = null, string branch = null, int?channelId = null, bool?enabled = null)
        {
            IQueryable <Data.Models.DefaultChannel> query = _context.DefaultChannels.Include(dc => dc.Channel)
                                                            .AsNoTracking();

            if (!string.IsNullOrEmpty(repository))
            {
                query = query.Where(dc => dc.Repository == repository);
            }

            if (!string.IsNullOrEmpty(branch))
            {
                // Normalize the branch name to not include refs/heads
                string normalizedBranchName = GitHelpers.NormalizeBranchName(branch);
                query = query.Where(dc => dc.Branch == normalizedBranchName);
            }

            if (channelId.HasValue)
            {
                query = query.Where(dc => dc.ChannelId == channelId.Value);
            }

            if (enabled.HasValue)
            {
                query = query.Where(dc => dc.Enabled == enabled.Value);
            }

            List <DefaultChannel> results = query.AsEnumerable().Select(dc => new DefaultChannel(dc)).ToList();

            return(Ok(results));
        }
示例#2
0
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetRemote(_options, _options.Repository, Logger);

                _options.Branch = GitHelpers.NormalizeBranchName(_options.Branch);

                if (!(await UxHelpers.VerifyAndConfirmBranchExistsAsync(remote, _options.Repository, _options.Branch, !_options.NoConfirmation)))
                {
                    Console.WriteLine("Aborting default channel creation.");
                    return(Constants.ErrorCode);
                }

                await remote.AddDefaultChannelAsync(_options.Repository, _options.Branch, _options.Channel);

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to add a new default channel association.");
                return(Constants.ErrorCode);
            }
        }
示例#3
0
        /// <summary>
        ///     Verify that a branch exists in the specified repo and contains a version details file.
        ///     If it does not, optionally prompt the user to confirm that they wish to continue.
        /// </summary>
        /// <param name="remote">Remote</param>
        /// <param name="repo">Repository that the branch should be in</param>
        /// <param name="branch">Branch to check the existence of</param>
        /// <param name="prompt">Prompt the user to verify that they want to continue</param>
        /// <returns>True if the branch exists, prompting is not desired, or if the user confirms that they want to continue. False otherwise.</returns>
        public static async Task <bool> VerifyAndConfirmBranchExistsAsync(IRemote remote, string repo, string branch, bool prompt)
        {
            const string regexPrefix = "-regex:";

            // IRemote doesn't currently provide a way for enumerating all branches in a repo, and the purpose of supporting regex is to allow new ones to match
            // So in this case we'll just prompt
            if (branch.StartsWith(regexPrefix, StringComparison.InvariantCultureIgnoreCase))
            {
                Console.WriteLine($"Warning: Regular expression '{branch.Substring(regexPrefix.Length)}' will be used to match on branches in '{repo}'.");
                Console.WriteLine("To ensure dependency updates (where desired), please verify all branches matching this pattern contain an eng/Version.Details.xml file.");
                return(!prompt || PromptForYesNo("Continue?"));
            }

            try
            {
                branch = GitHelpers.NormalizeBranchName(branch);

                await remote.GetDependenciesAsync(repo, branch);
            }
            catch (DependencyFileNotFoundException)
            {
                Console.WriteLine($"Warning: Could not find an eng/Version.Details.xml at '{repo}@{branch}'. Dependency updates may not happen as expected.");
                if (prompt)
                {
                    return(PromptForYesNo("Continue?"));
                }
            }

            return(true);
        }
示例#4
0
        public async Task <IEnumerable <DefaultChannel> > GetDefaultChannelsAsync(string repository = null, string branch = null, string channel = null)
        {
            IQueryable <Data.Models.DefaultChannel> query = _context.DefaultChannels.Include(dc => dc.Channel)
                                                            .AsNoTracking();

            if (!string.IsNullOrEmpty(repository))
            {
                query = query.Where(dc => dc.Repository == repository);
            }

            if (!string.IsNullOrEmpty(branch))
            {
                // Normalize the branch name to not include refs/heads
                string normalizedBranchName = GitHelpers.NormalizeBranchName(branch);
                query = query.Where(dc => dc.Branch == normalizedBranchName);
            }

            if (!string.IsNullOrEmpty(channel))
            {
                query = query.Where(dc => dc.Channel.Name == channel);
            }

            var defaultChannels = await query.ToListAsync();

            return(defaultChannels.Select(dc => ToClientModelDefaultChannel(dc)));
        }
 public async Task WriteTimelineBuilds(IEnumerable <AugmentedBuild> augmentedBuilds)
 {
     await KustoHelpers.WriteDataToKustoInMemoryAsync(
         _ingest,
         _database,
         "TimelineBuilds",
         _logger,
         augmentedBuilds,
         b => new[]
     {
         new KustoValue("BuildId", b.Build.Id, KustoDataType.Int),
         new KustoValue("Status", b.Build.Status, KustoDataType.String),
         new KustoValue("Result", b.Build.Result, KustoDataType.String),
         new KustoValue("Repository", b.Build.Repository?.Name ?? b.Build.Repository?.Id, KustoDataType.String),
         new KustoValue("Reason", b.Build.Reason, KustoDataType.String),
         new KustoValue("BuildNumber", b.Build.BuildNumber, KustoDataType.String),
         new KustoValue("QueueTime", b.Build.QueueTime, KustoDataType.DateTime),
         new KustoValue("StartTime", b.Build.StartTime, KustoDataType.DateTime),
         new KustoValue("FinishTime", b.Build.FinishTime, KustoDataType.DateTime),
         new KustoValue("Project", b.Build.Project?.Name, KustoDataType.String),
         new KustoValue("DefinitionId", b.Build.Definition?.Id.ToString(), KustoDataType.String),
         new KustoValue("Definition", $"{b.Build.Definition?.Path}\\{b.Build.Definition?.Name}", KustoDataType.String),
         new KustoValue("SourceBranch", GitHelpers.NormalizeBranchName(b.Build.SourceBranch), KustoDataType.String),
         new KustoValue("TargetBranch", GitHelpers.NormalizeBranchName(b.TargetBranch), KustoDataType.String),
     });
 }
示例#6
0
 public bool SubcriptionFilter(Subscription subscription, IEnumerable <DefaultChannel> defaultChannels)
 {
     return(SubscriptionParameterMatches(TargetRepository, subscription.TargetRepository) &&
            SubscriptionParameterMatches(GitHelpers.NormalizeBranchName(TargetBranch), subscription.TargetBranch) &&
            SubscriptionParameterMatches(SourceRepository, subscription.SourceRepository) &&
            SubscriptionParameterMatches(Channel, subscription.Channel.Name) &&
            SubscriptionEnabledParameterMatches(subscription) &&
            SubscriptionBatchableParameterMatches(subscription) &&
            SubscriptionIdsParameterMatches(subscription) &&
            SubscriptionFrequenciesParameterMatches(subscription) &&
            SubscriptionDefaultChannelTargetParameterMatches(subscription, defaultChannels));
 }
        /// <summary>
        ///     Resolve channel based on the input options. If no channel could be resolved
        ///     based on the input options, returns null.
        /// </summary>
        /// <returns>Default channel or null</returns>
        protected async Task <DefaultChannel> ResolveSingleChannel()
        {
            IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

            IEnumerable <DefaultChannel> potentialDefaultChannels = await remote.GetDefaultChannelsAsync();

            // User should have supplied id or a combo of the channel name, repo, and branch.
            if (_options.Id != -1)
            {
                DefaultChannel defaultChannel = potentialDefaultChannels.SingleOrDefault(d => d.Id == _options.Id);
                if (defaultChannel == null)
                {
                    Console.WriteLine($"Could not find a default channel with id {_options.Id}");
                }
                return(defaultChannel);
            }
            else if (string.IsNullOrEmpty(_options.Repository) ||
                     string.IsNullOrEmpty(_options.Channel) ||
                     string.IsNullOrEmpty(_options.Branch))
            {
                Console.WriteLine("Please specify either the default channel id with --id or a combination of --channel, --branch and --repo");
                return(null);
            }

            // Otherwise, filter based on the other inputs. If more than one resolves, then print the possible
            // matches and return null
            var matchingChannels = potentialDefaultChannels.Where(d =>
            {
                return((string.IsNullOrEmpty(_options.Repository) || d.Repository.Contains(_options.Repository, StringComparison.OrdinalIgnoreCase)) &&
                       (string.IsNullOrEmpty(_options.Channel) || d.Channel.Name.Contains(_options.Channel, StringComparison.OrdinalIgnoreCase)) &&
                       (string.IsNullOrEmpty(_options.Branch) || d.Branch.Contains(GitHelpers.NormalizeBranchName(_options.Branch), StringComparison.OrdinalIgnoreCase)));
            });

            if (!matchingChannels.Any())
            {
                Console.WriteLine($"No channels found matching the specified criteria.");
                return(null);
            }
            else if (matchingChannels.Count() != 1)
            {
                Console.WriteLine($"More than one channel matching the specified criteria. Please change your options to be more specific.");
                foreach (DefaultChannel defaultChannel in matchingChannels)
                {
                    Console.WriteLine($"    {UxHelpers.GetDefaultChannelDescriptionString(defaultChannel)}");
                }
                return(null);
            }
            else
            {
                return(matchingChannels.Single());
            }
        }
        public IActionResult ListRepositories(string repository = null, string branch = null)
        {
            IQueryable <Data.Models.RepositoryBranch> query = Context.RepositoryBranches;

            if (!string.IsNullOrEmpty(repository))
            {
                query = query.Where(r => r.RepositoryName == repository);
            }

            if (!string.IsNullOrEmpty(branch))
            {
                string normalizedBranchName = GitHelpers.NormalizeBranchName(branch);
                query = query.Where(r => r.BranchName == normalizedBranchName);
            }

            return(Ok(query.AsEnumerable().Select(r => new RepositoryBranch(r)).ToList()));
        }
示例#9
0
        public override IActionResult List(string repository = null, string branch = null, int?channelId = null, bool?enabled = null)
        {
            IQueryable <Data.Models.DefaultChannel> query = _context.DefaultChannels.Include(dc => dc.Channel)
                                                            .AsNoTracking();

            if (!string.IsNullOrEmpty(repository))
            {
                query = query.Where(dc => dc.Repository == repository);
            }

            if (channelId.HasValue)
            {
                query = query.Where(dc => dc.ChannelId == channelId.Value);
            }

            if (enabled.HasValue)
            {
                query = query.Where(dc => dc.Enabled == enabled.Value);
            }

            List <DefaultChannel> results = query.AsEnumerable().Select(dc => new DefaultChannel(dc)).ToList();

            if (!string.IsNullOrEmpty(branch))
            {
                List <DefaultChannel> branchFilteredResults = new List <DefaultChannel>();
                foreach (DefaultChannel defaultChannel in results)
                {
                    // Branch name expressed as a regular expression: must start with '-regex:' and have at least one more character.
                    // - Skips NormalizeBranchName here because internally everything is stored without that.
                    //   If there's a pattern of users doing '-regex:/refs/heads/release.*' this could be revisited.
                    if (defaultChannel.Branch.StartsWith(_regexBranchPrefix, StringComparison.InvariantCultureIgnoreCase) &&
                        defaultChannel.Branch.Length > _regexBranchPrefix.Length &&
                        new Regex(defaultChannel.Branch.Substring(_regexBranchPrefix.Length)).IsMatch(branch))
                    {
                        branchFilteredResults.Add(defaultChannel);
                    }
                    else if (defaultChannel.Branch == GitHelpers.NormalizeBranchName(branch))
                    {
                        branchFilteredResults.Add(defaultChannel);
                    }
                }
                return(Ok(branchFilteredResults));
            }
            return(Ok(results));
        }
示例#10
0
        /// <summary>
        ///     Verify that a branch exists in the specified repo and contains a version details file.
        ///     If it does not, optionally prompt the user to confirm that they wish to continue.
        /// </summary>
        /// <param name="remote">Remote</param>
        /// <param name="repo">Repository that the branch should be in</param>
        /// <param name="branch">Branch to check the existence of</param>
        /// <param name="prompt">Prompt the user to verify that they want to continue</param>
        /// <returns>True if the branch exists, prompting is not desired, or if the user confirms that they want to continue. False otherwise.</returns>
        public static async Task <bool> VerifyAndConfirmBranchExistsAsync(IRemote remote, string repo, string branch, bool prompt)
        {
            try
            {
                branch = GitHelpers.NormalizeBranchName(branch);

                await remote.GetDependenciesAsync(repo, branch);
            }
            catch (DependencyFileNotFoundException)
            {
                Console.WriteLine($"Warning: Could not find an eng/Version.Details.xml at '{repo}@{branch}'. Dependency updates may not happen as expected.");
                if (prompt)
                {
                    return(PromptForYesNo("Continue?"));
                }
            }

            return(true);
        }
        /// <summary>
        /// Implements the 'add-subscription' operation
        /// </summary>
        /// <param name="options"></param>
        public override async Task <int> ExecuteAsync()
        {
            IRemote remote = RemoteFactory.GetBarOnlyRemote(_options, Logger);

            if (_options.IgnoreChecks.Count() > 0 && !_options.AllChecksSuccessfulMergePolicy)
            {
                Console.WriteLine($"--ignore-checks must be combined with --all-checks-passed");
                return(Constants.ErrorCode);
            }

            // Parse the merge policies
            List <MergePolicy> mergePolicies = new List <MergePolicy>();

            if (_options.NoExtraCommitsMergePolicy)
            {
                mergePolicies.Add(
                    new MergePolicy
                {
                    Name = MergePolicyConstants.NoExtraCommitsMergePolicyName
                });
            }

            if (_options.AllChecksSuccessfulMergePolicy)
            {
                mergePolicies.Add(
                    new MergePolicy
                {
                    Name       = MergePolicyConstants.AllCheckSuccessfulMergePolicyName,
                    Properties = ImmutableDictionary.Create <string, JToken>()
                                 .Add(MergePolicyConstants.IgnoreChecksMergePolicyPropertyName, JToken.FromObject(_options.IgnoreChecks))
                });
            }

            if (_options.NoRequestedChangesMergePolicy)
            {
                mergePolicies.Add(
                    new MergePolicy
                {
                    Name       = MergePolicyConstants.NoRequestedChangesMergePolicyName,
                    Properties = ImmutableDictionary.Create <string, JToken>()
                });
            }

            if (_options.StandardAutoMergePolicies)
            {
                mergePolicies.Add(
                    new MergePolicy
                {
                    Name       = MergePolicyConstants.StandardMergePolicyName,
                    Properties = ImmutableDictionary.Create <string, JToken>()
                });
            }

            if (_options.Batchable && mergePolicies.Count > 0)
            {
                Console.WriteLine("Batchable subscriptions cannot be combined with merge policies. " +
                                  "Merge policies are specified at a repository+branch level.");
                return(Constants.ErrorCode);
            }

            string channel          = _options.Channel;
            string sourceRepository = _options.SourceRepository;
            string targetRepository = _options.TargetRepository;
            string targetBranch     = GitHelpers.NormalizeBranchName(_options.TargetBranch);
            string updateFrequency  = _options.UpdateFrequency;
            bool   batchable        = _options.Batchable;

            // If in quiet (non-interactive mode), ensure that all options were passed, then
            // just call the remote API
            if (_options.Quiet && !_options.ReadStandardIn)
            {
                if (string.IsNullOrEmpty(channel) ||
                    string.IsNullOrEmpty(sourceRepository) ||
                    string.IsNullOrEmpty(targetRepository) ||
                    string.IsNullOrEmpty(targetBranch) ||
                    string.IsNullOrEmpty(updateFrequency) ||
                    !Constants.AvailableFrequencies.Contains(updateFrequency, StringComparer.OrdinalIgnoreCase))
                {
                    Logger.LogError($"Missing input parameters for the subscription. Please see command help or remove --quiet/-q for interactive mode");
                    return(Constants.ErrorCode);
                }
            }
            else
            {
                // Grab existing subscriptions to get suggested values.
                // TODO: When this becomes paged, set a max number of results to avoid
                // pulling too much.
                var suggestedRepos    = remote.GetSubscriptionsAsync();
                var suggestedChannels = remote.GetChannelsAsync();

                // Help the user along with a form.  We'll use the API to gather suggested values
                // from existing subscriptions based on the input parameters.
                AddSubscriptionPopUp addSubscriptionPopup =
                    new AddSubscriptionPopUp("add-subscription/add-subscription-todo",
                                             Logger,
                                             channel,
                                             sourceRepository,
                                             targetRepository,
                                             targetBranch,
                                             updateFrequency,
                                             batchable,
                                             mergePolicies,
                                             (await suggestedChannels).Select(suggestedChannel => suggestedChannel.Name),
                                             (await suggestedRepos).SelectMany(subscription => new List <string> {
                    subscription.SourceRepository, subscription.TargetRepository
                }).ToHashSet(),
                                             Constants.AvailableFrequencies,
                                             Constants.AvailableMergePolicyYamlHelp);

                UxManager uxManager = new UxManager(_options.GitLocation, Logger);
                int       exitCode  = _options.ReadStandardIn ? uxManager.ReadFromStdIn(addSubscriptionPopup) : uxManager.PopUp(addSubscriptionPopup);
                if (exitCode != Constants.SuccessCode)
                {
                    return(exitCode);
                }
                channel          = addSubscriptionPopup.Channel;
                sourceRepository = addSubscriptionPopup.SourceRepository;
                targetRepository = addSubscriptionPopup.TargetRepository;
                targetBranch     = addSubscriptionPopup.TargetBranch;
                updateFrequency  = addSubscriptionPopup.UpdateFrequency;
                mergePolicies    = addSubscriptionPopup.MergePolicies;
                batchable        = addSubscriptionPopup.Batchable;
            }

            try
            {
                // If we are about to add a batchable subscription and the merge policies are empty for the
                // target repo/branch, warn the user.
                if (batchable)
                {
                    var existingMergePolicies = await remote.GetRepositoryMergePoliciesAsync(targetRepository, targetBranch);

                    if (!existingMergePolicies.Any())
                    {
                        Console.WriteLine("Warning: Batchable subscription doesn't have any repository merge policies. " +
                                          "PRs will not be auto-merged.");
                        Console.WriteLine($"Please use 'darc set-repository-policies --repo {targetRepository} --branch {targetBranch}' " +
                                          $"to set policies.{Environment.NewLine}");
                    }
                }

                // Verify the target
                IRemote targetVerifyRemote = RemoteFactory.GetRemote(_options, targetRepository, Logger);
                if (!(await UxHelpers.VerifyAndConfirmBranchExistsAsync(targetVerifyRemote, targetRepository, targetBranch, !_options.Quiet)))
                {
                    Console.WriteLine("Aborting subscription creation.");
                    return(Constants.ErrorCode);
                }

                // Verify the source.
                IRemote sourceVerifyRemote = RemoteFactory.GetRemote(_options, sourceRepository, Logger);
                if (!(await UxHelpers.VerifyAndConfirmRepositoryExistsAsync(sourceVerifyRemote, sourceRepository, !_options.Quiet)))
                {
                    Console.WriteLine("Aborting subscription creation.");
                    return(Constants.ErrorCode);
                }

                var newSubscription = await remote.CreateSubscriptionAsync(channel,
                                                                           sourceRepository,
                                                                           targetRepository,
                                                                           targetBranch,
                                                                           updateFrequency,
                                                                           batchable,
                                                                           mergePolicies);

                Console.WriteLine($"Successfully created new subscription with id '{newSubscription.Id}'.");

                // Prompt the user to trigger the subscription unless they have explicitly disallowed it
                if (!_options.NoTriggerOnCreate)
                {
                    bool triggerAutomatically = _options.TriggerOnCreate || UxHelpers.PromptForYesNo("Trigger this subscription immediately?");
                    if (triggerAutomatically)
                    {
                        await remote.TriggerSubscriptionAsync(newSubscription.Id.ToString());

                        Console.WriteLine($"Subscription '{newSubscription.Id}' triggered.");
                    }
                }

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (RestApiException e) when(e.Response.Status == (int)System.Net.HttpStatusCode.BadRequest)
            {
                // Could have been some kind of validation error (e.g. channel doesn't exist)
                Logger.LogError($"Failed to create subscription: {e.Response.Content}");
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, $"Failed to create subscription.");
                return(Constants.ErrorCode);
            }
        }
示例#12
0
        private async Task RunProject(
            AzureDevOpsClient azureServer,
            string project,
            int buildBatchSize,
            AzureDevOpsTimelineOptions options,
            CancellationToken cancellationToken)
        {
            DateTimeOffset latest;

            try
            {
                using (ICslQueryProvider query =
                           KustoClientFactory.CreateCslQueryProvider(options.KustoQueryConnectionString))
                    using (IDataReader result = await query.ExecuteQueryAsync(
                               options.KustoDatabase,
                               // This isn't use controlled, so I'm not worried about the Kusto injection
                               $"TimelineBuilds | where Project == '{project}' | summarize max(FinishTime)",
                               new ClientRequestProperties()
                               ))
                    {
                        if (!result.Read())
                        {
                            latest = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(30));
                            _logger.LogWarning($"No previous time found, using {latest.LocalDateTime:O}");
                        }
                        else
                        {
                            latest = result.GetDateTime(0);
                            _logger.LogInformation($"... fetched previous time of {latest.LocalDateTime:O}");
                        }
                    }
            }
            catch (SemanticException e) when(e.SemanticErrors == "'where' operator: Failed to resolve column or scalar expression named 'Project'")
            {
                // The Project column isn't there, we probably reinitalized the tables
                latest = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(30));
                _logger.LogWarning($"No table column 'Project' found, assumed reinialization: using {latest.LocalDateTime:O}");
            }

            _logger.LogInformation("Reading project {project}", project);
            Build[] builds = await GetBuildsAsync(azureServer, project, latest, buildBatchSize, cancellationToken);

            _logger.LogTrace("... found {builds} builds...", builds.Length);

            if (builds.Length == 0)
            {
                _logger.LogTrace("No work to do");
                return;
            }

            List <(int buildId, BuildRequestValidationResult validationResult)> validationResults = builds
                                                                                                    .SelectMany(
                build => build.ValidationResults,
                (build, validationResult) => (build.Id, validationResult))
                                                                                                    .ToList();

            _logger.LogTrace("Fetching timeline...");
            Dictionary <Build, Task <Timeline> > tasks = builds
                                                         .ToDictionary(
                build => build,
                build => azureServer.GetTimelineAsync(project, build.Id, cancellationToken)
                );

            await Task.WhenAll(tasks.Select(s => s.Value));

            _logger.LogTrace("... finished timeline");

            var records         = new List <AugmentedTimelineRecord>();
            var issues          = new List <AugmentedTimelineIssue>();
            var augmentedBuilds = new List <AugmentedBuild>();

            _logger.LogTrace("Aggregating results...");
            foreach ((Build build, Task <Timeline> timelineTask) in tasks)
            {
                string targetBranch = "";

                try
                {
                    if (build.Reason == "pullRequest")
                    {
                        targetBranch = (string)JObject.Parse(build.Parameters)["system.pullRequest.targetBranch"];
                    }
                }
                catch (JsonReaderException e)
                {
                    _logger.LogError(e.ToString());
                }

                augmentedBuilds.Add(new AugmentedBuild(build, targetBranch));

                Timeline timeline = await timelineTask;
                if (timeline?.Records == null)
                {
                    continue;
                }

                var recordCache =
                    new Dictionary <string, AugmentedTimelineRecord>();
                var issueCache = new List <AugmentedTimelineIssue>();
                foreach (TimelineRecord record in timeline.Records)
                {
                    var augRecord = new AugmentedTimelineRecord(build.Id, record);
                    recordCache.Add(record.Id, augRecord);
                    records.Add(augRecord);
                    if (record.Issues == null)
                    {
                        continue;
                    }

                    for (int iIssue = 0; iIssue < record.Issues.Length; iIssue++)
                    {
                        var augIssue =
                            new AugmentedTimelineIssue(build.Id, record.Id, iIssue, record.Issues[iIssue]);
                        augIssue.Bucket = GetBucket(augIssue);
                        issueCache.Add(augIssue);
                        issues.Add(augIssue);
                    }
                }

                foreach (AugmentedTimelineRecord record in recordCache.Values)
                {
                    FillAugmentedOrder(record, recordCache);
                }

                foreach (AugmentedTimelineIssue issue in issueCache)
                {
                    if (recordCache.TryGetValue(issue.RecordId, out AugmentedTimelineRecord record))
                    {
                        issue.AugmentedIndex = record.AugmentedOrder + "." + issue.Index.ToString("D3");
                    }
                    else
                    {
                        issue.AugmentedIndex = "999." + issue.Index.ToString("D3");
                    }
                }
            }

            if (string.IsNullOrEmpty(options.KustoIngestConnectionString))
            {
                _logger.LogError("No KustoIngestConnectionString set");
                return;
            }

            IKustoIngestClient ingest =
                KustoIngestFactory.CreateQueuedIngestClient(options.KustoIngestConnectionString);

            _logger.LogInformation("Saving TimelineBuilds...");
            await KustoHelpers.WriteDataToKustoInMemoryAsync(
                ingest,
                options.KustoDatabase,
                "TimelineBuilds",
                _logger,
                augmentedBuilds,
                b => new[]
            {
                new KustoValue("BuildId", b.Build.Id, KustoDataType.Int),
                new KustoValue("Status", b.Build.Status, KustoDataType.String),
                new KustoValue("Result", b.Build.Result, KustoDataType.String),
                new KustoValue("Repository", b.Build.Repository?.Name ?? b.Build.Repository?.Id, KustoDataType.String),
                new KustoValue("Reason", b.Build.Reason, KustoDataType.String),
                new KustoValue("BuildNumber", b.Build.BuildNumber, KustoDataType.String),
                new KustoValue("QueueTime", b.Build.QueueTime, KustoDataType.DateTime),
                new KustoValue("StartTime", b.Build.StartTime, KustoDataType.DateTime),
                new KustoValue("FinishTime", b.Build.FinishTime, KustoDataType.DateTime),
                new KustoValue("Project", b.Build.Project?.Name, KustoDataType.String),
                new KustoValue("DefinitionId", b.Build.Definition?.Id.ToString(), KustoDataType.String),
                new KustoValue("Definition", $"{b.Build.Definition?.Path}\\{b.Build.Definition?.Name}", KustoDataType.String),
                new KustoValue("SourceBranch", GitHelpers.NormalizeBranchName(b.Build.SourceBranch), KustoDataType.String),
                new KustoValue("TargetBranch", GitHelpers.NormalizeBranchName(b.TargetBranch), KustoDataType.String),
            });

            _logger.LogInformation("Saving TimelineValidationMessages...");
            await KustoHelpers.WriteDataToKustoInMemoryAsync(
                ingest,
                options.KustoDatabase,
                "TimelineIssues",
                _logger,
                validationResults,
                b => new[]
            {
                new KustoValue("BuildId", b.buildId, KustoDataType.Int),
                new KustoValue("RecordId", null, KustoDataType.String),
                new KustoValue("Index", null, KustoDataType.Int),
                new KustoValue("Path", null, KustoDataType.String),
                new KustoValue("Type", b.validationResult.Result, KustoDataType.String),
                new KustoValue("Category", "ValidationResult", KustoDataType.String),
                new KustoValue("Message", b.validationResult.Message, KustoDataType.String),
                new KustoValue("Bucket", "ValidationResult", KustoDataType.String),
            });

            _logger.LogInformation("Saving TimelineRecords...");
            await KustoHelpers.WriteDataToKustoInMemoryAsync(
                ingest,
                options.KustoDatabase,
                "TimelineRecords",
                _logger,
                records,
                b => new[]
            {
                new KustoValue("BuildId", b.BuildId, KustoDataType.Int),
                new KustoValue("RecordId", b.Raw.Id, KustoDataType.String),
                new KustoValue("Order", b.Raw.Order, KustoDataType.Int),
                new KustoValue("Path", b.AugmentedOrder, KustoDataType.String),
                new KustoValue("ParentId", b.Raw.ParentId, KustoDataType.String),
                new KustoValue("Name", b.Raw.Name, KustoDataType.String),
                new KustoValue("StartTime", b.Raw.StartTime, KustoDataType.DateTime),
                new KustoValue("FinishTime", b.Raw.FinishTime, KustoDataType.DateTime),
                new KustoValue("Result", b.Raw.Result, KustoDataType.String),
                new KustoValue("ResultCode", b.Raw.ResultCode, KustoDataType.String),
                new KustoValue("ChangeId", b.Raw.ChangeId, KustoDataType.Int),
                new KustoValue("LastModified", b.Raw.LastModified, KustoDataType.DateTime),
                new KustoValue("WorkerName", b.Raw.WorkerName, KustoDataType.String),
                new KustoValue("Details", b.Raw.Details?.Url, KustoDataType.String),
                new KustoValue("ErrorCount", b.Raw.ErrorCount, KustoDataType.Int),
                new KustoValue("WarningCount", b.Raw.WarningCount, KustoDataType.Int),
                new KustoValue("Url", b.Raw.Url, KustoDataType.String),
                new KustoValue("LogId", b.Raw.Log?.Id, KustoDataType.Int),
                new KustoValue("LogUri", b.Raw.Log?.Url, KustoDataType.String),
                new KustoValue("TaskId", b.Raw.Task?.Id, KustoDataType.Int),
                new KustoValue("TaskName", b.Raw.Task?.Name, KustoDataType.String),
                new KustoValue("TaskVersion", b.Raw.Task?.Version, KustoDataType.String),
                new KustoValue("Attempt", b.Raw.Attempt, KustoDataType.Int),
            });

            _logger.LogInformation("Saving TimelineIssues...");
            await KustoHelpers.WriteDataToKustoInMemoryAsync(
                ingest,
                options.KustoDatabase,
                "TimelineIssues",
                _logger,
                issues,
                b => new[]
            {
                new KustoValue("BuildId", b.BuildId, KustoDataType.Int),
                new KustoValue("RecordId", b.RecordId, KustoDataType.String),
                new KustoValue("Index", b.Index, KustoDataType.Int),
                new KustoValue("Path", b.AugmentedIndex, KustoDataType.String),
                new KustoValue("Type", b.Raw.Type, KustoDataType.String),
                new KustoValue("Category", b.Raw.Category, KustoDataType.String),
                new KustoValue("Message", b.Raw.Message, KustoDataType.String),
                new KustoValue("Bucket", b.Bucket, KustoDataType.String),
            });
        }
示例#13
0
        public override async Task <int> ExecuteAsync()
        {
            try
            {
                IRemote remote = RemoteFactory.GetRemote(_options, _options.Repository, Logger);

                // Users can ignore the flag and pass in -regex: but to prevent typos we'll avoid that.
                _options.Branch = _options.UseBranchAsRegex ? $"-regex:{_options.Branch}" : GitHelpers.NormalizeBranchName(_options.Branch);

                if (!(await UxHelpers.VerifyAndConfirmBranchExistsAsync(remote, _options.Repository, _options.Branch, !_options.NoConfirmation)))
                {
                    Console.WriteLine("Aborting default channel creation.");
                    return(Constants.ErrorCode);
                }

                await remote.AddDefaultChannelAsync(_options.Repository, _options.Branch, _options.Channel);

                return(Constants.SuccessCode);
            }
            catch (AuthenticationException e)
            {
                Console.WriteLine(e.Message);
                return(Constants.ErrorCode);
            }
            catch (Exception e)
            {
                Logger.LogError(e, "Error: Failed to add a new default channel association.");
                return(Constants.ErrorCode);
            }
        }