internal async Task <int> PrintSearchHelix(IEnumerable <string> args) { string text = null; var optionSet = new BuildSearchOptionSet() { { "v|value=", "text to search for", t => text = t }, }; ParseAll(optionSet, args); if (text is null) { Console.WriteLine("Must provide a text argument to search for"); optionSet.WriteOptionDescriptions(Console.Out); return(ExitFailure); } var badLogList = new List <string>(); var textRegex = new Regex(text, RegexOptions.Compiled | RegexOptions.IgnoreCase); var collection = await QueryUtil.ListBuildTestInfosAsync(optionSet); var found = collection .AsParallel() .Select(async b => (b.Build, await SearchBuild(b))); Console.WriteLine("|Build|Kind|Console Log|"); Console.WriteLine("|---|---|---|"); foreach (var task in found) { var(build, helixLogInfo) = await task; if (helixLogInfo is null) { continue; } var kind = "Rolling"; if (DevOpsUtil.TryGetPullRequestKey(build, out var pullRequestKey)) { kind = $"PR {pullRequestKey.PullRequestUri}"; } Console.WriteLine($"|[{build.Id}]({DevOpsUtil.GetBuildUri(build)})|{kind}|[console.log]({helixLogInfo.ConsoleUri})|"); } foreach (var line in badLogList) { Console.WriteLine(line); } return(ExitSuccess); async Task <HelixLogInfo> SearchBuild(BuildTestInfo buildTestInfo) { var build = buildTestInfo.Build; foreach (var workItem in buildTestInfo.GetHelixWorkItems()) { try { var logInfo = await GetHelixLogInfoAsync(workItem); if (logInfo.ConsoleUri is object) { using var stream = await Server.DownloadFileAsync(logInfo.ConsoleUri); if (IsMatch(stream, textRegex)) { return(logInfo); } } } catch { badLogList.Add($"Unable to download helix logs for {build.Id} {workItem.HelixInfo.JobId}"); } } return(null); }