private async Task <QuantifierResult> QuantifyPullRequest(PullRequestEventPayload payload)
        {
            var gitHubClientAdapter =
                await gitHubClientAdapterFactory.GetGitHubClientAdapterAsync(
                    payload.Installation.Id,
                    new Uri(payload.PullRequest.HtmlUrl).DnsSafeHost);

            var quantifierInput = await GetQuantifierInputFromPullRequest(payload, gitHubClientAdapter);

            var context = await GetContextFromRepoIfPresent(payload, gitHubClientAdapter);

            var quantifyClient         = new QuantifyClient(context);
            var quantifierClientResult = await quantifyClient.Compute(quantifierInput);

            await ApplyLabelToPullRequest(
                payload,
                gitHubClientAdapter,
                quantifierClientResult,
                quantifyClient.Context.Thresholds.Select(t => t.Label));

            var quantifierContextLink = !string.IsNullOrWhiteSpace(context)
                ? $"{payload.Repository.HtmlUrl}/blob/{payload.Repository.DefaultBranch}/prquantifier.yaml"
                : string.Empty;

            await UpdateCommentOnPullRequest(
                payload,
                gitHubClientAdapter,
                quantifierClientResult,
                quantifierContextLink);

            return(quantifierClientResult);
        }
Exemple #2
0
 public PullRequestContext(string repositoryName, PullRequestEventPayload payload, IConnection githubConnection, ILogger logger)
 {
     RepositoryName   = repositoryName;
     Payload          = payload;
     GithubConnection = githubConnection;
     Logger           = logger;
 }
        private async Task UpdateCommentOnPullRequest(
            PullRequestEventPayload payload,
            IGitHubClientAdapter gitHubClientAdapter,
            QuantifierResult quantifierClientResult,
            string quantifierContextLink)
        {
            // delete existing comments created by us
            var existingComments =
                await gitHubClientAdapter.GetIssueCommentsAsync(payload.Repository.Id, payload.PullRequest.Number);

            var existingCommentsCreatedByUs = existingComments.Where(
                ec =>
                ec.User.Login.Equals($"{gitHubClientAdapter.GitHubAppSettings.Name}[bot]"));

            foreach (var existingComment in existingCommentsCreatedByUs)
            {
                await gitHubClientAdapter.DeleteIssueCommentAsync(payload.Repository.Id, existingComment.Id);
            }

            // create a new comment on the issue
            var comment = await quantifierClientResult.ToMarkdownCommentAsync(
                payload.Repository.HtmlUrl,
                quantifierContextLink,
                payload.PullRequest.HtmlUrl,
                payload.PullRequest.User.Login,
                ShouldPostAnonymousFeedbackLink(payload),
                new MarkdownCommentOptions { CollapsePullRequestQuantifiedSection = true });

            await gitHubClientAdapter.CreateIssueCommentAsync(
                payload.Repository.Id,
                payload.PullRequest.Number,
                comment);
        }
Exemple #4
0
 /// <inheritdoc />
 public void ProcessPayload(PullRequestEventPayload payload)
 {
     if ((payload.Action != "opened" && payload.Action != "synchronize") || payload.PullRequest.State.Value != ItemState.Open)
     {
         return;
     }
     backgroundJobClient.Enqueue(() => ScanPullRequest(payload.Repository.Id, payload.PullRequest.Number, payload.Installation.Id, JobCancellationToken.Null));
 }
        /// <inheritdoc />
        public Task ProcessPayload(PullRequestEventPayload payload, CancellationToken cancellationToken)
        {
            if (payload == null)
            {
                throw new ArgumentNullException(nameof(payload));
            }

            return(CheckMergePullRequest(payload.PullRequest.Number, cancellationToken));
        }
Exemple #6
0
        /// <inheritdoc />
        public async Task ProcessPayload(PullRequestEventPayload payload, CancellationToken cancellationToken)
        {
            if (payload == null)
            {
                throw new ArgumentNullException(nameof(payload));
            }

            await CheckMergePullRequest(payload.PullRequest, cancellationToken).ConfigureAwait(false);
        }
Exemple #7
0
 public CheckSuiteRequestHandler(PullRequestEventPayload payload, IPrivateKeySource keySource, Guid requestId)
 {
     GithubInstallationId = payload.Installation.Id;
     CurrentRepository    = payload.Repository;
     CommitSha            = payload.PullRequest.Head.Sha;
     IsPullRequest        = true;
     RequestId            = requestId;
     Init(keySource);
 }
        private async Task ApplyLabelToPullRequest(
            PullRequestEventPayload payload,
            IGitHubClientAdapter gitHubClientAdapter,
            QuantifierResult quantifierClientResult,
            IEnumerable <string> labelOptionsFromContext)
        {
            // create a new label in the repository if doesn't exist
            try
            {
                var existingLabel = await gitHubClientAdapter.GetLabelAsync(
                    payload.Repository.Id,
                    quantifierClientResult.Label);
            }
            catch (NotFoundException)
            {
                // create new label
                var color = Color.FromName(quantifierClientResult.Color);
                await gitHubClientAdapter.CreateLabelAsync(
                    payload.Repository.Id,
                    new NewLabel(quantifierClientResult.Label, ConvertToHex(color)));
            }

            // remove any previous labels applied if it's different
            // labels do not have the property of who applied them
            // so we use string matching against the label options present in the context
            // if label strings have changed in context since last PR update, this will break
            var existingLabels =
                await gitHubClientAdapter.GetIssueLabelsAsync(payload.Repository.Id, payload.PullRequest.Number);

            var existingLabelsByUs = existingLabels.Select(el => el.Name).Intersect(labelOptionsFromContext).ToList();

            foreach (var existingLabel in existingLabelsByUs)
            {
                if (existingLabel == quantifierClientResult.Label)
                {
                    continue;
                }

                await gitHubClientAdapter.RemoveLabelFromIssueAsync(
                    payload.Repository.Id,
                    payload.PullRequest.Number,
                    existingLabel);
            }

            // apply new label to pull request
            await gitHubClientAdapter.ApplyLabelAsync(
                payload.Repository.Id,
                payload.PullRequest.Number,
                new[] { quantifierClientResult.Label });
        }
        private async Task <QuantifierInput> GetQuantifierInputFromPullRequest(PullRequestEventPayload payload, IGitHubClientAdapter gitHubClientAdapter)
        {
            // get pull request files
            var pullRequestFiles = await gitHubClientAdapter.GetPullRequestFilesAsync(
                payload.Repository.Id,
                payload.PullRequest.Number);

            // convert to quantifier input
            var quantifierInput = new QuantifierInput();

            foreach (var pullRequestFile in pullRequestFiles)
            {
                if (pullRequestFile.Patch == null)
                {
                    continue;
                }

                var changeType = GitChangeType.Modified;
                switch (pullRequestFile.Status)
                {
                case "modified":
                    break;

                case "added":
                    changeType = GitChangeType.Added;
                    break;

                case "deleted":
                    changeType = GitChangeType.Deleted;
                    break;
                }

                var fileExtension = !string.IsNullOrWhiteSpace(pullRequestFile.FileName)
                    ? new FileInfo(pullRequestFile.FileName).Extension
                    : string.Empty;
                var gitFilePatch = new GitFilePatch(
                    pullRequestFile.FileName,
                    fileExtension)
                {
                    ChangeType           = changeType,
                    AbsoluteLinesAdded   = pullRequestFile.Additions,
                    AbsoluteLinesDeleted = pullRequestFile.Deletions,
                    DiffContent          = pullRequestFile.Patch,
                };
                quantifierInput.Changes.Add(gitFilePatch);
            }

            return(quantifierInput);
        }
        private async Task <string> GetContextFromRepoIfPresent(PullRequestEventPayload payload, IGitHubClientAdapter gitHubClientAdapter)
        {
            // get context if present
            string context = null;

            try
            {
                var rawContext = await gitHubClientAdapter.GetRawFileAsync(
                    payload.Repository.Owner.Login,
                    payload.Repository.Name,
                    "/prquantifier.yaml");

                context = Encoding.UTF8.GetString(rawContext);
            }
            catch (NotFoundException)
            {
            }
            catch
            {
                // ignored
            }

            return(context);
        }
        private async Task <QuantifierResult> QuantifyPullRequest(PullRequestEventPayload payload)
        {
            var gitHubClientAdapter =
                await gitHubClientAdapterFactory.GetGitHubClientAdapterAsync(
                    payload.Installation.Id,
                    new Uri(payload.PullRequest.HtmlUrl).DnsSafeHost);

            // get pull request
            var pullRequest = await gitHubClientAdapter.GetPullRequestAsync(
                payload.Repository.Id,
                payload.PullRequest.Number);

            // get pull request files
            var pullRequestFiles = await gitHubClientAdapter.GetPullRequestFilesAsync(
                payload.Repository.Id,
                payload.PullRequest.Number);

            // convert to quantifier input
            var quantifierInput = new QuantifierInput();

            foreach (var pullRequestFile in pullRequestFiles)
            {
                if (pullRequestFile.Patch == null)
                {
                    continue;
                }

                var changeType = GitChangeType.Modified;
                switch (pullRequestFile.Status)
                {
                case "modified":
                    break;

                case "added":
                    changeType = GitChangeType.Added;
                    break;

                case "deleted":
                    changeType = GitChangeType.Deleted;
                    break;
                }

                var fileExtension = !string.IsNullOrWhiteSpace(pullRequestFile.FileName)
                    ? new FileInfo(pullRequestFile.FileName).Extension
                    : string.Empty;
                var gitFilePatch = new GitFilePatch(
                    pullRequestFile.FileName,
                    fileExtension)
                {
                    ChangeType           = changeType,
                    AbsoluteLinesAdded   = pullRequestFile.Additions,
                    AbsoluteLinesDeleted = pullRequestFile.Deletions,
                    DiffContent          = pullRequestFile.Patch,
                };
                quantifierInput.Changes.Add(gitFilePatch);
            }

            // get context if present
            string context = null;

            try
            {
                var rawContext = await gitHubClientAdapter.GetRawFileAsync(
                    payload.Repository.Owner.Login,
                    payload.Repository.Name,
                    "/prquantifier.yaml");

                context = Encoding.UTF8.GetString(rawContext);
            }
            catch (NotFoundException)
            {
            }
            catch
            {
                // ignored
            }

            var quantifyClient         = new QuantifyClient(context);
            var quantifierClientResult = await quantifyClient.Compute(quantifierInput);

            // create a new label in the repository if doesn't exist
            try
            {
                var existingLabel = await gitHubClientAdapter.GetLabelAsync(
                    payload.Repository.Id,
                    quantifierClientResult.Label);
            }
            catch (NotFoundException)
            {
                // create new label
                var color = Color.FromName(quantifierClientResult.Color);
                await gitHubClientAdapter.CreateLabelAsync(
                    payload.Repository.Id,
                    new NewLabel(quantifierClientResult.Label, ConvertToHex(color)));
            }

            // apply label to pull request
            await gitHubClientAdapter.ApplyLabelAsync(
                payload.Repository.Id,
                payload.PullRequest.Number,
                new[] { quantifierClientResult.Label });

            // create a comment on the issue
            var defaultBranch         = payload.Repository.DefaultBranch;
            var quantifierContextLink = $"{payload.Repository.HtmlUrl}/blob/{defaultBranch}/prquantifier.yaml";
            var comment = await quantifierClientResult.ToMarkdownCommentAsync(
                payload.Repository.HtmlUrl,
                quantifierContextLink,
                payload.PullRequest.HtmlUrl,
                payload.PullRequest.User.Login);

            await gitHubClientAdapter.CreateIssueCommentAsync(
                payload.Repository.Id,
                payload.PullRequest.Number,
                comment);

            return(quantifierClientResult);
        }
        /// <summary>
        /// Page has been loaded - called by GitHub when a pull request is opened, merged or closed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Page_Load(object sender, EventArgs e)
        {
            string json = GetJsonFromInputStream();

            if (string.IsNullOrWhiteSpace(json))
            {
                Response.StatusCode = 200;
                return;
            }

            SimpleJsonSerializer    octokitSerialiser = new SimpleJsonSerializer();
            PullRequestEventPayload payload           = octokitSerialiser.Deserialize <PullRequestEventPayload>(json);

            if (payload == null || payload.PullRequest == null)
            {
                ShowMessage("Cannot find pull request in GitHub JSON.");
                Response.StatusCode = 400; // Bad request
            }
            else if (!payload.PullRequest.Merged)
            {
                ShowMessage("Pull request not merged - ignored");
            }
            else if (payload.PullRequest.GetIssueID() == -1)
            {
                ShowMessage("Pull request doesn't reference an issue.");
            }
            else if (payload.Repository.Owner.Login == "APSIMInitiative")
            {
                if (payload.Repository.Name == "ApsimX")
                {
                    // If an ApsimX Pull Request has been merged, start a CreateInstallation job on Jenkins.
                    string issueNumber = payload.PullRequest.GetIssueID().ToString();
                    string pullId      = payload.PullRequest.Number.ToString();
                    string author      = payload.PullRequest.User.Login;
                    string token       = GetJenkinsToken();
                    string issueTitle  = payload.PullRequest.GetIssueTitle("APSIMInitiative", "ApsimX");
                    bool   released    = payload.PullRequest.FixesAnIssue();
                    string mergeCommit = payload.PullRequest.MergeCommitSha;
                    string jenkinsUrl  = $"https://jenkins.apsim.info/job/apsim-release/buildWithParameters?token={token}&ISSUE_NUMBER={issueNumber}&PULL_ID={pullId}&COMMIT_AUTHOR={author}&ISSUE_TITLE={issueTitle}&RELEASED={released}&MERGE_COMMIT={mergeCommit}";
                    if (released)
                    {
                        WebUtilities.CallRESTService <object>(jenkinsUrl);
                        ShowMessage(string.Format("Triggered a deploy step for {0}'s pull request {1} - {2}", author, pullId, payload.PullRequest.Title));
                    }
                    else
                    {
                        ShowMessage($"No release will be generated {author}'s pull request #{pullId} - {payload.PullRequest.Title} as it doesn't resolve an issue");
                    }
                }
                else if (payload.Repository.Name == "APSIMClassic")
                {
                    // If an APSIM Classic Pull Request has been created, and it fixes an issue, start a ReleaseClassic job on Jenkins
                    string pullId = payload.PullRequest.Number.ToString();
                    string author = payload.PullRequest.User.Login;
                    if (payload.PullRequest.FixesAnIssue())
                    {
                        string token      = GetJenkinsReleaseClassicToken();
                        string sha        = payload.PullRequest.MergeCommitSha;
                        string jenkinsUrl = $"http://apsimdev.apsim.info:8080/jenkins/job/ReleaseClassic/buildWithParameters?token={token}&PULL_ID={pullId}&SHA1={sha}";
                        WebUtilities.CallRESTService <object>(jenkinsUrl);
                        ShowMessage($"Triggered a deploy step for {author}'s pull request #{pullId} - {payload.PullRequest.Title}");
                    }
                    else
                    {
                        ShowMessage($"No release will be generated {author}'s pull request #{pullId} - {payload.PullRequest.Title} as it doesn't resolve an issue");
                    }
                }
            }
        }
Exemple #13
0
        public ActionResult Default()
        {
            string actionName = Request.Headers.Get("X-GITHUB-EVENT");

            // Only the below events will be handled
            if (!actionName.Equals("check_suite", StringComparison.InvariantCultureIgnoreCase) &&
                !actionName.Equals("check_run", StringComparison.InvariantCultureIgnoreCase) &&
                !actionName.Equals("pull_request", StringComparison.InvariantCultureIgnoreCase))
            {
                return(new HttpStatusCodeResult(200));
            }

            // Obtain the body signature
            string messageSignature = Request.Headers.Get("X-HUB-Signature");

            if (string.IsNullOrEmpty(messageSignature))
            {
                return(new HttpStatusCodeResult(400));
            }

            // Read the body
            string body = GetRequestPostData(Request);

            // Validate message integrity
            if (!RequestPayloadHelper.ValidateSender(body, messageSignature, GitScanAppConfig.GetValue(Constants.GlobalSection, Constants.GitHubAppWebhookSecretKey)))
            {
                return(new HttpStatusCodeResult(400));
            }

            Guid requestId = Guid.NewGuid();

            if (actionName.Equals("check_run", StringComparison.InvariantCultureIgnoreCase))
            {
                CheckRunEventPayload checkRunPayload = RequestPayloadHelper.Parse <CheckRunEventPayload>(body);

                if (checkRunPayload.Action.Equals("rerequested", StringComparison.InvariantCultureIgnoreCase))
                {
                    CheckSuiteRequestHandler handler = new CheckSuiteRequestHandler(checkRunPayload, PrivateKeySource.Value, requestId);
                    handler.Go();
                    return(new HttpStatusCodeResult(200));
                }
                else
                {
                    return(new HttpStatusCodeResult(200));
                }
            }

            if (actionName.Equals("pull_request", StringComparison.InvariantCultureIgnoreCase))
            {
                PullRequestEventPayload pullPayload = RequestPayloadHelper.Parse <PullRequestEventPayload>(body);
                if (pullPayload.Action.Equals("opened", StringComparison.InvariantCultureIgnoreCase))
                {
                    CheckSuiteRequestHandler handler = new CheckSuiteRequestHandler(pullPayload, PrivateKeySource.Value, requestId);
                    handler.Go().Wait();
                    return(new HttpStatusCodeResult(200));
                }
                else
                {
                    return(new HttpStatusCodeResult(200));
                }
            }

            CheckSuiteEventPayload payload = RequestPayloadHelper.Parse(body);

            if (!payload.Action.Equals("rerequested", StringComparison.OrdinalIgnoreCase) &&
                (payload.CheckSuite.PullRequests == null || payload.CheckSuite.PullRequests.Count == 0))
            {
                return(new HttpStatusCodeResult(200));
            }

            if (!payload.Action.Equals("completed", StringComparison.OrdinalIgnoreCase))
            {
                CheckSuiteRequestHandler handler = new CheckSuiteRequestHandler(payload, PrivateKeySource.Value, requestId);
                handler.Go();
            }

            return(new HttpStatusCodeResult(200));
        }
 public PullRequestContext(PullRequestEventPayload payload, IConnection githubConnection, ILogger logger)
 {
     Payload          = payload;
     GithubConnection = githubConnection;
     Logger           = logger;
 }
 // only post anonymous feedback link in case of github.com flavor
 private bool ShouldPostAnonymousFeedbackLink(PullRequestEventPayload payload)
 {
     return(new Uri(payload.PullRequest.HtmlUrl).DnsSafeHost.Equals("github.com"));
 }