Exemple #1
0
        public async Task <GitHubPullRequest> SearchPullRequestsAsync(
            GitHubProject project,
            string headPrefix,
            string author,
            // Parameter ignored: hasn't been important so far and harder in VSTS.
            string sortType = "created")
        {
            string url = $"{GitApiBaseUrl(project)}pullrequests" +
                         $"?searchCriteria.sourceRefName=refs/heads/{headPrefix}" +
                         $"&searchCriteria.status=active" +
                         $"&searchCriteria.creatorId={author}";

            using (HttpResponseMessage response = await _httpClient.GetAsync(url))
            {
                await EnsureSuccessfulAsync(response);

                JObject queryResponse = JObject.Parse(await response.Content.ReadAsStringAsync());

                int count = queryResponse["count"].Value <int>();

                GitHubPullRequest[] prs = queryResponse["value"]
                                          .Values <JObject>()
                                          .Select(o => new GitHubPullRequest
                {
                    Number = o["pullRequestId"].Value <int>(),
                    Title  = o["title"].Value <string>(),
                    // Description seems optional and may not be returned.
                    Body = o["description"]?.Value <string>() ?? "",
                    Head = new GitHubHead
                    {
                        Sha   = o["lastMergeSourceCommit"]["commitId"].Value <string>(),
                        Ref   = o["sourceRefName"].Value <string>().Substring("refs/heads/".Length),
                        Label = o["sourceRefName"].Value <string>().Substring("refs/heads/".Length),
                        User  = new GitHubUser
                        {
                            Login = o["createdBy"]["id"].Value <string>()
                        }
                    }
                })
                                          .ToArray();

                if (count == 0)
                {
                    Trace.TraceInformation($"Could not find any pull request with head {headPrefix}");
                    return(null);
                }

                if (count > 1)
                {
                    IEnumerable <int> allIds = prs.Select(pr => pr.Number);
                    Trace.TraceInformation(
                        $"Found multiple pull requests with head {headPrefix}. " +
                        $"On this page, found {string.Join(", ", allIds)}");
                }

                // Get the PR with the highest ID if there are multiple, but this is not expected
                // in current VersionTools scenarios.
                return(prs.OrderBy(pr => pr.Number).Last());
            }
        }
Exemple #2
0
 public Task <GitReference> PostReferenceAsync(
     GitHubProject project,
     string @ref,
     string sha)
 {
     throw new NotImplementedException();
 }
Exemple #3
0
        private void TraceListenedExecute()
        {
            var auth = new GitHubAuth(GitHubAuthToken, GitHubUser, GitHubEmail);

            using (GitHubClient client = new GitHubClient(auth))
            {
                var origin = new GitHubProject(ProjectRepoName, GitHubUser);

                var upstreamBranch = new GitHubBranch(
                    ProjectRepoBranch,
                    new GitHubProject(ProjectRepoName, ProjectRepoOwner));

                string body = Body ?? string.Empty;

                if (NotifyGitHubUsers != null)
                {
                    body += PullRequestCreator.NotificationString(NotifyGitHubUsers.Select(item => item.ItemSpec));
                }

                var prCreator = new PullRequestCreator(client.Auth, GitHubAuthor);
                prCreator.CreateOrUpdateAsync(
                    CommitMessage,
                    CommitMessage + $" ({ProjectRepoBranch})",
                    body,
                    upstreamBranch,
                    origin,
                    new PullRequestOptions
                {
                    ForceCreate           = AlwaysCreateNewPullRequest,
                    MaintainersCanModify  = MaintainersCanModifyPullRequest,
                    TrackDiscardedCommits = TrackDiscardedCommits
                }).Wait();
            }
        }
Exemple #4
0
        public async Task PostCommentAsync(GitHubProject project, int issueNumber, string message)
        {
            EnsureAuthenticated();

            string body = JsonConvert.SerializeObject(new
            {
                comments = new[]
                {
                    new
                    {
                        content = message
                    },
                },
                status = "closed"
            }, Formatting.Indented);

            string pullUrl = $"{GitApiBaseUrl(project)}pullrequests/{issueNumber}/threads";

            var bodyContent = new StringContent(body, Encoding.UTF8, "application/json");

            using (HttpResponseMessage response = await _httpClient.PostAsync(pullUrl, bodyContent))
            {
                await EnsureSuccessfulAsync(response);
            }
        }
Exemple #5
0
 public Task <GitHubContents> GetGitHubFileAsync(
     string path,
     GitHubProject project,
     string @ref)
 {
     throw new NotImplementedException();
 }
Exemple #6
0
        public async Task <GitCommit> PostCommitAsync(
            GitHubProject project,
            string message,
            string tree,
            string[] parents)
        {
            EnsureAuthenticated();

            string body = JsonConvert.SerializeObject(new
            {
                message,
                tree,
                parents
            }, Formatting.Indented);

            string url = $"https://api.github.com/repos/{project.Segments}/git/commits";

            var bodyContent = new StringContent(body);

            using (HttpResponseMessage response = await _httpClient.PostAsync(url, bodyContent))
            {
                Trace.TraceInformation($"Posting new commit for tree '{tree}' with parents '{string.Join(", ", parents)}' to {project.Segments}");
                return(await DeserializeSuccessfulAsync <GitCommit>(response));
            }
        }
Exemple #7
0
        public async Task <GitCommit> GetCommitAsync(GitHubProject project, string sha)
        {
            string url = $"{GitApiBaseUrl(project)}commits/{sha}";

            using (HttpResponseMessage response = await _httpClient.GetAsync(url))
            {
                Trace.TraceInformation($"Getting info about commit {sha} in {project.Segments}");

                await EnsureSuccessfulAsync(response);

                JObject o = JObject.Parse(await response.Content.ReadAsStringAsync());

                return(new GitCommit
                {
                    Sha = o["commitId"].Value <string>(),
                    Message = o["comment"]?.Value <string>(),
                    HtmlUrl = o["_links"]?["web"]?["href"]?.Value <string>(),
                    Author = new GitCommitUser
                    {
                        Name = o["author"]?["name"]?.Value <string>(),
                        Email = o["author"]?["email"]?.Value <string>(),
                    },
                    Committer = new GitCommitUser
                    {
                        Name = o["committer"]?["name"]?.Value <string>(),
                        Email = o["committer"]?["email"]?.Value <string>(),
                    }
                });
            }
        }
Exemple #8
0
        public async Task <GitReference> PatchReferenceAsync(GitHubProject project, string @ref, string sha, bool force)
        {
            EnsureAuthenticated();

            string body = JsonConvert.SerializeObject(new
            {
                sha,
                force
            }, Formatting.Indented);

            string url = $"https://api.github.com/repos/{project.Segments}/git/refs/{@ref}";

            var bodyContent            = new StringContent(body);
            HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("PATCH"), url)
            {
                Content = bodyContent
            };

            using (HttpResponseMessage response = await _httpClient.SendAsync(request))
            {
                Trace.TraceInformation($"Patching reference '{@ref}' to '{sha}' with force={force} in {project.Segments}");
                try
                {
                    return(await DeserializeSuccessfulAsync <GitReference>(response));
                }
                catch (HttpFailureResponseException e) when(
                    e.HttpStatusCode == UnprocessableEntityStatusCode &&
                    JObject.Parse(e.Content)["message"]?.Value <string>() == NotFastForwardMessage)
                {
                    throw new NotFastForwardUpdateException(
                              $"Could not update {project.Segments} '{@ref}' to '{sha}': " +
                              NotFastForwardMessage);
                }
            }
        }
Exemple #9
0
        public async Task <GitReference> GetReferenceAsync(GitHubProject project, string @ref)
        {
            // A specific common reason to escape is '/' => '%2F'.
            string escapedRef = Uri.EscapeDataString(@ref);
            string url        = $"{GitApiBaseUrl(project)}refs?filter={escapedRef}";

            using (HttpResponseMessage response = await _httpClient.GetAsync(url))
            {
                Trace.TraceInformation($"Getting info about ref {@ref} in {project.Segments}");

                await EnsureSuccessfulAsync(response);

                JObject responseRefs = JObject.Parse(await response.Content.ReadAsStringAsync());

                if (responseRefs["count"].Value <int>() == 0)
                {
                    Trace.TraceInformation($"Could not find ref '{@ref}'");
                    return(null);
                }

                JObject o = responseRefs["value"].Values <JObject>().First();

                return(new GitReference
                {
                    Ref = o["name"].Value <string>(),
                    Object = new GitReferenceObject
                    {
                        Sha = o["objectId"].Value <string>()
                    },
                });
            }
        }
 public async Task <OrchestratedBuildModel> FetchManifestAsync(
     GitHubProject project,
     string @ref,
     string basePath)
 {
     return(OrchestratedBuildModel.Parse(await FetchModelXmlAsync(project, @ref, basePath)));
 }
        // GET: /<controller>/
        public IActionResult Index()
        {
            //return View();
            var projects = GitHubProject.GetProjects();

            return(View(projects));
        }
Exemple #12
0
 public Task <GitReference> PatchReferenceAsync(
     GitHubProject project,
     string @ref,
     string sha,
     bool force)
 {
     throw new NotImplementedException();
 }
Exemple #13
0
 public Task <GitCommit> PostCommitAsync(
     GitHubProject project,
     string message,
     string tree,
     string[] parents)
 {
     throw new NotImplementedException();
 }
Exemple #14
0
        private List <GitHubColumns> GetGitHubColumnses(GitHubProject selectedProject)
        {
            var projectsColumnsRequest = new RestRequest($@"/projects/{selectedProject.id}/columns", Method.GET);

            var columnsJson = _restClient.Execute(projectsColumnsRequest).Content;
            var columns     = JsonConvert.DeserializeObject <List <GitHubColumns> >(columnsJson);

            return(columns);
        }
        public async Task TestPushConflictAsync()
        {
            var mockGitHub = new Mock <IGitHubClient>(MockBehavior.Strict);

            var client = new BuildManifestClient(mockGitHub.Object);
            var build  = new OrchestratedBuildModel(new BuildIdentity {
                Name = "orch", BuildId = "123"
            });
            var    proj     = new GitHubProject("versions", "dotnet");
            string @ref     = "heads/master";
            string basePath = "build-info/dotnet/product/cli/master";
            string message  = "Test build upload commit";

            string fakeCommitHash    = "fakeCommitHash";
            string fakeTreeHash      = "fakeTreeHash";
            string fakeNewCommitHash = "fakeNewCommitHash";

            mockGitHub
            .Setup(c => c.GetReferenceAsync(proj, @ref))
            .ReturnsAsync(() => new GitReference
            {
                Object = new GitReferenceObject {
                    Sha = fakeCommitHash
                }
            });

            mockGitHub
            .Setup(c => c.PostTreeAsync(proj, fakeCommitHash, It.IsAny <GitObject[]>()))
            .ReturnsAsync(() => new GitTree {
                Sha = fakeTreeHash
            });

            mockGitHub
            .Setup(c => c.PostCommitAsync(proj, message, fakeTreeHash, It.IsAny <string[]>()))
            .ReturnsAsync(() => new GitCommit {
                Sha = fakeNewCommitHash
            });

            mockGitHub
            .Setup(c => c.PatchReferenceAsync(proj, @ref, fakeNewCommitHash, false))
            .Callback(() =>
            {
                // Once the exception is hit, let the next patch call work.
                mockGitHub
                .Setup(c => c.PatchReferenceAsync(proj, @ref, fakeNewCommitHash, false))
                .ReturnsAsync(() => null);
            })
            .ThrowsAsync(new NotFastForwardUpdateException("Testing non-fast-forward update."));

            await client.PushNewBuildAsync(
                new BuildManifestLocation(proj, @ref, basePath),
                build,
                null,
                message);

            mockGitHub.VerifyAll();
        }
Exemple #16
0
        public async Task <GitReference> GetReferenceAsync(GitHubProject project, string @ref)
        {
            string url = $"https://api.github.com/repos/{project.Segments}/git/refs/{@ref}";

            using (HttpResponseMessage response = await _httpClient.GetAsync(url))
            {
                Trace.TraceInformation($"Getting info about ref {@ref} in {project.Segments}");
                return(await DeserializeSuccessfulAsync <GitReference>(response));
            }
        }
Exemple #17
0
        public async Task <GitCommit> GetCommitAsync(GitHubProject project, string sha)
        {
            string url = $"https://api.github.com/repos/{project.Segments}/git/commits/{sha}";

            using (HttpResponseMessage response = await _httpClient.GetAsync(url))
            {
                Trace.TraceInformation($"Getting info about commit {sha} in {project.Segments}");
                return(await DeserializeSuccessfulAsync <GitCommit>(response));
            }
        }
        private async Task <XElement> FetchModelXmlAsync(
            GitHubProject project,
            string @ref,
            string basePath)
        {
            string contents = await _github.GetGitHubFileContentsAsync(
                $"{basePath}/{BuildManifestXmlName}",
                project,
                @ref);

            return(XElement.Parse(contents));
        }
        public async Task <SemaphoreModel> FetchSemaphoreAsync(
            GitHubProject project,
            string @ref,
            string basePath,
            string semaphorePath)
        {
            string contents = await _github.GetGitHubFileContentsAsync(
                $"{basePath}/{semaphorePath}",
                project,
                @ref);

            return(SemaphoreModel.Parse(semaphorePath, contents));
        }
        private async static Task <bool> BranchExists(GitHubClient client, GitHubProject project, string @ref)
        {
            try
            {
                await client.GetReferenceAsync(project, @ref);

                return(true);
            }
            catch (HttpFailureResponseException)
            {
                return(false);
            }
        }
Exemple #21
0
        public async Task <GitHubContents> GetGitHubFileAsync(
            string path,
            GitHubProject project,
            string @ref)
        {
            string url = $"https://api.github.com/repos/{project.Segments}/contents/{path}?ref={@ref}";

            Trace.TraceInformation($"Getting contents of '{path}' using '{url}'");

            using (HttpResponseMessage response = await _httpClient.GetAsync(url))
            {
                return(await DeserializeSuccessfulAsync <GitHubContents>(response));
            }
        }
Exemple #22
0
        public GitHubWriteVersionUpdater(GitHubAuth gitHubAuth, GitHubProject project)
        {
            if (gitHubAuth == null)
            {
                throw new ArgumentNullException(nameof(gitHubAuth));
            }
            _gitHubAuth = gitHubAuth;

            if (project == null)
            {
                throw new ArgumentNullException(nameof(project));
            }
            _project = project;
        }
Exemple #23
0
        public async Task <GitCommit> GetCommitAsync(GitHubProject project, string sha)
        {
            string url = $"https://api.github.com/repos/{project.Segments}/git/commits/{sha}";

            using (HttpResponseMessage response = await _httpClient.GetAsync(url))
            {
                response.EnsureSuccessStatusCode();
                Trace.TraceInformation($"Got info about commit {sha} in {project.Segments}");

                return(JsonConvert.DeserializeObject <GitCommit>(
                           await response.Content.ReadAsStringAsync(),
                           s_jsonSettings));
            }
        }
        public async Task <OrchestratedBuildModel> FetchManifestAsync(
            GitHubProject project,
            string @ref,
            string basePath)
        {
            XElement contents = await FetchModelXmlAsync(project, @ref, basePath);

            if (contents == null)
            {
                return(null);
            }

            return(OrchestratedBuildModel.Parse(contents));
        }
Exemple #25
0
        public static void Main(string[] args)
        {
            HandleDebugSwitch(ref args);

            bool onlyUpdate = args.Length > 0 && string.Equals("--Update", args[0], StringComparison.OrdinalIgnoreCase);

            List <BuildInfo> buildInfos = new List <BuildInfo>();

            buildInfos.Add(GetBuildInfo("CoreSetup", s_config.CoreSetupVersionFragment, fetchLatestReleaseFile: false));

            if (s_config.HasRoslynVersionFragment)
            {
                buildInfos.Add(GetBuildInfo("Roslyn", s_config.RoslynVersionFragment, fetchLatestReleaseFile: false));
            }

            IEnumerable <IDependencyUpdater> updaters = GetUpdaters();
            var dependencyBuildInfos = buildInfos.Select(buildInfo =>
                                                         new BuildDependencyInfo(
                                                             buildInfo,
                                                             upgradeStableVersions: true,
                                                             disabledPackages: Enumerable.Empty <string>()));
            DependencyUpdateResults updateResults = DependencyUpdateUtils.Update(updaters, dependencyBuildInfos);

            if (!onlyUpdate && updateResults.ChangesDetected())
            {
                GitHubAuth    gitHubAuth     = new GitHubAuth(s_config.Password, s_config.UserName, s_config.Email);
                GitHubProject origin         = new GitHubProject(s_config.GitHubProject, s_config.UserName);
                GitHubBranch  upstreamBranch = new GitHubBranch(
                    s_config.GitHubUpstreamBranch,
                    new GitHubProject(s_config.GitHubProject, s_config.GitHubUpstreamOwner));

                string suggestedMessage = updateResults.GetSuggestedCommitMessage();
                string body             = string.Empty;
                if (s_config.GitHubPullRequestNotifications.Any())
                {
                    body += PullRequestCreator.NotificationString(s_config.GitHubPullRequestNotifications);
                }

                new PullRequestCreator(gitHubAuth)
                .CreateOrUpdateAsync(
                    suggestedMessage,
                    suggestedMessage + $" ({upstreamBranch.Name})",
                    body,
                    upstreamBranch,
                    origin,
                    new PullRequestOptions())
                .Wait();
            }
        }
        private static async Task CreatePullRequestAsync(GitHubClient client, GitRepo gitRepo, Config config)
        {
            GitHubProject project       = new GitHubProject(gitRepo.Name, gitRepo.Owner);
            GitHubProject forkedProject = new GitHubProject(gitRepo.Name, Options.GitUser);
            GitHubBranch  baseBranch    = new GitHubBranch(gitRepo.Branch, project);
            GitHubBranch  headBranch    = new GitHubBranch(
                $"{gitRepo.Name}-{gitRepo.Branch}{config.WorkingBranchSuffix}",
                forkedProject);

            IEnumerable <GitObject> changes = await GetUpdatedFiles(config.SourcePath, client, baseBranch);

            if (!changes.Any())
            {
                return;
            }

            GitReference currentRef = await client.GetReferenceAsync(project, $"heads/{baseBranch.Name}");

            string  parentSha = currentRef.Object.Sha;
            GitTree tree      = await client.PostTreeAsync(forkedProject, parentSha, changes.ToArray());

            GitCommit commit = await client.PostCommitAsync(forkedProject, config.CommitMessage, tree.Sha, new[] { parentSha });

            string workingReference = $"heads/{headBranch.Name}";

            if (await BranchExists(client, forkedProject, workingReference))
            {
                await client.PatchReferenceAsync(forkedProject, workingReference, commit.Sha, force : true);
            }
            else
            {
                await client.PostReferenceAsync(forkedProject, workingReference, commit.Sha);
            }

            GitHubPullRequest pullRequestToUpdate = await client.SearchPullRequestsAsync(
                project,
                headBranch.Name,
                await client.GetMyAuthorIdAsync());

            if (pullRequestToUpdate == null)
            {
                await client.PostGitHubPullRequestAsync(
                    $"[{gitRepo.Branch}] {config.PullRequestTitle}",
                    config.PullRequestDescription,
                    headBranch,
                    baseBranch,
                    maintainersCanModify : true);
            }
        }
Exemple #27
0
        public async Task <GitReference> GetReferenceAsync(GitHubProject project, string @ref)
        {
            string url = $"https://api.github.com/repos/{project.Segments}/git/refs/{@ref}";

            using (HttpResponseMessage response = await _httpClient.GetAsync(url))
            {
                await EnsureSuccessfulAsync(response);

                Trace.TraceInformation($"Got info about ref {@ref} in {project.Segments}");

                return(JsonConvert.DeserializeObject <GitReference>(
                           await response.Content.ReadAsStringAsync(),
                           s_jsonSettings));
            }
        }
Exemple #28
0
        public async Task <IActionResult> OnGetAsync(int?id)
        {
            if (id == null)
            {
                return(NotFound());
            }

            GitHubProject = await _context.GitHubProject.FirstOrDefaultAsync(m => m.ID == id);

            if (GitHubProject == null)
            {
                return(NotFound());
            }
            return(Page());
        }
Exemple #29
0
        private async Task <ImageArtifactDetails> GetImageInfoForSubscriptionAsync(Subscription subscription, ManifestInfo manifest)
        {
            string imageDataJson;

            using (IGitHubClient gitHubClient = _gitHubClientFactory.GetClient(Options.GitOptions.ToGitHubAuth(), Options.IsDryRun))
            {
                GitHubProject project = new GitHubProject(subscription.ImageInfo.Repo, subscription.ImageInfo.Owner);
                GitHubBranch  branch  = new GitHubBranch(subscription.ImageInfo.Branch, project);

                GitFile repo = subscription.Manifest;
                imageDataJson = await gitHubClient.GetGitHubFileContentsAsync(subscription.ImageInfo.Path, branch);
            }

            return(ImageInfoHelper.LoadFromContent(imageDataJson, manifest, skipManifestValidation: true));
        }
        public override async Task ExecuteAsync()
        {
            Logger.WriteHeading("UPDATING VERSIONS");

            // Hookup a TraceListener in order to capture details from Microsoft.DotNet.VersionTools
            Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));

            DockerHelper.PullBaseImages(Manifest, Options);

            GitHubAuth githubAuth = new GitHubAuth(Options.GitAuthToken, Options.GitUsername, Options.GitEmail);

            using (GitHubClient client = new GitHubClient(githubAuth))
            {
                for (int i = 0; i < MaxTries; i++)
                {
                    try
                    {
                        GitHubProject project    = new GitHubProject(Options.GitRepo, Options.GitOwner);
                        GitHubBranch  branch     = new GitHubBranch(Options.GitBranch, project);
                        GitObject[]   gitObjects = await GetUpdatedVerionInfo(client, branch);

                        if (gitObjects.Any())
                        {
                            string       masterRef     = $"heads/{Options.GitBranch}";
                            GitReference currentMaster = await client.GetReferenceAsync(project, masterRef);

                            string  masterSha = currentMaster.Object.Sha;
                            GitTree tree      = await client.PostTreeAsync(project, masterSha, gitObjects);

                            string    commitMessage = "Update Docker image digests";
                            GitCommit commit        = await client.PostCommitAsync(
                                project, commitMessage, tree.Sha, new[] { masterSha });

                            // Only fast-forward. Don't overwrite other changes: throw exception instead.
                            await client.PatchReferenceAsync(project, masterRef, commit.Sha, force : false);
                        }

                        break;
                    }
                    catch (HttpRequestException ex) when(i < (MaxTries - 1))
                    {
                        Logger.WriteMessage($"Encountered exception committing build-info update: {ex.Message}");
                        Logger.WriteMessage($"Trying again in {RetryMillisecondsDelay}ms. {MaxTries - i - 1} tries left.");
                        await Task.Delay(RetryMillisecondsDelay);
                    }
                }
            }
        }
        public async Task UpdateGitHubPullRequestAsync(
            GitHubProject project,
            int number,
            string title = null,
            string body = null,
            string state = null)
        {
            var updatePrBody = new JObject();

            if (title != null)
            {
                updatePrBody.Add(new JProperty("title", title));
            }
            if (body != null)
            {
                updatePrBody.Add(new JProperty("body", body));
            }
            if (state != null)
            {
                updatePrBody.Add(new JProperty("state", state));
            }

            string url = $"https://api.github.com/repos/{project.Segments}/pulls/{number}";

            HttpRequestMessage request = new HttpRequestMessage(new HttpMethod("PATCH"), url);
            request.Content = new StringContent(JsonConvert.SerializeObject(updatePrBody));

            using (HttpResponseMessage response = await _httpClient.SendAsync(request))
            {
                response.EnsureSuccessStatusCode();

                Trace.TraceInformation($"Updated pull request #{number}.");
                Trace.TraceInformation($"Pull request page: {await GetPullRequestUrlAsync(response)}");
            }
        }
        public async Task<GitReference> GetReferenceAsync(GitHubProject project, string @ref)
        {
            string url = $"https://api.github.com/repos/{project.Segments}/git/refs/{@ref}";

            using (HttpResponseMessage response = await _httpClient.GetAsync(url))
            {
                response.EnsureSuccessStatusCode();
                Trace.TraceInformation($"Got info about ref {@ref} in {project.Segments}");

                return JsonConvert.DeserializeObject<GitReference>(
                    await response.Content.ReadAsStringAsync(),
                    s_jsonSettings);
            }
        }
        public async Task PostCommentAsync(GitHubProject project, int issueNumber, string message)
        {
            string commentBody = JsonConvert.SerializeObject(new
            {
                body = message
            });

            string url = $"https://api.github.com/repos/{project.Segments}/issues/{issueNumber}/comments";

            var bodyContent = new StringContent(commentBody);
            using (HttpResponseMessage response = await _httpClient.PostAsync(url, bodyContent))
            {
                response.EnsureSuccessStatusCode();
            }
        }
        public async Task<GitHubPullRequest> SearchPullRequestsAsync(
            GitHubProject project,
            string headPrefix,
            string author,
            string sortType = "created")
        {
            int pullRequestNumber;

            // First: find the number of the pull request.
            string queryString = $"repo:{project.Segments}+head:{headPrefix}+author:{author}+state:open";
            string queryUrl = $"https://api.github.com/search/issues?q={queryString}&sort={sortType}&order=desc";

            using (HttpResponseMessage response = await _httpClient.GetAsync(queryUrl))
            {
                response.EnsureSuccessStatusCode();

                var queryResponse = JsonConvert.DeserializeObject<GitHubIssueQueryResponse>(
                    await response.Content.ReadAsStringAsync(),
                    s_jsonSettings);

                if (queryResponse.TotalCount == 0)
                {
                    Trace.TraceInformation($"Could not find any pull request with head {headPrefix}");
                    return null;
                }
                if (queryResponse.TotalCount > 1)
                {
                    IEnumerable<int> allIds = queryResponse.Items.Select(item => item.Id);
                    Trace.TraceInformation($"Found multiple pull requests with head {headPrefix}. On this page, found {string.Join(", ", allIds)}");
                }

                pullRequestNumber = queryResponse.Items.First().Number;
            }
            // Second: fetch details for the pull request.
            string pullRequestUrl = $"https://api.github.com/repos/{project.Segments}/pulls/{pullRequestNumber}";

            using (HttpResponseMessage response = await _httpClient.GetAsync(pullRequestUrl))
            {
                response.EnsureSuccessStatusCode();

                return JsonConvert.DeserializeObject<GitHubPullRequest>(
                    await response.Content.ReadAsStringAsync(),
                    s_jsonSettings);
            }
        }