public async Task CreateOrUpdatePullRequestMergeStatusInfoAsync(string pullRequestUrl, IReadOnlyList <MergePolicyEvaluationResult> evaluations) { (string owner, string repo, int id) = ParsePullRequestUri(pullRequestUrl); // Get the sha of the latest commit for the current PR string prSha = (await Client.PullRequest.Get(owner, repo, id))?.Head?.Sha; if (prSha == null) { throw new InvalidOperationException("We cannot find the sha of the pull request"); } // Get a list of all the merge policies checks runs for the current PR List <CheckRun> existingChecksRuns = (await Client.Check.Run.GetAllForReference(owner, repo, prSha)) .CheckRuns.Where(e => e.ExternalId.StartsWith(MergePolicyConstants.MaestroMergePolicyCheckRunPrefix)).ToList(); var toBeAdded = evaluations.Where(e => existingChecksRuns.All(c => c.ExternalId != CheckRunId(e, prSha))); var toBeUpdated = existingChecksRuns.Where(c => evaluations.Any(e => c.ExternalId == CheckRunId(e, prSha))); var toBeDeleted = existingChecksRuns.Where(c => evaluations.All(e => c.ExternalId != CheckRunId(e, prSha))); foreach (var newCheckRunValidation in toBeAdded) { await Client.Check.Run.Create(owner, repo, CheckRunForAdd(newCheckRunValidation, prSha)); } foreach (var updatedCheckRun in toBeUpdated) { MergePolicyEvaluationResult eval = evaluations.Single(e => updatedCheckRun.ExternalId == CheckRunId(e, prSha)); CheckRunUpdate newCheckRunUpdateValidation = CheckRunForUpdate(eval); await Client.Check.Run.Update(owner, repo, updatedCheckRun.Id, newCheckRunUpdateValidation); } foreach (var deletedCheckRun in toBeDeleted) { await Client.Check.Run.Update(owner, repo, deletedCheckRun.Id, CheckRunForDelete(deletedCheckRun)); } }
public async Task UpdatesCheckRun() { using (var repoContext = await _github.CreateRepositoryContext(new NewRepository(Helper.MakeNameWithTimestamp("public-repo")) { AutoInit = true })) { // Create a new feature branch var headCommit = await _github.Repository.Commit.Get(repoContext.RepositoryId, "master"); var featureBranch = await Helper.CreateFeatureBranch(repoContext.RepositoryOwner, repoContext.RepositoryName, headCommit.Sha, "my-feature"); // Create a check run for the feature branch var newCheckRun = new NewCheckRun("name", featureBranch.Object.Sha) { Status = CheckStatus.Queued }; var checkRun = await _githubAppInstallation.Check.Run.Create(repoContext.RepositoryOwner, repoContext.RepositoryName, newCheckRun); // Update the check run var update = new CheckRunUpdate("new-name") { Status = CheckStatus.InProgress }; var result = await _githubAppInstallation.Check.Run.Update(repoContext.RepositoryOwner, repoContext.RepositoryName, checkRun.Id, update); // Check result Assert.NotNull(result); Assert.Equal(featureBranch.Object.Sha, result.HeadSha); Assert.Equal("new-name", result.Name); Assert.Equal(CheckStatus.InProgress, result.Status); } }
/// <inheritdoc /> public async Task UpdateCheckRun(long repositoryId, long checkRunId, CheckRunUpdate checkRunUpdate, CancellationToken cancellationToken) { logger.LogTrace("Update check run {0} on repository {1}", checkRunId, repositoryId); var client = await CreateInstallationClient(repositoryId, cancellationToken).ConfigureAwait(false); await client.Check.Run.Update(repositoryId, checkRunId, checkRunUpdate).ConfigureAwait(false); }
/// <summary> /// Update a check run based on a NewCheckRun and evaluation /// </summary> /// <param name="newCheckRun">The NewCheckRun that needs to be updated</param> /// <param name="eval">The result of that updated check run</param> /// <returns>The updated CheckRun</returns> private CheckRunUpdate CheckRunForUpdate(MergePolicyEvaluationResult eval) { CheckRunUpdate updatedCheckRun = new CheckRunUpdate(); UpdateCheckRun(updatedCheckRun, eval); return(updatedCheckRun); }
private async void Process_pull_requestAsync(JToken pull_request) { // todo: read comments from repo /org admins though for things like // "I approve this" and then approve the changes and then wait for // "merge squashed/rebased/commit" (if enabled on repository). // seems I cannot create a status yet??? var newCheckRun = new NewCheckRun("Misc/NEWS", pull_request["head"]["sha"].Value <string>()) { Status = CheckStatus.InProgress }; var check = await Program.client.Check.Run.Create(pull_request["base"]["repo"]["owner"].Value <string>(), pull_request["base"]["repo"]["name"].Value <string>(), newCheckRun); var _issue = await Program.client.Issue.Get(pull_request["base"]["repo"]["owner"].Value <string>(), pull_request["base"]["repo"]["name"].Value <string>(), Convert.ToInt32(pull_request["number"].Value <string>())); foreach (var label in _issue.Labels) { if (label.Name != "skip news") { var _newCheckRunOutput = new NewCheckRunOutput("Misc/NEWS", "'skip news' label found!"); var _checkRunUpdate = new CheckRunUpdate { Status = CheckStatus.Completed, Output = _newCheckRunOutput, Conclusion = CheckConclusion.Success }; _ = await Program.client.Check.Run.Update(pull_request["base"]["repo"]["owner"].Value <string>(), pull_request["base"]["repo"]["name"].Value <string>(), check.Id, _checkRunUpdate); return;// label.Name == "skip news"; } } var files = await Program.client.PullRequest.Files(pull_request["base"]["repo"]["owner"].Value <string>(), pull_request["base"]["repo"]["name"].Value <string>(), Convert.ToInt32(pull_request["number"].Value <string>())); foreach (var file in files) { if (file.FileName.StartsWith("Misc/NEWS")) { var _newCheckRunOutput = new NewCheckRunOutput("Misc/NEWS", "Misc/NEWS entry found!"); var _checkRunUpdate = new CheckRunUpdate { Status = CheckStatus.Completed, Output = _newCheckRunOutput, Conclusion = CheckConclusion.Success }; _ = await Program.client.Check.Run.Update(pull_request["base"]["repo"]["owner"].Value <string>(), pull_request["base"]["repo"]["name"].Value <string>(), check.Id, _checkRunUpdate); return; // !file[:filename].starts_with ? ("Misc/NEWS"); } } var newCheckRunOutput = new NewCheckRunOutput("Misc/NEWS", "Misc/NEWS entry not found and 'skip news' is not added!"); var checkRunUpdate = new CheckRunUpdate { Status = CheckStatus.Completed, Output = newCheckRunOutput, Conclusion = CheckConclusion.Failure }; _ = await Program.client.Check.Run.Update(pull_request["base"]["repo"]["owner"].Value <string>(), pull_request["base"]["repo"]["name"].Value <string>(), check.Id, checkRunUpdate); }
/// <summary> /// Create a CheckRunUpdate based on a check run that needs to be deleted /// </summary> /// <param name="checkRun">The check run that needs to be deleted</param> /// <returns>The deleted check run</returns> private CheckRunUpdate CheckRunForDelete(CheckRun checkRun) { CheckRunUpdate updatedCheckRun = new CheckRunUpdate(); updatedCheckRun.CompletedAt = checkRun.CompletedAt; updatedCheckRun.Status = "completed"; updatedCheckRun.Conclusion = "skipped"; return(updatedCheckRun); }
public async Task EnsuresNonEmptyArguments() { var gitHubClient = Substitute.For <IGitHubClient>(); var client = new ObservableCheckRunsClient(gitHubClient); var update = new CheckRunUpdate { Status = CheckStatus.InProgress }; Assert.Throws <ArgumentException>(() => client.Update("", "repo", 1, update)); Assert.Throws <ArgumentException>(() => client.Update("fake", "", 1, update)); }
public async Task EnsuresNonEmptyArguments() { var connection = Substitute.For <IApiConnection>(); var client = new CheckRunsClient(connection); var update = new CheckRunUpdate { Status = CheckStatus.InProgress }; await Assert.ThrowsAsync <ArgumentException>(() => client.Update("", "repo", 1, update)); await Assert.ThrowsAsync <ArgumentException>(() => client.Update("fake", "", 1, update)); }
public async Task RequestsCorrectUrl() { var gitHubClient = Substitute.For <IGitHubClient>(); var client = new ObservableCheckRunsClient(gitHubClient); var update = new CheckRunUpdate { Status = CheckStatus.InProgress }; client.Update("fake", "repo", 1, update); gitHubClient.Check.Run.Received().Update("fake", "repo", 1, update); }
public async Task UpdateCheckRun(InstallationToken token, string repo, long checkRunId, string status, string conclusion, CheckRunOutput output, DateTime?completeDate = null, List <Action> actions = null) { var url = $"{Base}repos/{token.Account}/{repo}/check-runs/{checkRunId}"; var payload = new CheckRunUpdate { Status = status, Conclusion = conclusion, Output = output, Actions = actions }; if (completeDate != null) { payload.CompletedAt = completeDate.Value.ToUniversalTime(); } await Execute <CheckRun>(HttpMethod.Patch, url, token, JsonConvert.SerializeObject(payload)); }
public async Task RequestsCorrectUrl() { var connection = Substitute.For <IApiConnection>(); var client = new CheckRunsClient(connection); var update = new CheckRunUpdate { Status = CheckStatus.InProgress }; await client.Update("fake", "repo", 1, update); connection.Received().Patch <CheckRun>( Arg.Is <Uri>(u => u.ToString() == "repos/fake/repo/check-runs/1"), update, "application/vnd.github.antiope-preview+json"); }
/// <summary> /// Publish a <see cref="List{T}"/> of <paramref name="diffResults"/>s to the <see cref="IDatabaseContext"/> and GitHub /// </summary> /// <param name="pullRequest">The <see cref="PullRequest"/> the <paramref name="diffResults"/> are for</param> /// <param name="checkRunId">The <see cref="CheckRun.Id"/></param> /// <param name="diffResults">The map of <see cref="MapDiff"/>s to <see cref="MapRegion"/>s</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param> /// <returns>A <see cref="Task"/> representing the running operation</returns> async Task HandleResults(PullRequest pullRequest, long checkRunId, Dictionary <MapDiff, MapRegion> diffResults, CancellationToken cancellationToken) { int formatterCount = 0; var databaseContext = serviceProvider.GetRequiredService <IDatabaseContext>(); var prefix = generalConfiguration.ApplicationPrefix; logger.LogTrace("Generating comment and preparing database query..."); var outputImages = new List <CheckRunImage>() { Capacity = diffResults.Count }; foreach (var kv in diffResults) { var I = kv.Key; var beforeUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, "before")); var afterUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, "after")); var logsUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, "logs")); logger.LogTrace("Adding MapDiff for {0}...", I.MapPath); var region = kv.Value?.ToString() ?? stringLocalizer["ALL"]; outputImages.Add(new CheckRunImage(region, beforeUrl, stringLocalizer["Old"])); outputImages.Add(new CheckRunImage(region, afterUrl, stringLocalizer["New"])); databaseContext.MapDiffs.Add(I); ++formatterCount; } logger.LogTrace("Committing new MapDiffs to the database..."); await databaseContext.Save(cancellationToken).ConfigureAwait(false); logger.LogTrace("Finalizing GitHub Check..."); var ncr = new CheckRunUpdate { DetailsUrl = String.Concat(prefix, FilesController.RouteToBrowse(pullRequest.Base.Repository, checkRunId)), Status = CheckStatus.Completed, CompletedAt = DateTimeOffset.Now, Output = new CheckRunOutput(stringLocalizer["Map Renderings"], stringLocalizer["Before and after renderings of .dmm files"], null, null, outputImages), Conclusion = CheckConclusion.Success }; await serviceProvider.GetRequiredService <IGitHubManager>().UpdateCheckRun(pullRequest.Base.Repository.Id, checkRunId, ncr, cancellationToken).ConfigureAwait(false); }
/// <inheritdoc/> protected override async Task <object> CallGitHubApi(DialogContext dc, Octokit.GitHubClient gitHubClient, CancellationToken cancellationToken = default(CancellationToken)) { if (Owner != null && Name != null && CheckRunId != null && CheckRunUpdate != null) { var ownerValue = Owner.GetValue(dc.State); var nameValue = Name.GetValue(dc.State); var checkRunIdValue = CheckRunId.GetValue(dc.State); var checkRunUpdateValue = CheckRunUpdate.GetValue(dc.State); return(await gitHubClient.Check.Run.Update(ownerValue, nameValue, (Int64)checkRunIdValue, checkRunUpdateValue).ConfigureAwait(false)); } if (RepositoryId != null && CheckRunId != null && CheckRunUpdate != null) { var repositoryIdValue = RepositoryId.GetValue(dc.State); var checkRunIdValue = CheckRunId.GetValue(dc.State); var checkRunUpdateValue = CheckRunUpdate.GetValue(dc.State); return(await gitHubClient.Check.Run.Update((Int64)repositoryIdValue, (Int64)checkRunIdValue, checkRunUpdateValue).ConfigureAwait(false)); } throw new ArgumentNullException("Required [checkRunId,checkRunUpdate] arguments missing for GitHubClient.Check.Run.Update"); }
/// <summary> /// Update some properties of a CheckRunUpdate /// </summary> /// <param name="newUpdateCheckRun">The CheckRunUpdate that needs to be updated</param> /// <param name="result">The result of that new check run</param> private void UpdateCheckRun(CheckRunUpdate newUpdateCheckRun, MergePolicyEvaluationResult result) { var output = FormatOutput(result); newUpdateCheckRun.Output = output; newUpdateCheckRun.Status = CheckStatus.Completed; if (result.Status == MergePolicyEvaluationStatus.Pending) { newUpdateCheckRun.Status = CheckStatus.InProgress; } else if (result.Status == MergePolicyEvaluationStatus.Success) { newUpdateCheckRun.Conclusion = "success"; newUpdateCheckRun.CompletedAt = DateTime.Now; } else { newUpdateCheckRun.Conclusion = "failure"; newUpdateCheckRun.CompletedAt = DateTime.UtcNow; } }
/// <summary> /// Publish a <see cref="List{T}"/> of <paramref name="diffResults"/>s to the <see cref="IDatabaseContext"/> and GitHub /// </summary> /// <param name="pullRequest">The <see cref="PullRequest"/> the <paramref name="diffResults"/> are for</param> /// <param name="installationId">The <see cref="InstallationId.Id"/></param> /// <param name="checkRunId">The <see cref="CheckRun.Id"/></param> /// <param name="diffResults">The <see cref="IconDiff"/>s</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param> /// <returns>A <see cref="Task"/> representing the running operation</returns> async Task HandleResults(PullRequest pullRequest, long installationId, long checkRunId, List <IconDiff> diffResults, CancellationToken cancellationToken) { int formatterCount = 0; var databaseContext = serviceProvider.GetRequiredService <IDatabaseContext>(); logger.LogTrace("Generating check run output and preparing database query..."); var outputImages = new List <CheckRunImage>() { Capacity = diffResults.Count }; foreach (var kv in diffResults) { var beforeUrl = FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, "before"); var afterUrl = FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, "after"); logger.LogTrace("Adding IconDiff for {0}...", kv.DmiPath); outputImages.Add(new CheckRunImage(null, beforeUrl, stringLocalizer["Old - {0}", kv.DmiPath])); outputImages.Add(new CheckRunImage(null, afterUrl, stringLocalizer["New - {0}", kv.DmiPath])); databaseContext.IconDiffs.Add(kv); ++formatterCount; } logger.LogTrace("Committing new IconDiffs to the database..."); await databaseContext.Save(cancellationToken).ConfigureAwait(false); logger.LogTrace("Finalizing GitHub Check..."); var ncr = new CheckRunUpdate { Status = CheckStatus.Completed, CompletedAt = DateTimeOffset.Now, Output = new CheckRunOutput(stringLocalizer["Icon Diffs"], null, null, outputImages), Conclusion = CheckConclusion.Success }; await serviceProvider.GetRequiredService <IGitHubManager>().UpdateCheckRun(pullRequest.Base.Repository.Id, installationId, checkRunId, ncr, cancellationToken).ConfigureAwait(false); }
/// <summary> /// Publish a <see cref="List{T}"/> of <paramref name="diffResults"/>s to the <paramref name="databaseContext"/> and GitHub /// </summary> /// <param name="pullRequest">The <see cref="PullRequest"/> the <paramref name="diffResults"/> are for</param> /// <param name="installationId">The <see cref="InstallationId.Id"/></param> /// <param name="checkRunId">The <see cref="CheckRun.Id"/></param> /// <param name="diffResults">The <see cref="IconDiff"/>s</param> /// <param name="scope">The <see cref="IServiceScope"/> for the operation</param> /// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the operation if any</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param> /// <returns>A <see cref="Task"/> representing the running operation</returns> async Task HandleResults(PullRequest pullRequest, long installationId, long checkRunId, List <IconDiff> diffResults, IServiceScope scope, IDatabaseContext databaseContext, CancellationToken cancellationToken) { logger.LogTrace("Generating check run output and preparing database query..."); var prefix = generalConfiguration.ApplicationPrefix; var commentBuilder = new StringBuilder(); //create a dic of filepath -> IconDiff var dic = new Dictionary <string, List <IconDiff> >(); foreach (var I in diffResults) { if (!dic.TryGetValue(I.DmiPath, out List <IconDiff> list)) { list = new List <IconDiff>(); dic.Add(I.DmiPath, list); } list.Add(I); if (databaseContext != null && (I.Before != null || I.After != null)) { databaseContext.IconDiffs.Add(I); } } foreach (var kv in dic) { commentBuilder.Append(String.Format(CultureInfo.InvariantCulture, "<details><summary>{0}</summary>{5}{5}{1} | {2} | {3} | {4}{5}--- | --- | --- | ---", kv.Key, stringLocalizer["State"], stringLocalizer["Old"], stringLocalizer["New"], stringLocalizer["Status"], Environment.NewLine)); foreach (var I in kv.Value) { var beforeUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, I.FileId, true, (I.Before ?? I.After)?.IsGif ?? false)); var afterUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, I.FileId, false, (I.After ?? I.Before)?.IsGif ?? false)); string status; if (I.Before == null && I.After == null) { status = stringLocalizer["This icon could not be renderered due to an error. Please add a permalink to the .dmi that caused this [here]({0}) to help discover the reason.{1}```{1}{2}{1}```", IssueReportUrl, Environment.NewLine, I.Error]; } else { status = stringLocalizer[I.Before == null ? "Created" : I.After == null ? "Deleted" : "Modified"]; } var stateName = I.StateName; if (stateName == null) { stateName = "(Empty Icon File)"; } else if (stateName.Length == 0) { stateName = "(Default State)"; } commentBuilder.Append(String.Format(CultureInfo.InvariantCulture, "{0}{1} | ![]({2}) | ![]({3}) | {4}", Environment.NewLine, stateName, beforeUrl, afterUrl, status )); } commentBuilder.Append(String.Format(CultureInfo.InvariantCulture, "{0}{0}</details>{0}{0}", Environment.NewLine)); } var comment = String.Format(CultureInfo.CurrentCulture, "{0}{1}{1}{1}{1}<br>{1}{1}{2}", commentBuilder, Environment.NewLine, stringLocalizer["Please report any issues [here]({0}).", IssueReportUrl] ); if (databaseContext != null) { logger.LogTrace("Committing new IconDiffs to the database..."); await databaseContext.Save(cancellationToken).ConfigureAwait(false); } logger.LogTrace("Finalizing GitHub Check..."); var ghm = scope.ServiceProvider.GetRequiredService <IGitHubManager>(); if (comment.Length > 65535) //arbitrary github limit { if (diffResults.Count > 1) //come on dude { logger.LogInformation("Comment too large, employing breakdown procedure."); var cutoff = diffResults.Count / 2; var set1 = new List <IconDiff>(); var set2 = new List <IconDiff>(); for (var I = 0; I < diffResults.Count; ++I) { (I < cutoff ? set1 : set2).Add(diffResults[I]); } var firstCR = HandleResults(pullRequest, installationId, checkRunId, set1, scope, null, cancellationToken); var nextCheckRunId = await ghm.CreateCheckRun(pullRequest.Base.Repository.Id, installationId, new NewCheckRun(stringLocalizer["Additional Diffs - Pull Request #{0} - Set Base: {1}", pullRequest.Number, set2.First().DmiPath], pullRequest.Head.Sha) { StartedAt = DateTimeOffset.Now, Status = CheckStatus.InProgress }, cancellationToken).ConfigureAwait(false); await HandleResults(pullRequest, installationId, nextCheckRunId, set2, scope, null, cancellationToken).ConfigureAwait(false); await firstCR.ConfigureAwait(false); return; } var iPath = diffResults.First().DmiPath; logger.LogWarning("Unable to post check run for icon {0}!", iPath); comment = stringLocalizer["Unable to render this icon ({0}) due to limitations on github check run output!", iPath]; } var ncr = new CheckRunUpdate { Status = CheckStatus.Completed, CompletedAt = DateTimeOffset.Now, Output = new NewCheckRunOutput(stringLocalizer["Icon Diffs"], stringLocalizer["Icons with diff:"]) { Text = comment }, Conclusion = CheckConclusion.Success }; await ghm.UpdateCheckRun(pullRequest.Base.Repository.Id, installationId, checkRunId, ncr, cancellationToken).ConfigureAwait(false); }
/// <summary> /// Updates a check run for a specific commit in a repository /// </summary> /// <remarks> /// See the <a href="https://developer.github.com/v3/checks/runs/#update-a-check-run">Check Runs API documentation</a> for more information. /// </remarks> /// <param name="repositoryId">The Id of the repository</param> /// <param name="checkRunId">The Id of the check run</param> /// <param name="checkRunUpdate">The updates to the check run</param> public IObservable <CheckRun> Update(long repositoryId, long checkRunId, CheckRunUpdate checkRunUpdate) { Ensure.ArgumentNotNull(checkRunUpdate, nameof(checkRunUpdate)); return(_client.Update(repositoryId, checkRunId, checkRunUpdate).ToObservable()); }
/// <summary> /// Updates a check run for a specific commit in a repository /// </summary> /// <remarks> /// See the <a href="https://developer.github.com/v3/checks/runs/#update-a-check-run">Check Runs API documentation</a> for more information. /// </remarks> /// <param name="owner">The owner of the repository</param> /// <param name="name">The name of the repository</param> /// <param name="checkRunId">The Id of the check run</param> /// <param name="checkRunUpdate">The updates to the check run</param> public IObservable <CheckRun> Update(string owner, string name, long checkRunId, CheckRunUpdate checkRunUpdate) { Ensure.ArgumentNotNullOrEmptyString(owner, nameof(owner)); Ensure.ArgumentNotNullOrEmptyString(name, nameof(name)); Ensure.ArgumentNotNull(checkRunUpdate, nameof(checkRunUpdate)); return(_client.Update(owner, name, checkRunId, checkRunUpdate).ToObservable()); }
/// <summary> /// Publish a <see cref="List{T}"/> of <paramref name="diffResults"/>s to the <see cref="IDatabaseContext"/> and GitHub /// </summary> /// <param name="pullRequest">The <see cref="PullRequest"/> the <paramref name="diffResults"/> are for</param> /// <param name="checkRunId">The <see cref="CheckRun.Id"/></param> /// <param name="diffResults">The map of <see cref="MapDiff"/>s to <see cref="MapRegion"/>s</param> /// <param name="scope">The <see cref="IServiceScope"/> for the operation</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param> /// <returns>A <see cref="Task"/> representing the running operation</returns> async Task HandleResults(PullRequest pullRequest, long checkRunId, Dictionary <MapDiff, MapRegion> diffResults, IServiceScope scope, CancellationToken cancellationToken) { int formatterCount = 0; var databaseContext = scope.ServiceProvider.GetRequiredService <IDatabaseContext>(); logger.LogTrace("Generating comment and preparing database query..."); var commentBuilder = new StringBuilder(); var prefix = generalConfiguration.ApplicationPrefix; foreach (var kv in diffResults) { var I = kv.Key; var beforeUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, "before")); var afterUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, "after")); var differenceUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, "diff")); var logsUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, "logs")); commentBuilder.Append(String.Format(CultureInfo.InvariantCulture, "<details><summary>{0}</summary>{11}{11}{1} | {2} | {14}{11}--- | --- | ---{11}![{13}]({3}) | ![{13}]({4}) | ![{13}]({15}){11}{11}{5} | {6} | {7} | {12}{11}--- | --- | --- | ---{11}{8} | {9} | [{7}]({10}) | [{1}]({3}) \\| [{2}]({4}) \\| [{14}]({15}){11}{11}</details>{11}{11}", I.MapPath, stringLocalizer["Old"], stringLocalizer["New"], beforeUrl, afterUrl, stringLocalizer["Status"], stringLocalizer["Region"], stringLocalizer["Logs"], I.BeforeImage != null ? (I.AfterImage != null ? stringLocalizer["Modified"] : stringLocalizer["Deleted"]) : stringLocalizer["Created"], kv.Value?.ToString() ?? stringLocalizer["ALL"], logsUrl, Environment.NewLine, stringLocalizer["Raw"], stringLocalizer["If the image doesn't load, it may be too big for GitHub. Use the \"Raw\" links."], stringLocalizer["Difference"], differenceUrl )); logger.LogTrace("Adding MapDiff for {0}...", I.MapPath); databaseContext.MapDiffs.Add(I); ++formatterCount; } var comment = String.Format(CultureInfo.CurrentCulture, "{0}{2}{2}{2}{2}<br>{1}{2}{2}{2}{2}{3}", commentBuilder, stringLocalizer["Merge base commit used: {0}", pullRequest.Base.Sha], Environment.NewLine, stringLocalizer["Please report any issues [here]({0}).", IssueReportUrl] ); logger.LogTrace("Committing new MapDiffs to the database..."); await databaseContext.Save(cancellationToken).ConfigureAwait(false); logger.LogTrace("Finalizing GitHub Check..."); var ncr = new CheckRunUpdate { DetailsUrl = String.Concat(prefix, FilesController.RouteToBrowse(pullRequest.Base.Repository, checkRunId)), Status = CheckStatus.Completed, CompletedAt = DateTimeOffset.Now, Output = new NewCheckRunOutput(stringLocalizer["Map Renderings"], stringLocalizer["Maps with diff:"]) { Text = comment }, Conclusion = CheckConclusion.Success }; await scope.ServiceProvider.GetRequiredService <IGitHubManager>().UpdateCheckRun(pullRequest.Base.Repository.Id, checkRunId, ncr, cancellationToken).ConfigureAwait(false); }
private async Task StartExecution() { IReadOnlyList <Installation> installations = await this.gitHubAppClient.GitHubApps.GetAllInstallationsForCurrent().ConfigureAwait(false); try { if (!this.IsGitHubInstallationClientValid()) { throw new InvalidOperationException("Error: gitHubInstallationClient is invalid."); } if (IsPullRequest) { ICheckSuitesClient checkSuiteClient = gitHubInstallationClient.Check.Suite; CheckSuitesResponse x = await checkSuiteClient.GetAllForReference(CurrentRepository.Id, CommitSha).ConfigureAwait(false); if (x.TotalCount > 0) { long checkSuiteId = x.CheckSuites.FirstOrDefault().Id; bool res = await checkSuiteClient.Rerequest(CurrentRepository.Id, checkSuiteId); } else { var newCheckSuite = new NewCheckSuite(CommitSha); try { CheckSuite suite = await checkSuiteClient.Create( CurrentRepository.Owner.Login, CurrentRepository.Name, newCheckSuite) .ConfigureAwait(false); } catch (Exception ex) { } } return; } ICheckRunsClient checkRunClient = gitHubInstallationClient.Check.Run; // Create a new heckRun in GitHub var newCheckRun = new NewCheckRun("ScanX", CommitSha) { Status = CheckStatus.Queued, }; CheckRun checkRun = await checkRunClient.Create( CurrentRepository.Owner.Login, CurrentRepository.Name, newCheckRun) .ConfigureAwait(false); // --- Downoad a ZIP --- byte[] buffer = await ScanHelper.DownloadRepoZip(gitHubInstallationClient, CurrentRepository.Id, CommitSha).ConfigureAwait(false); int size = buffer.Length; // Upload ZIP to a storage blob string blobName = $"{RequestId.ToString()}"; string blobUri = await ScanHelper.UploadBufferToStorage(buffer, blobName); // Update check's status to "in progress" CheckRunUpdate checkRunUpdate = new CheckRunUpdate { Status = CheckStatus.InProgress, Name = checkRun.Name }; checkRun = await checkRunClient.Update(CurrentRepository.Id, checkRun.Id, checkRunUpdate).ConfigureAwait(false); // --- Start a scan --- // Simulate sending of a message to a SB queue // Create worker notification message MalwareDeterminationRequest scanRequest = new MalwareDeterminationRequest(); scanRequest.ClientId = "GitHubScanX"; scanRequest.FileName = $"{RequestId.ToString()}.zip"; scanRequest.FileSizeInBytes = 1000; //dummy scanRequest.RequestId = RequestId; scanRequest.Uri = new Uri(blobUri); // Notify worker (aka put the notification message to a queue) ScanXMock mock = new ScanXMock(); await mock.SendScanRequest(scanRequest).ConfigureAwait(false); // --- Poll for a scan completion --- MalwareDeterminationResult scanResult; do { await Task.Delay(500).ConfigureAwait(false); if (await mock.TryGetResult(RequestId)) { scanResult = await mock.GetResult(RequestId).ConfigureAwait(false); break; } }while (true); //!!!! for POC only checkRunUpdate.Status = CheckStatus.Completed; checkRunUpdate.CompletedAt = DateTime.UtcNow; checkRunUpdate.Conclusion = scanResult.WorkStatus == WorkStatus.Clean ? CheckConclusion.Success : CheckConclusion.Failure; if (checkRunUpdate.Conclusion == CheckConclusion.Failure) { checkRunUpdate.Output = new NewCheckRunOutput( "Scan Report", $"GitScan detected {scanResult.ConfirmedMalwares.Count()} infected files. See details below."); checkRunUpdate.Output.Text = "| File Path| Malware Type| AV Engines|\n"; checkRunUpdate.Output.Text += "|:---|:---|:---|\n"; foreach (var entry in scanResult.ConfirmedMalwares) { checkRunUpdate.Output.Text += $"|{entry.FileName}|{entry.MalwareInfo}|{string.Join(",", entry.AvEngines.ToArray())}"; } } checkRun = await checkRunClient.Update(CurrentRepository.Id, checkRun.Id, checkRunUpdate).ConfigureAwait(false); } catch (Exception ex) { Console.WriteLine($"Exception: {ex.Message}"); } }
/// <summary> /// Publish a <see cref="List{T}"/> of <paramref name="diffResults"/>s to the <paramref name="databaseContext"/> and GitHub /// </summary> /// <param name="pullRequest">The <see cref="PullRequest"/> the <paramref name="diffResults"/> are for</param> /// <param name="installationId">The <see cref="InstallationId.Id"/></param> /// <param name="checkRunId">The <see cref="CheckRun.Id"/></param> /// <param name="diffResults">The <see cref="IconDiff"/>s</param> /// <param name="scope">The <see cref="IServiceScope"/> for the operation</param> /// <param name="databaseContext">The <see cref="IDatabaseContext"/> for the operation</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> for the operation</param> /// <returns>A <see cref="Task"/> representing the running operation</returns> async Task HandleResults(PullRequest pullRequest, long installationId, long checkRunId, List <IconDiff> diffResults, IServiceScope scope, IDatabaseContext databaseContext, CancellationToken cancellationToken) { int formatterCount = 0; logger.LogTrace("Generating check run output and preparing database query..."); var outputImages = new List <CheckRunImage>() { Capacity = diffResults.Count }; var prefix = generalConfiguration.ApplicationPrefix; var commentBuilder = new StringBuilder(); //create a dic of filepath -> IconDiff var dic = new Dictionary <string, List <IconDiff> >(); foreach (var I in diffResults) { if (!dic.TryGetValue(I.DmiPath, out List <IconDiff> list)) { list = new List <IconDiff>(); dic.Add(I.DmiPath, list); } list.Add(I); databaseContext.IconDiffs.Add(I); } foreach (var kv in dic) { commentBuilder.Append(String.Format(CultureInfo.InvariantCulture, "<details><summary>{0}</summary>{5}{5}{1} | {2} | {3} | {4}{5}--- | --- | --- | ---", kv.Key, stringLocalizer["State"], stringLocalizer["Old"], stringLocalizer["New"], stringLocalizer["Status"], Environment.NewLine)); foreach (var I in kv.Value) { var beforeUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, true)); var afterUrl = String.Concat(prefix, FilesController.RouteTo(pullRequest.Base.Repository, checkRunId, formatterCount, false)); commentBuilder.Append(String.Format(CultureInfo.InvariantCulture, "{0}{1} | ![]({2}) | ![]({3}) | {4}", Environment.NewLine, I.StateName, beforeUrl, afterUrl, stringLocalizer[I.Before == null ? "Created" : I.After == null ? "Deleted" : "Modified"] )); ++formatterCount; } commentBuilder.Append(String.Format(CultureInfo.InvariantCulture, "{0}{0}</details>{0}{0}", Environment.NewLine)); } var comment = String.Format(CultureInfo.CurrentCulture, "{0}{1}{1}{1}{1}<br>{1}{1}{2}", commentBuilder, Environment.NewLine, stringLocalizer["Please report any issues [here]({0}).", IssueReportUrl] ); logger.LogTrace("Committing new IconDiffs to the database..."); await databaseContext.Save(cancellationToken).ConfigureAwait(false); logger.LogTrace("Finalizing GitHub Check..."); var ncr = new CheckRunUpdate { Status = CheckStatus.Completed, CompletedAt = DateTimeOffset.Now, Output = new CheckRunOutput(stringLocalizer["Icon Diffs"], stringLocalizer["Icons with diff:"], comment, null, outputImages), Conclusion = CheckConclusion.Success }; await scope.ServiceProvider.GetRequiredService <IGitHubManager>().UpdateCheckRun(pullRequest.Base.Repository.Id, installationId, checkRunId, ncr, cancellationToken).ConfigureAwait(false); }