public async Task <ActionResult <CommentMoveResult> > MoveComment( string toOwnerName, string toRepoName, [FromBody] CommentMoveRequest commentMoveRequest) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); try { await gitHub.Issue.Comment.Create(toOwnerName, toRepoName, commentMoveRequest.IssueNumber, commentMoveRequest.Text); return(Ok( new CommentMoveResult { })); } catch (Exception ex) { return(BadRequest( new CommentMoveResult { Exception = ex, })); } }
public async Task <IActionResult> GetMoveData(string fromOwnerName, string fromRepoName, string fromIssueNumber) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); if (!int.TryParse(fromIssueNumber, out var fromIssueNumberInt)) { return(BadRequest( new IssueMoveData { ErrorMessage = $"Issue number is invalid: {fromIssueNumber}", })); } var fromIssue = await gitHub.Issue.Get(fromOwnerName, fromRepoName, fromIssueNumberInt); var comments = (await gitHub.Issue.Comment.GetAllForIssue(fromOwnerName, fromRepoName, fromIssueNumberInt)) .Select(issueComment => new CommentData { Author = issueComment.User.Login, Text = issueComment.Body, Date = issueComment.CreatedAt, }) .ToList(); try { return(Ok( new IssueMoveData { RepoOwner = fromOwnerName, RepoName = fromRepoName, State = GetIssueState(fromIssue.State.Value), HtmlUrl = fromIssue.HtmlUrl, IsPullRequest = fromIssue.PullRequest != null, Title = fromIssue.Title, Number = fromIssue.Number, Author = fromIssue.User.Login, Body = fromIssue.Body, Assignees = fromIssue.Assignees.Select(a => a.Login).ToArray(), CreatedDate = fromIssue.CreatedAt, Milestone = fromIssue.Milestone?.Title, Labels = fromIssue.Labels.Select(l => new LabelData { Text = l.Name, Color = l.Color, }).ToList(), Comments = comments, })); } catch (Exception ex) { return(BadRequest( new IssueMoveData { Exception = ex, })); } }
/// <summary> /// Commits pre-release to GitHub converting it to Latest Release. /// </summary> /// <param name="settings">Zombie Settings.</param> public async void PushReleaseToGitHub(ZombieSettings settings) { try { var client = new GitHubClient(new ProductHeaderValue("Zombie")); var tokenAuth = new Credentials(settings.AccessToken); client.Credentials = tokenAuth; var segments = GitHubUtils.ParseUrl(settings.Address); var unused = await client.Repository.Release.Edit(segments["owner"], segments["repo"], settings.LatestRelease.Id, new ReleaseUpdate { Body = settings.LatestRelease.Body, Draft = false, Name = settings.LatestRelease.Name, Prerelease = false, TagName = settings.LatestRelease.TagName, TargetCommitish = "master" }); Messenger.Default.Send(new UpdateStatus { Message = "Zombie changed your Release!" }); } catch (Exception e) { _logger.Fatal(e.Message); Messenger.Default.Send(new UpdateStatus { Message = "Failed to push updates to your release!" }); } }
public async Task <ActionResult <IssueCloseResult> > CloseIssue( string fromOwnerName, string fromRepoName, [FromBody] IssueCloseRequest issueCloseRequest) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); try { await gitHub.Issue.Update(fromOwnerName, fromRepoName, issueCloseRequest.IssueNumber, new IssueUpdate { State = ItemState.Closed, }); return(Ok( new IssueCloseResult { })); } catch (Exception ex) { return(BadRequest( new IssueCloseResult { Exception = ex, })); } }
public async Task <IActionResult> GetRepoData(string toOwnerName, string toRepoName) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); var repo = await gitHub.Repository.Get(toOwnerName, toRepoName); try { return(Ok( new RepoMoveData { Owner = repo.Owner?.Login, Repo = repo.Name, OpenIssueCount = repo.OpenIssuesCount, })); } catch (Exception ex) { return(BadRequest( new RepoMoveData { Exception = ex, })); } }
public async Task <IActionResult> ApplyLabel(string owner, string repo, int issueNumber, string prediction) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); var issue = await gitHub.Issue.Get(owner, repo, issueNumber); var issueUpdate = new IssueUpdate { Milestone = issue.Milestone?.Number // Have to re-set milestone because otherwise it gets cleared out. See https://github.com/octokit/octokit.net/issues/1927 }; issueUpdate.AddLabel(prediction); // Add all existing labels to the update so that they don't get removed foreach (var label in issue.Labels) { issueUpdate.AddLabel(label.Name); } await gitHub.Issue.Update(owner, repo, issueNumber, issueUpdate); // Because GitHub search queries can show stale data, add a cache entry to // indicate this issue should be hidden for a while because it was just labeled. _memoryCache.Set( GetIssueHiderCacheKey(owner, repo, issueNumber), 0, // no data is needed; the existence of the cache key is what counts new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30), }); return(RedirectToPage("/MikLabel")); }
public static async Task <string> updateStack([ActivityTrigger] UpdateBaseImageRequest request, ILogger log) { SecretsUtils _secretsUtils = new SecretsUtils(); await _secretsUtils.GetSecrets(); GitHubUtils _githubUtils = new GitHubUtils(_secretsUtils._gitToken); String timeStamp = DateTime.Now.ToString("yyyyMMddHHmmss"); String random = new Random().Next(0, 9999).ToString(); String prname = String.Format("blimp{0}{1}", timeStamp, random); String parent = String.Format("D:\\local\\Temp\\blimp{0}{1}", timeStamp, random); String repoUrl = String.Format("https://github.com/Azure-App-Service/{0}-template.git", request.stack); String repoName = String.Format("{0}-template", request.stack); _githubUtils.CreateDir(parent); String localTemplateRepoPath = String.Format("{0}\\{1}", parent, repoName); _githubUtils.Clone(repoUrl, localTemplateRepoPath, "dev"); _githubUtils.Checkout(localTemplateRepoPath, prname); // edit the configs await updateConfig(String.Format("{0}\\{1}", localTemplateRepoPath, "blessedImageConfig-dev.json"), request.NewBaseImage); await updateConfig(String.Format("{0}\\{1}", localTemplateRepoPath, "blessedImageConfig-master.json"), request.NewBaseImage); await updateConfig(String.Format("{0}\\{1}", localTemplateRepoPath, "blessedImageConfig-temp.json"), request.NewBaseImage); await updateConfig(String.Format("{0}\\{1}", localTemplateRepoPath, "blessedImageConfig-save.json"), request.NewBaseImage); _githubUtils.Stage(localTemplateRepoPath, "*"); _githubUtils.CommitAndPush(localTemplateRepoPath, prname, String.Format("[blimp] new base image {0}", request.NewBaseImage)); String pullRequestURL = String.Format("https://api.github.com/repos/{0}/{1}-template/pulls?access_token={2}", "azure-app-service", request.stack, _secretsUtils._gitToken); String body = "{ " + "\"title\": " + JsonConvert.SerializeObject("[blimp] Update Base Image") + ", " + "\"body\": " + JsonConvert.SerializeObject("[blimp] auto generated Update Base Image") + ", " + "\"head\": " + JsonConvert.SerializeObject("azure-app-service:" + prname) + ", " + "\"base\": " + JsonConvert.SerializeObject("dev") + "}"; HttpClient httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("patricklee2"); HttpResponseMessage response = null; response = await httpClient.PostAsync(pullRequestURL, new StringContent(body)); // fails on empty commits String result = await response.Content.ReadAsStringAsync(); System.Console.WriteLine(response.ToString()); System.Console.WriteLine(result); //if (response.StatusCode == HttpStatusCode.UnprocessableEntity) //{ // System.Console.WriteLine("Unable to make PR due to no differnce"); //} _githubUtils.gitDispose(localTemplateRepoPath); _githubUtils.Delete(parent); return(""); }
public async Task <ActionResult <IssueMoveResult> > MoveIssue( string toOwnerName, string toRepoName, [FromBody] IssueMoveRequest issueMoveRequest) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); var destinationMilestones = await gitHub.Issue.Milestone.GetAllForRepository(toOwnerName, toRepoName); try { // Create new issue var newIssueDetails = new NewIssue(issueMoveRequest.Title) { Body = issueMoveRequest.Body, }; if (issueMoveRequest.Milestone != null) { // Set the milestone to the ID that matches the one in the destination repo, if it exists var destinationMilestone = destinationMilestones.SingleOrDefault(m => string.Equals(m.Title, issueMoveRequest.Milestone, StringComparison.OrdinalIgnoreCase)); newIssueDetails.Milestone = destinationMilestone?.Number; } if (issueMoveRequest.Assignees != null) { foreach (var assignee in issueMoveRequest.Assignees) { newIssueDetails.Assignees.Add(assignee); } } if (issueMoveRequest.Labels != null) { foreach (var label in issueMoveRequest.Labels) { newIssueDetails.Labels.Add(label); } } var newIssueCreated = await gitHub.Issue.Create(toOwnerName, toRepoName, newIssueDetails); return(Ok( new IssueMoveResult { IssueNumber = newIssueCreated.Number, HtmlUrl = newIssueCreated.HtmlUrl, })); } catch (Exception ex) { return(BadRequest( new IssueMoveResult { Exception = ex, })); } }
public async Task <IActionResult> ApplyLabel(string owner, string repo, int issueNumber, string prediction, string repoSetName) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); await ApplyLabel(gitHub, owner, repo, issueNumber, prediction); return(RedirectToPage("/MikLabel", routeValues: new { repoSetName = repoSetName })); }
private async Task CheckDownloads(bool forceCheck) { var lastCheck = forceCheck ? DateTime.MinValue : Repo.LastChecked; var(newData, entries) = await GitHubUtils.GetAsync <GithubReleaseEntry[]>(Repo.RepoApiReleasesUrl, UserSettingsManager.Instance.GitHubToken, lastCheck); if (!newData) { return; } var downloads = entries.OrderByDescending(r => r.Published).SelectMany(entry => entry.Assets).Sum(a => a.Downloads); if (Repo.LastTotalDownloads != downloads) { int change = downloads - Repo.LastTotalDownloads; lblDownloads.BackColor = change > 0 ? Color.LightGreen : Color.LightPink; PopupMessage msg = new PopupMessage { Caption = Repo.DisplayName, Text = $"Downloads: {downloads} ({(change > 0 ? "+" : string.Empty)}{change})", Image = Properties.Resources.Download_32x32 }; using (var popupNotifier = new NotificationWindow.PopupNotifier { TitleText = msg.Caption, ContentText = msg.Text, IsRightToLeft = false, Image = msg.Image, AutoContentHeight = true, IgnoreWhenFullScreen = true }) { popupNotifier.TitleFont = new Font(popupNotifier.TitleFont.FontFamily, 16.0f); popupNotifier.ContentColor = change > 0 ? Color.ForestGreen : Color.Red; popupNotifier.ContentFont = new Font(popupNotifier.ContentFont.FontFamily, 14.0f); if (change > 0 || !UserSettingsManager.Instance.DoNotShowDecrementPopups) { popupNotifier.Popup(); } } } else { lblDownloads.BackColor = SystemColors.Control; } lblDownloads.Text = "Downloads: " + downloads; Repo.LastTotalDownloads = downloads; }
private async Task CheckAPILimits() { try { var result = await GitHubUtils.GetRateLimit(Settings.GitHubToken); tsslblAPILimit.Text = "API Limits:" + result?.Rate; } catch (Exception e) { tsslblAPILimit.Text = $"API Limits: Check Error: {e.Message}"; } }
/// <summary> /// /// </summary> /// <param name="settings"></param> /// <param name="asset"></param> /// <param name="destinationDir"></param> /// <param name="type"></param> /// <param name="streams"></param> /// <returns></returns> private static bool LockAllContents(ZombieSettings settings, AssetObject asset, string destinationDir, LocationType type, out Dictionary <string, FileStream> streams) { streams = new Dictionary <string, FileStream>(); var dir = FileUtils.GetZombieDownloadsDirectory(); string filePath; if (type == LocationType.Trash) { filePath = Path.Combine(dir, Path.GetFileNameWithoutExtension(asset.Name) + "_old" + Path.GetExtension(asset.Name)); if (!File.Exists(filePath)) { if (!GitHubUtils.DownloadAssets(settings, asset.Url, filePath)) { return(false); } } } else { filePath = Path.Combine(dir, asset.Name); } try { using (var zip = ZipFile.Open(filePath, ZipArchiveMode.Read)) { foreach (var file in zip.Entries) { var completeFileName = Path.Combine(destinationDir, file.FullName); if (file.Name == string.Empty || !Directory.Exists(Path.GetDirectoryName(completeFileName)) || !File.Exists(completeFileName)) { continue; } var fs = new FileStream(completeFileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); streams.Add(completeFileName, fs); } } } catch (Exception e) { _logger.Fatal(e.Message); return(false); } return(true); }
private async Task CheckTrafficViews(bool forceCheck) { var lastCheck = forceCheck ? DateTime.MinValue : Repo.LastChecked; var(newData, views) = await GitHubUtils.GetAsync <GithubTrafficViews>(Repo.RepoApiTrafficViewsUrl, UserSettingsManager.Instance.GitHubToken, lastCheck); if (!newData) { return; } if (Repo.LastTotalViews != views.Total) { int change = views.Total - Repo.LastTotalViews; lblViews.BackColor = change > 0 ? Color.LightGreen : Color.LightPink; PopupMessage msg = new PopupMessage { Caption = Repo.DisplayName, Text = $"Views: {views.Total} ({(change > 0 ? "+" : string.Empty)}{change})", Image = Properties.Resources.Show_32x32 }; using (var popupNotifier = new NotificationWindow.PopupNotifier()) { { popupNotifier.TitleText = msg.Caption; popupNotifier.ContentText = msg.Text; popupNotifier.IsRightToLeft = false; popupNotifier.Image = msg.Image; popupNotifier.TitleFont = new Font(popupNotifier.TitleFont.FontFamily, 16.0f); popupNotifier.ContentColor = change > 0 ? Color.ForestGreen : Color.Red; popupNotifier.IgnoreWhenFullScreen = true; popupNotifier.ContentFont = new Font(popupNotifier.ContentFont.FontFamily, 14.0f); popupNotifier.AutoContentHeight = true; } if (change > 0 || !UserSettingsManager.Instance.DoNotShowDecrementPopups) { popupNotifier.Popup(); } } } else { lblViews.BackColor = SystemColors.Control; } lblViews.Text = $"Views: {views.Total}. U:{views.Views.Sum(v => v.Uniques)}"; Repo.LastTotalViews = views.Total; Repo.LastTotalUniqueViews = views.Views.Sum(v => v.Uniques); }
public async Task <IActionResult> MissingRepos() { var gitHubName = HttpContext.User.Identity.Name; var gitHubAccessToken = await HttpContext.Authentication.GetTokenAsync("access_token"); var gitHubClient = GitHubUtils.GetGitHubClient(gitHubAccessToken); var repoDataSet = await RepoSetProvider.GetRepoDataSet(); var repoSetLists = repoDataSet.GetRepoSetLists(); var distinctOrgs = repoSetLists .SelectMany( repoSet => repoSet.Value.Repos.Select(repoDefinition => repoDefinition.Owner)) .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(org => org).ToList(); var allOrgRepos = new ConcurrentDictionary <string, string[]>(StringComparer.OrdinalIgnoreCase); var result = AsyncParallelUtils.ForEachAsync(distinctOrgs, 5, async org => { var reposInOrg = await gitHubClient.Repository.GetAllForOrg(org); allOrgRepos[org] = reposInOrg.Where(repo => !repo.Fork).Select(repo => repo.Name).ToArray(); }); await result; var missingOrgRepos = allOrgRepos.Select(org => new MissingRepoSet { Org = org.Key, MissingRepos = org.Value .Except( repoSetLists .SelectMany(repoSetList => repoSetList.Value.Repos) .Select(repoDefinition => repoDefinition.Name), StringComparer.OrdinalIgnoreCase) .OrderBy(repo => repo, StringComparer.OrdinalIgnoreCase) .ToList(), }) .OrderBy(missingRepoSet => missingRepoSet.Org, StringComparer.OrdinalIgnoreCase) .ToList(); return(View(new MissingReposViewModel { GitHubUserName = gitHubName, RepoSetNames = repoDataSet.GetRepoSetLists().Select(repoSetList => repoSetList.Key).ToArray(), MissingRepos = missingOrgRepos, })); }
private void OnShowContents() { IsContentVisible = !IsContentVisible; if (Contents.Any()) { return; } var dir = Path.Combine(Directory.GetCurrentDirectory(), "downloads"); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } try { var filePath = Path.Combine(dir, Asset.Name); // download GitHubUtils.DownloadAssets(App.Settings, Asset.Url, filePath); // verify if (!File.Exists(filePath)) { StatusBarManager.StatusLabel.Text = "Could not retrieve contents of the Asset!"; return; } using (var zip = ZipFile.Open(filePath, ZipArchiveMode.Read)) { foreach (var asset in zip.Entries) { Contents.Add(new AssetViewModel(new AssetObject { Name = asset.Name }) { IsContent = true }); } } } catch (Exception e) { Console.WriteLine(e); throw; } }
public async Task <IActionResult> GetIssuesToDispatch(string ownerName, string repoName) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); // Issue the three queries simultaneously and wait for results var allRepoIssuesTask = gitHub.Issue.GetAllForRepository(ownerName, repoName); var allLabelsTask = gitHub.Issue.Labels.GetAllForRepository(ownerName, repoName); await Task.WhenAll(allRepoIssuesTask, allLabelsTask); var allRepoIssues = await allRepoIssuesTask; var allLabels = await allLabelsTask; var sortedRepoLabelNames = allLabels .Where(label => label.Name.StartsWith("repo:", StringComparison.OrdinalIgnoreCase)) .Select(label => label.Name) .OrderBy(labelName => labelName, StringComparer.OrdinalIgnoreCase) .ToList(); // TODO: Ignore Backlog/Discussion(s) milestones? // TODO: Project the issues to a simpler item that also includes action links for dispatching, etc. var repoRef = new RepositoryReference { Owner = new UserReference { Login = ownerName, }, Name = repoName, }; var allIssuesWithoutRepoLabels = allRepoIssues .Where(issue => issue.Labels.All(label => !sortedRepoLabelNames.Contains(label.Name, StringComparer.OrdinalIgnoreCase)) && !IsExcludedMilestone(issue.Milestone?.Title)) .Select(issue => GetIssueDataFromIssue(issue, repoRef)) .ToList(); return(Json(new { issuesWithoutRepoLabels = allIssuesWithoutRepoLabels, repoLabels = sortedRepoLabelNames, })); }
public async Task <IActionResult> ApplyLabels([FromForm] List <string> applyDefault, string repoSetName) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); var tasks = new Task[applyDefault.Count]; for (var i = 0; i < applyDefault.Count; i++) { var(owner, repo, number, prediction) = ParsePrediction(applyDefault[i]); tasks[i] = ApplyLabel(gitHub, owner, repo, number, prediction); } await Task.WhenAll(tasks); return(RedirectToPage("/MikLabel", routeValues: new { repoSetName = repoSetName })); }
/// <summary> /// Downloads latest pre-release from GitHub. /// </summary> /// <param name="settings">Zombie Settings.</param> public async void DownloadPreRelease(ZombieSettings settings) { var segments = GitHubUtils.ParseUrl(settings.Address); var client = new GitHubClient(new ProductHeaderValue("Zombie")); var tokenAuth = new Credentials(settings.AccessToken); client.Credentials = tokenAuth; Release prerelease = null; try { var releases = await client.Repository.Release.GetAll(segments["owner"], segments["repo"], ApiOptions.None); if (releases.Any()) { prerelease = releases.OrderBy(x => x.PublishedAt).FirstOrDefault(x => x.Prerelease); } if (prerelease == null) { Messenger.Default.Send(new PrereleaseDownloaded { Status = PrereleaseStatus.Failed, Settings = null }); return; } } catch (Exception e) { _logger.Fatal("Failed to retrieve Pre-Release from GitHub. " + e.Message); return; } settings.LatestRelease = new ReleaseObject(prerelease); Messenger.Default.Send(new PrereleaseDownloaded { Status = PrereleaseStatus.Found, Settings = settings }); }
public async Task <ActionResult <LabelCreateResult> > CreateLabels( string toOwnerName, string toRepoName, [FromBody] LabelCreateRequest labelCreateRequest) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); var destinationLabels = await gitHub.Issue.Labels.GetAllForRepository(toOwnerName, toRepoName); var listOfLabelsToCreate = labelCreateRequest.Labels .Where(labelNeeded => !destinationLabels .Any(destinationLabel => string.Equals( labelNeeded.Text, destinationLabel.Name, StringComparison.OrdinalIgnoreCase))) .ToList(); try { foreach (var labelToCreate in listOfLabelsToCreate) { await gitHub.Issue.Labels.Create(toOwnerName, toRepoName, new NewLabel(labelToCreate.Text, labelToCreate.Color)); } return(Ok( new LabelCreateResult { LabelsCreated = listOfLabelsToCreate, })); } catch (Exception ex) { return(BadRequest( new LabelCreateResult { Exception = ex, })); } }
private static async void cleanupGithub() { String _gitToken = File.ReadAllText("../../../gitToken.txt"); // list temp repos HttpClient httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("patricklee2"); HttpResponseMessage response = null; List <Repo> resultList = new List <Repo>(); List <Repo> stackRepos = null; int run = 0; while (true) { String generatedReposURL = String.Format("https://api.github.com/orgs/{0}/repos?page={1}&per_page=30&sort=full_name&direction=asc", "blessedimagepipeline", run); response = await httpClient.GetAsync(generatedReposURL); response.EnsureSuccessStatusCode(); string contentString = await response.Content.ReadAsStringAsync(); List <Repo> l = JsonConvert.DeserializeObject <List <Repo> >(contentString); resultList.AddRange(l); run++; if (l.Count < 30) { break; } } stackRepos = resultList.FindAll(isTemp); GitHubUtils gitHubUtils = new GitHubUtils(_gitToken); foreach (Repo r in stackRepos) { Console.WriteLine(r.full_name); gitHubUtils.DeleteGithubAsync("blessedimagepipeline", r.name); // delete image //delete webapp } }
public async Task <IActionResult> DispatchIssueTo(string ownerName, string repoName, int issueNumber, string destinationLabel) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); var issueUpdate = new IssueUpdate(); issueUpdate.AddLabel(destinationLabel); try { await gitHub.Issue.Update(ownerName, repoName, issueNumber, issueUpdate); return(Ok()); } catch (Exception ex) { return(BadRequest(ex.Message)); } }
private async Task CheckNotifications(bool forceCheck = false) { if (forceCheck || DateTime.Now >= Settings.LastReadUserNotification.AddMinutes(Settings.NotificationsIntervalCheck)) { var(newData, notifications) = await GitHubUtils.GetAsync <GitHubUserNotification[]>( "https://api.github.com/notifications", UserSettingsManager.Instance.GitHubToken, UserSettingsManager.Instance.LastReadUserNotification); Settings.LastReadUserNotification = DateTime.Now; if (newData) { Settings.LastReadUserNotification = DateTime.Now; Settings.LastUnReadUserNotifications = notifications.Where(n => n.Unread).ToList(); foreach (var notification in notifications) { using (var popupNotifier = new NotificationWindow.PopupNotifier()) { { popupNotifier.TitleText = notification.Repository.FullName; popupNotifier.ContentText = notification.Subject.Title; popupNotifier.IsRightToLeft = false; popupNotifier.Image = Properties.Resources.Question_32x32; popupNotifier.IgnoreWhenFullScreen = true; popupNotifier.AutoContentHeight = true; } popupNotifier.Popup(); } } } } if (Settings.LastUnReadUserNotifications.Any()) { lstNotifications.Items.Clear(); lstNotifications.Items.AddRange(Settings.LastUnReadUserNotifications.Select(n => $"{n.Repository.FullName}: {n.Subject.Title}").ToArray()); } tsslblNotifications.Text = $"Notification: {Settings.LastUnReadUserNotifications.Count}. Last Update: {Settings.LastReadUserNotification}"; }
public async Task <ActionResult <LabelCreateResult> > CreateMilestone( string toOwnerName, string toRepoName, [FromBody] MilestoneCreateRequest milestoneCreateRequest) { var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); var destinationMilestones = await gitHub.Issue.Milestone.GetAllForRepository(toOwnerName, toRepoName); if (destinationMilestones.Any(m => string.Equals(m.Title, milestoneCreateRequest.Milestone, StringComparison.OrdinalIgnoreCase))) { // Milestone already exists, so do nothing return(Ok(new MilestoneCreateResult { MilestoneCreated = null, })); } try { await gitHub.Issue.Milestone.Create(toOwnerName, toRepoName, new NewMilestone(milestoneCreateRequest.Milestone)); return(Ok( new MilestoneCreateResult { MilestoneCreated = milestoneCreateRequest.Milestone, })); } catch (Exception ex) { return(BadRequest( new MilestoneCreateResult { Exception = ex, })); } }
public static void rubyBase(List <Repo> repos, String root, String upstream) { Repository repo = new Repository(upstream); foreach (Repo r in repos) { // pull temps String dest = root + "\\" + r.name; Repository.Clone(r.clone_url, dest, new CloneOptions { BranchName = "master" }); // move String version = r.name.ToLower().Replace("rubybase-", ""); GitHubUtils githubUtils = new GitHubUtils("fake"); githubUtils.Delete(upstream + "\\base_images\\" + version, skipGit: true); githubUtils.DeepCopy(dest, upstream + "\\base_images\\" + version); // stage Commands.Stage(repo, upstream + "\\base_images\\" + version); } }
public async Task <IActionResult> GetIssuesByUserAsync(string repoSetName, string userName) { var repoSet = _dataSource.GetRepoDataSet().GetRepoSet(repoSetName); var accessToken = await HttpContext.GetTokenAsync("access_token"); var gitHub = GitHubUtils.GetGitHubClient(accessToken); // Issue the three queries simultaneously and wait for results var assignedIssuesQuery = repoSet.GenerateQuery("is:open", "is:issue", $"assignee:{userName}"); var assignedPrsQuery = repoSet.GenerateQuery("is:open", "is:pr", $"assignee:{userName}"); var createdPrsQuery = repoSet.GenerateQuery("is:open", "is:pr", $"author:{userName}"); var assignedIssuesTask = _github.SearchIssuesAsync(assignedIssuesQuery, accessToken); var assignedPrsTask = _github.SearchIssuesAsync(assignedPrsQuery, accessToken); var createdPrsTask = _github.SearchIssuesAsync(createdPrsQuery, accessToken); await Task.WhenAll(assignedIssuesTask, assignedPrsTask, createdPrsTask); var assignedIssues = await assignedIssuesTask; var assignedPrs = await assignedPrsTask; var createdPrs = await createdPrsTask; // Identify issues being worked on var workingIssues = new List <IssueData>(); var otherIssues = new List <IssueData>(); foreach (var result in assignedIssues.Search) { if (result.Labels.Any(l => repoSet.WorkingLabels.Contains(l.Name))) { // We need to grab additional data about Working issues result.Working = true; result.WorkingStartedAt = await GetWorkingStartTime(result, repoSet.WorkingLabels, gitHub); workingIssues.Add(result); } else { otherIssues.Add(result); } } // Update rate limit information var rateLimitCost = RateLimitInfo.Add(RateLimitInfo.Add(assignedIssues.RateLimit, assignedPrs.RateLimit), createdPrs.RateLimit); _logger.LogDebug("Fetched issues for {User} in repo group {Group}. Total Rate Limit Cost: {Cost}", userName, repoSetName, rateLimitCost.Cost); return(Json(new { working = SortWorkingIssues(workingIssues), other = SortOtherAssignedIssues(otherIssues), prs = SortPRs(Enumerable.Concat(assignedPrs.Search, createdPrs.Search)), graphQlRateLimit = rateLimitCost, restRateLimit = gitHub.GetLastApiInfo()?.RateLimit, pages = assignedIssues.Pages + assignedPrs.Pages + createdPrs.Pages, queries = new string[] { assignedIssuesQuery, assignedPrsQuery, createdPrsQuery } })); }
/// <summary> /// /// </summary> /// <param name="settings"></param> public async void GetLatestRelease(ZombieSettings settings) { if (string.IsNullOrEmpty(settings?.AccessToken) || string.IsNullOrEmpty(settings.Address)) { UpdateUI("Connection failed!", ConnectionResult.Failure); return; } var response = await GetLatestReleaseFromGitHub(settings); if (response.StatusCode != HttpStatusCode.OK) { UpdateUI("Connection failed!", ConnectionResult.Failure); return; } var release = response.Data; var currentVersion = Properties.Settings.Default["CurrentVersion"].ToString(); if (!release.Assets.Any() || new Version(release.TagName).CompareTo(new Version(currentVersion)) <= 0) { UpdateUI("Your release is up to date!", ConnectionResult.UpToDate, release); return; } var dir = FileUtils.GetZombieDownloadsDirectory(); var downloaded = 0; foreach (var asset in release.Assets) { var filePath = Path.Combine(dir, asset.Name); if (GitHubUtils.DownloadAssets(settings, asset.Url, filePath)) { downloaded++; } } if (downloaded != release.Assets.Count) { UpdateUI("Failed to download assets!", ConnectionResult.Failure); return; } // (Konrad) Let's get updated settings, they might be local, or remote. // We need latest settings since there might be changes to the target locations. ZombieSettings newSettings; if (File.Exists(settings.SettingsLocation)) { if (!SettingsUtils.TryGetStoredSettings(settings.SettingsLocation, out newSettings)) { UpdateUI("Could not get latest local Zombie Settings!", ConnectionResult.Failure); return; } } else { if (!SettingsUtils.TryGetRemoteSettings(settings.SettingsLocation, out newSettings)) { UpdateUI("Could not get latest remote Zombie Settings!", ConnectionResult.Failure); return; } } // (Konrad) Let's make sure that we own the files that we are trying to override var fileStreams = new Dictionary <string, FileStream>(); foreach (var loc in newSettings.DestinationAssets) { foreach (var asset in loc.Assets) { if (asset.IsArchive()) { if (LockAllContents(settings, asset, loc.DirectoryPath, out var zippedStreams)) { fileStreams = fileStreams.Concat(zippedStreams).GroupBy(x => x.Key) .ToDictionary(x => x.Key, x => x.First().Value); continue; } UpdateUI("Could not get access to all ZIP contents!", ConnectionResult.Failure); return; } var to = Path.Combine(FilePathUtils.CreateUserSpecificPath(loc.DirectoryPath), asset.Name); try { var fs = new FileStream(to, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); fileStreams.Add(to, fs); } catch (Exception e) { UpdateUI(e.Message, ConnectionResult.Failure); return; } } } // (Konrad) Move assets to target locations foreach (var loc in newSettings.DestinationAssets) { foreach (var asset in loc.Assets) { if (asset.IsArchive()) { if (ExtractToDirectory(asset, loc.DirectoryPath, fileStreams)) { continue; } UpdateUI("Could not override existing ZIP contents!", ConnectionResult.Failure); return; } var from = Path.Combine(dir, asset.Name); var to = Path.Combine(FilePathUtils.CreateUserSpecificPath(loc.DirectoryPath), asset.Name); // make sure that file is not locked var stream = fileStreams[to]; stream?.Close(); if (FileUtils.Copy(@from, @to)) { continue; } UpdateUI("Could not override existing file!", ConnectionResult.Failure); return; } } // (Konrad) Remove temporary assets if (!FileUtils.DeleteDirectory(dir)) { UpdateUI("Could not remove temporary download assets!", ConnectionResult.Failure); return; } // (Konrad) Update UI and save current version Properties.Settings.Default.CurrentVersion = release.TagName; Properties.Settings.Default.Save(); _logger.Info("Successfully updated to version: " + release.TagName); Messenger.Default.Send(new UpdateStatus { Status = "Successfully updated to version: " + release.TagName }); Messenger.Default.Send(new ReleaseDownloaded { Release = release, Result = ConnectionResult.Success }); }
public async Task <IGitHubClient> GetGitHubClient() { var accessToken = await _jsRuntime.InvokeAsync <string>("GetGitHubAccessToken"); return(GitHubUtils.GetGitHubClient(accessToken)); }
public static async void createPR(String stack) { String _gitToken = File.ReadAllText("../../../gitToken.txt"); // clone master String timeStamp = DateTime.Now.ToString("yyyyMMddHHmmss"); String root = String.Format("D:\\local\\temp\\blimpPR{0}", timeStamp); String upstream = root + "\\" + stack; String upstreamURL = String.Format("https://github.com/Azure-App-Service/{0}.git", stack); String branch = String.Format("blimp{0}", timeStamp); Repository.Clone(upstreamURL, upstream, new CloneOptions { BranchName = "dev" }); // branch Repository repo = new Repository(upstream); repo.CreateBranch(branch); Commands.Checkout(repo, branch); // list temp repos HttpClient httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("patricklee2"); HttpResponseMessage response = null; List <Repo> resultList = new List <Repo>(); List <Repo> stackRepos = null; int run = 0; while (true) { String generatedReposURL = String.Format("https://api.github.com/orgs/{0}/repos?page={1}&per_page=30&sort=full_name&direction=asc", "blessedimagepipeline", run); response = await httpClient.GetAsync(generatedReposURL); response.EnsureSuccessStatusCode(); string contentString = await response.Content.ReadAsStringAsync(); List <Repo> l = JsonConvert.DeserializeObject <List <Repo> >(contentString); resultList.AddRange(l); run++; if (l.Count < 30) { break; } } switch (stack) { case "dotnetcore": stackRepos = resultList.FindAll(isDotnetcoreRepo); break; case "node": stackRepos = resultList.FindAll(isNodeRepo); break; case "php": stackRepos = resultList.FindAll(isPhpRepo); break; case "python": stackRepos = resultList.FindAll(isPythonRepo); break; case "ruby": stackRepos = resultList.FindAll(isRubyRepo); rubyBase(resultList.FindAll(isRubyBaseRepo), root, upstream); break; } // List<Repo> stackRepos = resultList.FindAll(isStackRepo(stack)); foreach (Repo r in stackRepos) { try { Console.WriteLine("copying " + r.full_name); // pull temps String dest = root + "\\" + r.name; Repository.Clone(r.clone_url, dest, new CloneOptions { BranchName = "dev" }); // move String version = r.name.ToLower().Replace(stack + "-", ""); String suffix = ""; if (stack.Equals("php")) { suffix = "-apache"; } GitHubUtils githubUtils = new GitHubUtils("fake"); githubUtils.Delete(upstream + "\\" + version + suffix, skipGit: true); githubUtils.DeepCopy(dest, upstream + "\\" + version + suffix); // stage Commands.Stage(repo, upstream + "\\" + version + suffix); } catch (LibGit2Sharp.NameConflictException e) { } catch (Exception e) { Console.WriteLine(e.ToString()); } } // git commit // Create the committer's signature and commit //_log.Info("git commit"); Signature author = new Signature("blimp", "*****@*****.**", DateTime.Now); Signature committer = author; // Commit to the repository try { Commit commit = repo.Commit("blimp", author, committer); } catch (Exception e) { //_log.info("Empty commit"); } Remote remote = repo.Network.Remotes.Add("upstream", upstreamURL); repo.Branches.Update(repo.Head, b => b.Remote = remote.Name, b => b.UpstreamBranch = repo.Head.CanonicalName); // git push //_log.Info("git push"); LibGit2Sharp.PushOptions options = new LibGit2Sharp.PushOptions(); options.CredentialsProvider = new CredentialsHandler( (url, usernameFromUrl, types) => new UsernamePasswordCredentials() { Username = _gitToken.Trim(), Password = String.Empty }); repo.Network.Push(repo.Branches[branch], options); // fails if branch already exists //create PR String pullRequestURL = String.Format("https://api.github.com/repos/{0}/{1}/pulls?access_token={2}", "azure-app-service", stack, _gitToken); String body = "{ " + "\"title\": " + JsonConvert.SerializeObject("sync from templates") + ", " + "\"body\": " + JsonConvert.SerializeObject("sync from templates") + ", " + "\"head\": " + JsonConvert.SerializeObject("azure-app-service:" + branch) + ", " + "\"base\": " + JsonConvert.SerializeObject("dev") + "}"; response = await httpClient.PostAsync(pullRequestURL, new StringContent(body)); // fails on empty commits String result = await response.Content.ReadAsStringAsync(); System.Console.WriteLine(response.ToString()); System.Console.WriteLine(result); if (response.StatusCode == HttpStatusCode.UnprocessableEntity) { System.Console.WriteLine("Unable to make PR due to no differnce"); } //cleanup //new DirectoryInfo(root).Delete(true); }
public IActionResult Index(string repoSet, string gitHubAccessToken, string gitHubName) { // Authenticated and all claims have been read if (!RepoSetProvider.RepoSetExists(repoSet)) { return(HttpNotFound()); } var requestStopwatch = new Stopwatch(); requestStopwatch.Start(); var repos = RepoSetProvider.GetRepoSet(repoSet); var distinctRepos = repos.Repos .Distinct() .Where(repo => repo.RepoInclusionLevel != RepoInclusionLevel.None) .ToArray(); var personSetName = repos.AssociatedPersonSetName; var personSet = PersonSetProvider.GetPersonSet(personSetName); var peopleInPersonSet = personSet?.People ?? new string[0]; var workingLabels = repos.WorkingLabels ?? new string[0]; var allIssuesByRepo = new ConcurrentDictionary <RepoDefinition, RepoTask <IReadOnlyList <Issue> > >(); var allPullRequestsByRepo = new ConcurrentDictionary <RepoDefinition, RepoTask <IReadOnlyList <PullRequest> > >(); var gitHubClient = GitHubUtils.GetGitHubClient(gitHubAccessToken); Parallel.ForEach(distinctRepos, repo => allIssuesByRepo[repo] = GetIssuesForRepo(repo, gitHubClient)); Parallel.ForEach(distinctRepos, repo => allPullRequestsByRepo[repo] = GetPullRequestsForRepo(repo, gitHubClient)); // while waiting for queries to run, do some other work... var labelQuery = GetLabelQuery(repos.LabelFilter); var openIssuesQuery = GetOpenIssuesQuery(GetExcludedMilestonesQuery(), labelQuery, distinctRepos); var workingIssuesQuery = GetWorkingIssuesQuery(labelQuery, workingLabels, distinctRepos); var unassignedIssuesQuery = GetUnassignedIssuesQuery(GetExcludedMilestonesQuery(), labelQuery, distinctRepos); var untriagedIssuesQuery = GetUntriagedIssuesQuery(labelQuery, distinctRepos); var openPRsQuery = GetOpenPRsQuery(distinctRepos); var stalePRsQuery = GetStalePRsQuery(distinctRepos); // now wait for queries to finish executing try { Task.WaitAll(allIssuesByRepo.Select(x => x.Value.Task).ToArray()); } catch (AggregateException) { // Just hide the exceptions here - faulted tasks will be aggregated later } try { Task.WaitAll(allPullRequestsByRepo.Select(x => x.Value.Task).ToArray()); } catch (AggregateException) { // Just hide the exceptions here - faulted tasks will be aggregated later } var repoFailures = new List <RepoFailure>(); repoFailures.AddRange( allIssuesByRepo .Where(repoTask => repoTask.Value.Task.IsFaulted || repoTask.Value.Task.IsCanceled) .Select(repoTask => new RepoFailure { Repo = repoTask.Key, FailureMessage = string.Format("Issues couldn't be retrieved for the {0}/{1} repo", repoTask.Key.Owner, repoTask.Key.Name), })); repoFailures.AddRange( allPullRequestsByRepo .Where(repoTask => repoTask.Value.Task.IsFaulted || repoTask.Value.Task.IsCanceled) .Select(repoTask => new RepoFailure { Repo = repoTask.Key, FailureMessage = string.Format("Pull requests couldn't be retrieved for the {0}/{1} repo", repoTask.Key.Owner, repoTask.Key.Name), })); var allIssues = allIssuesByRepo .Where(repoTask => !repoTask.Value.Task.IsFaulted && !repoTask.Value.Task.IsCanceled) .SelectMany(issueList => issueList.Value.Task.Result .Where( issue => !IsExcludedMilestone(issue.Milestone?.Title) && issue.PullRequest == null && IsFilteredIssue(issue, repos) && ItemIncludedByInclusionLevel(issue.Assignee?.Login, issueList.Key, peopleInPersonSet)) .Select( issue => new IssueWithRepo { Issue = issue, Repo = issueList.Key, WorkingStartTime = GetWorkingStartTime(issueList.Key, issue, workingLabels, gitHubClient).Result, IsInAssociatedPersonSet = IsInAssociatedPersonSet(issue.Assignee?.Login, personSet), })) .OrderBy(issueWithRepo => issueWithRepo.WorkingStartTime) .ToList(); var workingIssues = allIssues .Where(issue => issue.Issue.Labels .Any(label => workingLabels.Contains(label.Name, StringComparer.OrdinalIgnoreCase))) .ToList(); var untriagedIssues = allIssues .Where(issue => issue.Issue.Milestone == null).ToList(); var unassignedIssues = allIssues .Where(issue => issue.Issue.Assignee == null).ToList(); var allPullRequests = allPullRequestsByRepo .Where(repoTask => !repoTask.Value.Task.IsFaulted && !repoTask.Value.Task.IsCanceled) .SelectMany(pullRequestList => pullRequestList.Value.Task.Result .Where( pullRequest => !IsExcludedMilestone(pullRequest.Milestone?.Title) && (ItemIncludedByInclusionLevel(pullRequest.Assignee?.Login, pullRequestList.Key, peopleInPersonSet) || ItemIncludedByInclusionLevel(pullRequest.User.Login, pullRequestList.Key, peopleInPersonSet))) .Select(pullRequest => new PullRequestWithRepo { PullRequest = pullRequest, Repo = pullRequestList.Key, IsInAssociatedPersonSet = IsInAssociatedPersonSet(pullRequest.User?.Login, personSet), })) .OrderBy(pullRequestWithRepo => pullRequestWithRepo.PullRequest.CreatedAt) .ToList(); var milestoneData = distinctRepos .OrderBy(repo => repo.Owner + "/" + repo.Name, StringComparer.OrdinalIgnoreCase) .Select(repo => new MilestoneSummary() { Repo = repo, MilestoneData = allIssues .Where(issue => issue.Repo == repo) .GroupBy(issue => issue.Issue.Milestone?.Title) .Select(issueMilestoneGroup => new MilestoneData { Milestone = issueMilestoneGroup.Key, OpenIssues = issueMilestoneGroup.Count(), }) .ToList(), }); var fullSortedMilestoneList = milestoneData .SelectMany(milestone => milestone.MilestoneData) .Select(milestone => milestone.Milestone) .Distinct() .OrderBy(milestone => new PossibleSemanticVersion(milestone)); var issueListViewModel = new IssueListViewModel { RepoFailures = repoFailures, GitHubUserName = gitHubName, LastUpdated = DateTimeOffset.Now.ToPacificTime().ToString(), ExtraLinks = repos.RepoExtraLinks, RepoSetName = repoSet, RepoSetNames = RepoSetProvider.GetRepoSetLists().Select(repoSetList => repoSetList.Key).ToArray(), TotalIssues = allIssues.Count, WorkingIssues = workingIssues.Count, UntriagedIssues = untriagedIssues.Count, UnassignedIssues = unassignedIssues.Count, OpenPullRequests = allPullRequests.Count, StalePullRequests = allPullRequests.Where(pr => pr.PullRequest.CreatedAt < DateTimeOffset.Now.AddDays(-14)).Count(), ReposIncluded = distinctRepos .OrderBy(repo => repo.Owner.ToLowerInvariant()) .ThenBy(repo => repo.Name.ToLowerInvariant()) .Select(repo => new RepoSummary { Repo = repo, OpenIssues = allIssues.Where(issue => issue.Repo == repo).Count(), OpenIssuesQueryUrl = GetOpenIssuesQuery(GetExcludedMilestonesQuery(), labelQuery, repo), UnassignedIssues = allIssues.Where(issue => issue.Repo == repo && issue.Issue.Assignee == null).Count(), UnassignedIssuesQueryUrl = GetUnassignedIssuesQuery(GetExcludedMilestonesQuery(), labelQuery, repo), UntriagedIssues = allIssues.Where(issue => issue.Repo == repo && issue.Issue.Milestone == null).Count(), UntriagedIssuesQueryUrl = GetUntriagedIssuesQuery(labelQuery, repo), WorkingIssues = allIssues.Where(issue => issue.Repo == repo && workingIssues.Contains(issue)).Count(), WorkingIssuesQueryUrl = GetWorkingIssuesQuery(labelQuery, workingLabels, repo), OpenPRs = allPullRequests.Where(pullRequest => pullRequest.Repo == repo).Count(), OpenPRsQueryUrl = GetOpenPRsQuery(repo), StalePRs = allPullRequests.Where(pullRequest => pullRequest.Repo == repo && pullRequest.PullRequest.CreatedAt < DateTimeOffset.Now.AddDays(-14)).Count(), StalePRsQueryUrl = GetStalePRsQuery(repo), }) .ToList(), MilestoneSummary = milestoneData.ToList(), MilestonesAvailable = fullSortedMilestoneList.ToList(), OpenIssuesQuery = openIssuesQuery, WorkingIssuesQuery = workingIssuesQuery, UntriagedIssuesQuery = untriagedIssuesQuery, UnassignedIssuesQuery = unassignedIssuesQuery, OpenPRsQuery = openPRsQuery, StalePRsQuery = stalePRsQuery, GroupByAssignee = new GroupByAssigneeViewModel { Assignees = peopleInPersonSet .OrderBy(person => person, StringComparer.OrdinalIgnoreCase) .Select(person => new GroupByAssigneeAssignee { Assignee = person, IsInAssociatedPersonSet = IsInAssociatedPersonSet(person, personSet), Issues = workingIssues .Where(workingIssue => workingIssue.Issue.Assignee?.Login == person) .ToList(), PullRequests = allPullRequests .Where( pr => pr.PullRequest.User.Login == person || pr.PullRequest.Assignee?.Login == person) .OrderBy(pr => pr.PullRequest.CreatedAt) .ToList(), OtherIssues = allIssues .Where(issue => issue.Issue.Assignee?.Login == person) .Except(workingIssues) .OrderBy(issueWithRepo => new PossibleSemanticVersion(issueWithRepo.Issue.Milestone?.Title)) .ThenBy(issueWithRepo => issueWithRepo.Repo.Name, StringComparer.OrdinalIgnoreCase) .ThenBy(issueWithRepo => issueWithRepo.Issue.Number) .ToList(), }) .Concat(new[] { new GroupByAssigneeAssignee { Assignee = "<other assignees>", IsMetaAssignee = true, IsInAssociatedPersonSet = false, Issues = workingIssues .Where(workingIssue => workingIssue.Issue.Assignee != null && !peopleInPersonSet.Contains(workingIssue.Issue.Assignee.Login, StringComparer.OrdinalIgnoreCase)) .ToList(), PullRequests = allPullRequests .Where( pr => pr.PullRequest.Assignee != null && !peopleInPersonSet.Contains(pr.PullRequest.User.Login, StringComparer.OrdinalIgnoreCase) && !peopleInPersonSet.Contains(pr.PullRequest.Assignee.Login, StringComparer.OrdinalIgnoreCase)) .OrderBy(pr => pr.PullRequest.CreatedAt) .ToList(), OtherIssues = allIssues .Where(issue => issue.Issue.Assignee != null && !peopleInPersonSet.Contains(issue.Issue.Assignee?.Login, StringComparer.OrdinalIgnoreCase)) .Except(workingIssues) .OrderBy(issueWithRepo => issueWithRepo.Issue.Assignee.Login, StringComparer.OrdinalIgnoreCase) .ThenBy(issueWithRepo => new PossibleSemanticVersion(issueWithRepo.Issue.Milestone?.Title)) .ThenBy(issueWithRepo => issueWithRepo.Repo.Name, StringComparer.OrdinalIgnoreCase) .ThenBy(issueWithRepo => issueWithRepo.Issue.Number) .ToList(), }, new GroupByAssigneeAssignee { Assignee = "<unassigned>", IsMetaAssignee = true, IsInAssociatedPersonSet = false, Issues = workingIssues .Where(workingIssue => workingIssue.Issue.Assignee == null) .ToList(), PullRequests = allPullRequests .Where( pr => pr.PullRequest.Assignee == null && !peopleInPersonSet.Contains(pr.PullRequest.User.Login, StringComparer.OrdinalIgnoreCase)) .OrderBy(pr => pr.PullRequest.CreatedAt) .ToList(), OtherIssues = allIssues .Where(issue => issue.Issue.Assignee == null) .Except(workingIssues) .OrderBy(issueWithRepo => new PossibleSemanticVersion(issueWithRepo.Issue.Milestone?.Title)) .ThenBy(issueWithRepo => issueWithRepo.Repo.Name, StringComparer.OrdinalIgnoreCase) .ThenBy(issueWithRepo => issueWithRepo.Issue.Number) .ToList(), }, }) .ToList() .AsReadOnly(), }, GroupByMilestone = new GroupByMilestoneViewModel { Milestones = workingIssues .Select(issue => issue.Issue.Milestone?.Title) .Concat(new string[] { null }) .Distinct() .OrderBy(milestone => new PossibleSemanticVersion(milestone)) .Select(milestone => new GroupByMilestoneMilestone { Milestone = milestone, Issues = workingIssues .Where(issue => issue.Issue.Milestone?.Title == milestone) .OrderBy(issue => issue.WorkingStartTime) .ToList(), PullRequests = allPullRequests .Where(pullRequest => pullRequest.PullRequest.Milestone?.Title == milestone) .OrderBy(pullRequest => pullRequest.PullRequest.CreatedAt) .ToList(), }) .OrderBy(group => new PossibleSemanticVersion(group.Milestone)) .ToList() }, GroupByRepo = new GroupByRepoViewModel { Repos = workingIssues .Select(issue => issue.Repo) .Concat(allPullRequests.Select(pullRequest => pullRequest.Repo)) .Distinct() .OrderBy(repo => repo) .Select(repo => new GroupByRepoRepo { Repo = repo, Issues = workingIssues .Where(issue => issue.Repo == repo) .OrderBy(issue => issue.WorkingStartTime) .ToList(), PullRequests = allPullRequests .Where(pullRequest => pullRequest.Repo == repo) .OrderBy(pullRequest => pullRequest.PullRequest.Assignee?.Login) .ThenBy(pullRequest => pullRequest.PullRequest.Number) .ToList(), }) .ToList() }, }; requestStopwatch.Stop(); issueListViewModel.PageRequestTime = requestStopwatch.Elapsed; return(View(issueListViewModel)); }
public async Task <IActionResult> Index(string repoSet) { using (_logger.BeginScope("Requesting Triage Data for {RepoSet}", repoSet)) { HttpContext.AddTelemetryProperty("RepoSet", repoSet); HttpContext.AddTelemetryProperty("RepoSetView", "Triage"); var metricsPrefix = $"TriageController:RepoSet({repoSet})"; var gitHubName = HttpContext.User.Identity.Name; HttpContext.AddTelemetryProperty("GitHubUser", gitHubName); var gitHubAccessToken = await HttpContext.GetTokenAsync("access_token"); // Authenticated and all claims have been read var repoDataSet = _dataSource.GetRepoDataSet(); if (!repoDataSet.RepoSetExists(repoSet)) { var invalidRepoSetPageViewTelemetry = new PageViewTelemetry("RepoSet") { Url = new Uri(Request.GetDisplayUrl()), }; HttpContext.AddTelemetryProperty("RepoSetValid", false); return(NotFound()); } var requestStopwatch = new Stopwatch(); requestStopwatch.Start(); var repos = repoDataSet.GetRepoSet(repoSet); var distinctRepos = repos.Repos .Distinct() .Where(repo => repo.RepoInclusionLevel != RepoInclusionLevel.NotInRepoSet && repo.RepoInclusionLevel != RepoInclusionLevel.Ignored) .ToArray(); var personSetName = repos.AssociatedPersonSetName; var personSet = _dataSource.GetPersonSet(personSetName); var peopleInPersonSet = personSet?.People ?? new string[0]; var workingLabels = repos.WorkingLabels ?? new HashSet <string>(); var allIssuesByRepo = new ConcurrentDictionary <RepoDefinition, RepoTask <IReadOnlyList <Issue> > >(); var allPullRequestsByRepo = new ConcurrentDictionary <RepoDefinition, RepoTask <IReadOnlyList <PullRequest> > >(); var gitHubClient = GitHubUtils.GetGitHubClient(gitHubAccessToken); // Get missing repos var distinctOrgs = distinctRepos .Select( repoDefinition => repoDefinition.Owner) .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(org => org) .ToList(); var allOrgRepos = new ConcurrentDictionary <string, string[]>(StringComparer.OrdinalIgnoreCase); using (_metricsService.Time($"{metricsPrefix}:GetAllRepositories")) { var getAllOrgReposTask = AsyncParallelUtils.ForEachAsync(distinctOrgs, 5, async org => { IReadOnlyList <Repository> reposInOrg; using (_metricsService.Time($"{metricsPrefix}:Org({org}):GetRepositories")) { reposInOrg = await gitHubClient.Repository.GetAllForOrg(org); } allOrgRepos[org] = reposInOrg.Where(repo => !repo.Fork).Select(repo => repo.Name).ToArray(); }); await getAllOrgReposTask; } var missingOrgRepos = allOrgRepos.Select(org => new MissingRepoSet { Org = org.Key, MissingRepos = org.Value .Except( repos.Repos .Distinct() .Where(repo => repo.RepoInclusionLevel != RepoInclusionLevel.NotInRepoSet) .Select(repoDefinition => repoDefinition.Name), StringComparer.OrdinalIgnoreCase) .OrderBy(repo => repo, StringComparer.OrdinalIgnoreCase) .ToList(), }) .OrderBy(missingRepoSet => missingRepoSet.Org, StringComparer.OrdinalIgnoreCase) .ToList(); // Get bugs/PR data Parallel.ForEach(distinctRepos, repo => allIssuesByRepo[repo] = GetIssuesForRepo(repo, gitHubClient, metricsPrefix)); Parallel.ForEach(distinctRepos, repo => allPullRequestsByRepo[repo] = GetPullRequestsForRepo(repo, gitHubClient, metricsPrefix)); // while waiting for queries to run, do some other work... var distinctMainRepos = distinctRepos.Where(repo => repo.RepoInclusionLevel == RepoInclusionLevel.AllItems).ToArray(); var distinctExtraRepos = distinctRepos.Where(repo => repo.RepoInclusionLevel == RepoInclusionLevel.ItemsAssignedToPersonSet).ToArray(); var labelQuery = GetLabelQuery(repos.LabelFilter); var openIssuesQuery = GetOpenIssuesQuery(GetExcludedMilestonesQuery(), labelQuery, distinctMainRepos); var workingIssuesQuery = GetWorkingIssuesQuery(labelQuery, workingLabels, distinctMainRepos); var unassignedIssuesQuery = GetUnassignedIssuesQuery(GetExcludedMilestonesQuery(), labelQuery, distinctMainRepos); var untriagedIssuesQuery = GetUntriagedIssuesQuery(labelQuery, distinctMainRepos); var openPRsQuery = GetOpenPRsQuery(distinctMainRepos); var stalePRsQuery = GetStalePRsQuery(distinctMainRepos); // now wait for queries to finish executing var failuresOccurred = false; try { Task.WaitAll(allIssuesByRepo.Select(x => x.Value.Task).ToArray()); } catch (AggregateException) { // Just hide the exceptions here - faulted tasks will be aggregated later failuresOccurred = true; } try { Task.WaitAll(allPullRequestsByRepo.Select(x => x.Value.Task).ToArray()); } catch (AggregateException) { // Just hide the exceptions here - faulted tasks will be aggregated later failuresOccurred = true; } using (_metricsService.Time($"{metricsPrefix}:PostQueryProcessingTime")) { // Log failures var repoFailures = new List <RepoFailure>(); if (failuresOccurred) { repoFailures.AddRange( allIssuesByRepo .Where(repoTask => repoTask.Value.Task.IsFaulted || repoTask.Value.Task.IsCanceled) .Select(repoTask => new RepoFailure { Repo = repoTask.Key, IssueType = IssueType.Issue, FailureMessage = string.Format("Issues couldn't be retrieved for the {0}/{1} repo", repoTask.Key.Owner, repoTask.Key.Name), Exception = repoTask.Value.Task.Exception, })); repoFailures.AddRange( allPullRequestsByRepo .Where(repoTask => repoTask.Value.Task.IsFaulted || repoTask.Value.Task.IsCanceled) .Select(repoTask => new RepoFailure { Repo = repoTask.Key, IssueType = IssueType.PullRequest, FailureMessage = string.Format("Pull requests couldn't be retrieved for the {0}/{1} repo", repoTask.Key.Owner, repoTask.Key.Name), Exception = repoTask.Value.Task.Exception, })); foreach (var failure in repoFailures) { _logger.LogError( failure.Exception, "Error retrieving {IssueType} data for {RepositoryOwner}/{RepositoryName}", failure.IssueType, failure.Repo.Owner, failure.Repo.Name); } } var allIssues = allIssuesByRepo .Where(repoTask => !repoTask.Value.Task.IsFaulted && !repoTask.Value.Task.IsCanceled) .SelectMany(issueList => issueList.Value.Task.Result .Where( issue => !IsExcludedMilestone(issue.Milestone?.Title) && issue.PullRequest == null && IsFilteredIssue(issue, repos) && ItemIncludedByInclusionLevel(issue.Assignee?.Login, issueList.Key, peopleInPersonSet)) .Select( issue => new IssueWithRepo { Issue = issue, Repo = issueList.Key, IsInAssociatedPersonSet = IsInAssociatedPersonSet(issue.Assignee?.Login, personSet), })) .OrderBy(issueWithRepo => issueWithRepo.WorkingStartTime) .ToList(); var workingIssues = allIssues .Where(issue => issue.Issue.Labels .Any(label => workingLabels.Contains(label.Name, StringComparer.OrdinalIgnoreCase))) .ToList(); var untriagedIssues = allIssues .Where(issue => issue.Issue.Milestone == null).ToList(); var unassignedIssues = allIssues .Where(issue => issue.Issue.Assignee == null).ToList(); var allPullRequests = allPullRequestsByRepo .Where(repoTask => !repoTask.Value.Task.IsFaulted && !repoTask.Value.Task.IsCanceled) .SelectMany(pullRequestList => pullRequestList.Value.Task.Result .Where( pullRequest => !IsExcludedMilestone(pullRequest.Milestone?.Title) && (ItemIncludedByInclusionLevel(pullRequest.Assignee?.Login, pullRequestList.Key, peopleInPersonSet) || ItemIncludedByInclusionLevel(pullRequest.User.Login, pullRequestList.Key, peopleInPersonSet))) .Select(pullRequest => new PullRequestWithRepo { PullRequest = pullRequest, Repo = pullRequestList.Key, IsInAssociatedPersonSet = IsInAssociatedPersonSet(pullRequest.User?.Login, personSet), })) .OrderBy(pullRequestWithRepo => pullRequestWithRepo.PullRequest.CreatedAt) .ToList(); var allIssuesInMainRepos = allIssues.Where(issue => distinctMainRepos.Contains(issue.Repo)).ToList(); var allIssuesInExtraRepos = allIssues.Where(issue => distinctExtraRepos.Contains(issue.Repo)).ToList(); var mainMilestoneData = distinctMainRepos .OrderBy(repo => repo.Owner + "/" + repo.Name, StringComparer.OrdinalIgnoreCase) .Select(repo => new MilestoneSummary() { Repo = repo, MilestoneData = allIssuesInMainRepos .Where(issue => issue.Repo == repo) .GroupBy(issue => issue.Issue.Milestone?.Title) .Select(issueMilestoneGroup => new MilestoneData { Milestone = issueMilestoneGroup.Key, OpenIssues = issueMilestoneGroup.Count(), }) .ToList(), }); var fullSortedMainMilestoneList = mainMilestoneData .SelectMany(milestone => milestone.MilestoneData) .Select(milestone => milestone.Milestone) .Distinct() .OrderBy(milestone => new PossibleSemanticVersion(milestone)); var extraMilestoneData = distinctExtraRepos .OrderBy(repo => repo.Owner + "/" + repo.Name, StringComparer.OrdinalIgnoreCase) .Select(repo => new MilestoneSummary() { Repo = repo, MilestoneData = allIssuesInExtraRepos .Where(issue => issue.Repo == repo) .GroupBy(issue => issue.Issue.Milestone?.Title) .Select(issueMilestoneGroup => new MilestoneData { Milestone = issueMilestoneGroup.Key, OpenIssues = issueMilestoneGroup.Count(), }) .ToList(), }); var fullSortedExtraMilestoneList = extraMilestoneData .SelectMany(milestone => milestone.MilestoneData) .Select(milestone => milestone.Milestone) .Distinct() .OrderBy(milestone => new PossibleSemanticVersion(milestone)); var lastApiInfo = gitHubClient.GetLastApiInfo(); _metricsService.Record("TriageController:RateLimitRemaining", lastApiInfo.RateLimit.Remaining); var issueListViewModel = new IssueListViewModel { LastApiInfo = lastApiInfo, RepoFailures = repoFailures, GitHubUserName = gitHubName, LastUpdated = DateTimeOffset.Now.ToPacificTime().ToString(), ExtraLinks = repos.RepoExtraLinks, RepoSetName = repoSet, RepoSetNames = repoDataSet.GetRepoSetLists().Select(repoSetList => repoSetList.Key).ToArray(), TotalIssues = allIssues.Where(issue => issue.Repo.RepoInclusionLevel == RepoInclusionLevel.AllItems).Count(), WorkingIssues = workingIssues.Count, UntriagedIssues = untriagedIssues.Where(issue => issue.Repo.RepoInclusionLevel == RepoInclusionLevel.AllItems).Count(), UnassignedIssues = unassignedIssues.Count, OpenPullRequests = allPullRequests.Where(pr => pr.Repo.RepoInclusionLevel == RepoInclusionLevel.AllItems).Count(), StalePullRequests = allPullRequests.Where(pr => pr.Repo.RepoInclusionLevel == RepoInclusionLevel.AllItems && pr.PullRequest.CreatedAt < DateTimeOffset.Now.AddDays(-14)).Count(), MainReposIncluded = distinctMainRepos.GetRepoSummary(allIssues, workingIssues, allPullRequests, labelQuery, workingLabels, this), ExtraReposIncluded = distinctExtraRepos.GetRepoSummary(allIssues, workingIssues, allPullRequests, labelQuery, workingLabels, this), MissingRepos = missingOrgRepos, IgnoredRepos = repos.Repos .Where(repo => repo.RepoInclusionLevel == RepoInclusionLevel.Ignored) .GroupBy(repo => repo.Owner, StringComparer.OrdinalIgnoreCase) .Select(ignoredRepoGroup => new MissingRepoSet { Org = ignoredRepoGroup.Key, MissingRepos = ignoredRepoGroup .Select(repo => repo.Name) .OrderBy(repoName => repoName, StringComparer.OrdinalIgnoreCase) .ToArray() }) .OrderBy(missingRepoSet => missingRepoSet.Org, StringComparer.OrdinalIgnoreCase) .ToArray(), MainMilestoneSummary = new MilestoneSummaryData { MilestoneData = mainMilestoneData.ToList(), MilestonesAvailable = fullSortedMainMilestoneList.ToList(), }, ExtraMilestoneSummary = new MilestoneSummaryData { MilestoneData = extraMilestoneData.ToList(), MilestonesAvailable = fullSortedExtraMilestoneList.ToList(), }, OpenIssuesQuery = openIssuesQuery, WorkingIssuesQuery = workingIssuesQuery, UntriagedIssuesQuery = untriagedIssuesQuery, UnassignedIssuesQuery = unassignedIssuesQuery, OpenPRsQuery = openPRsQuery, StalePRsQuery = stalePRsQuery, GroupByAssignee = new GroupByAssigneeViewModel { Assignees = new[] { new GroupByAssigneeAssignee { Assignee = "<assigned outside this person set>", IsMetaAssignee = true, IsInAssociatedPersonSet = false, Issues = workingIssues .Where(workingIssue => workingIssue.Issue.Assignee != null && !peopleInPersonSet.Contains(workingIssue.Issue.Assignee.Login, StringComparer.OrdinalIgnoreCase)) .ToList(), PullRequests = allPullRequests .Where( pr => pr.PullRequest.Assignee != null && !peopleInPersonSet.Contains(pr.PullRequest.User.Login, StringComparer.OrdinalIgnoreCase) && !peopleInPersonSet.Contains(pr.PullRequest.Assignee.Login, StringComparer.OrdinalIgnoreCase)) .OrderBy(pr => pr.PullRequest.CreatedAt) .ToList(), OtherIssues = allIssues .Where(issue => issue.Issue.Assignee != null && !peopleInPersonSet.Contains(issue.Issue.Assignee?.Login, StringComparer.OrdinalIgnoreCase)) .Except(workingIssues) .OrderBy(issueWithRepo => issueWithRepo.Issue.Assignee.Login, StringComparer.OrdinalIgnoreCase) .ThenBy(issueWithRepo => new PossibleSemanticVersion(issueWithRepo.Issue.Milestone?.Title)) .ThenBy(issueWithRepo => issueWithRepo.Repo.Name, StringComparer.OrdinalIgnoreCase) .ThenBy(issueWithRepo => issueWithRepo.Issue.Number) .ToList(), }, new GroupByAssigneeAssignee { Assignee = "<unassigned>", IsMetaAssignee = true, IsInAssociatedPersonSet = false, Issues = workingIssues .Where(workingIssue => workingIssue.Issue.Assignee == null) .ToList(), PullRequests = allPullRequests .Where( pr => pr.PullRequest.Assignee == null && !peopleInPersonSet.Contains(pr.PullRequest.User.Login, StringComparer.OrdinalIgnoreCase)) .OrderBy(pr => pr.PullRequest.CreatedAt) .ToList(), OtherIssues = allIssues .Where(issue => issue.Issue.Assignee == null) .Except(workingIssues) .OrderBy(issueWithRepo => new PossibleSemanticVersion(issueWithRepo.Issue.Milestone?.Title)) .ThenBy(issueWithRepo => issueWithRepo.Repo.Name, StringComparer.OrdinalIgnoreCase) .ThenBy(issueWithRepo => issueWithRepo.Issue.Number) .ToList(), }, } .ToList() .AsReadOnly(), }, }; requestStopwatch.Stop(); issueListViewModel.PageRequestTime = requestStopwatch.Elapsed; HttpContext.AddTelemetryProperty("RepoSetValid", true); return(View(issueListViewModel)); } } }