From 4c2895a62e3a0040c211d1c41bed7045c7fbee59 Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Thu, 22 Oct 2026 03:03:18 -0700 Subject: [PATCH 1/3] fix: handle a missing GHCommitPointer repository getCommit() now takes the repository and throws IOException with the message "Repository information is unavailable for this commit pointer" when it is null. Javadoc on getCommit() and getRepository() states that the repository can be null and that getCommit() throws IOException in that case. A WireMock test stubs a pull request whose head repo is JSON null, then asserts getRepository() is null and that getCommit() throws that IOException. Fixes #2035 --- .../org/kohsuke/github/GHCommitPointer.java | 14 +++-- .../kohsuke/github/GHCommitPointerTest.java | 52 +++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/kohsuke/github/GHCommitPointerTest.java diff --git a/src/main/java/org/kohsuke/github/GHCommitPointer.java b/src/main/java/org/kohsuke/github/GHCommitPointer.java index 41cb15114c..6c2c759529 100644 --- a/src/main/java/org/kohsuke/github/GHCommitPointer.java +++ b/src/main/java/org/kohsuke/github/GHCommitPointer.java @@ -48,12 +48,18 @@ public GHCommitPointer() { /** * Obtains the commit that this pointer is referring to. * + * When repository information is unavailable, this method throws {@link IOException}. + * * @return the commit * @throws IOException - * the io exception + * if repository information is unavailable or the commit cannot be retrieved */ public GHCommit getCommit() throws IOException { - return getRepository().getCommit(getSha()); + GHRepository repository = getRepository(); + if (repository == null) { + throw new IOException("Repository information is unavailable for this commit pointer"); + } + return repository.getCommit(getSha()); } /** @@ -77,7 +83,9 @@ public String getRef() { /** * The repository that contains the commit. * - * @return the repository + * This value is {@code null} when repository information is unavailable. + * + * @return the repository, or {@code null} when repository information is unavailable */ @SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior") public GHRepository getRepository() { diff --git a/src/test/java/org/kohsuke/github/GHCommitPointerTest.java b/src/test/java/org/kohsuke/github/GHCommitPointerTest.java new file mode 100644 index 0000000000..28546a71ec --- /dev/null +++ b/src/test/java/org/kohsuke/github/GHCommitPointerTest.java @@ -0,0 +1,52 @@ +package org.kohsuke.github; + +import org.junit.Test; + +import java.io.IOException; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThrows; + +// TODO: Auto-generated Javadoc +/** + * The Class GHCommitPointerTest. + */ +public class GHCommitPointerTest extends AbstractGitHubWireMockTest { + + /** + * Create default GHCommitPointerTest instance + */ + public GHCommitPointerTest() { + } + + /** + * A head repository of JSON {@code null} throws {@link IOException} instead of {@link NullPointerException}. + * + * @throws Exception + * the exception + */ + @Test + public void getCommitThrowsWhenRepositoryIsNull() throws Exception { + snapshotNotAllowed(); + mockGitHub.apiServer() + .stubFor(get(urlEqualTo("/repos/base-owner/base-repo")).willReturn(aResponse().withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"full_name\":\"base-owner/base-repo\"}"))); + mockGitHub.apiServer() + .stubFor(get(urlEqualTo("/repos/base-owner/base-repo/pulls/1")).willReturn(aResponse().withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody( + "{\"head\":{\"sha\":\"1111111111111111111111111111111111111111\",\"repo\":null}}"))); + + GHCommitPointer head = gitHub.getRepository("base-owner/base-repo").getPullRequest(1).getHead(); + assertThat(head.getRepository(), nullValue()); + + IOException failure = assertThrows(IOException.class, head::getCommit); + assertThat(failure.getMessage(), + equalTo("Repository information is unavailable for this commit pointer")); + } +} From 90e9ae6163ba5c8ae516ff1b299c96212e608c2b Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 23 Oct 2026 07:20:10 -0700 Subject: [PATCH 2/3] fix: resolve GHCommitPointer commit through the pull request repository When the head repository of a pull request has been deleted, GitHub reports head.repo as null, but the head commit is still reachable in the base repository. GHPullRequest now binds its head and base pointers to itself, and getCommit() falls back to the pull request's repository when the pointer has none. Only a pointer with neither still throws. The test replays checkstyle/checkstyle#1, the case from the issue. Fixes #2035 --- .../org/kohsuke/github/GHCommitPointer.java | 32 +- .../org/kohsuke/github/GHPullRequest.java | 4 +- .../kohsuke/github/GHCommitPointerTest.java | 45 +-- .../__files/1-user.json | 36 ++ .../__files/2-r_c_checkstyle.json | 148 ++++++++ .../__files/3-r_c_c_pulls_1.json | 319 ++++++++++++++++++ .../__files/4-r_c_c_commits_4889a777.json | 101 ++++++ .../mappings/1-user.json | 49 +++ .../mappings/2-r_c_checkstyle.json | 49 +++ .../mappings/3-r_c_c_pulls_1.json | 52 +++ .../mappings/4-r_c_c_commits_4889a777.json | 49 +++ 11 files changed, 856 insertions(+), 28 deletions(-) create mode 100644 src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/1-user.json create mode 100644 src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/2-r_c_checkstyle.json create mode 100644 src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/3-r_c_c_pulls_1.json create mode 100644 src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/4-r_c_c_commits_4889a777.json create mode 100644 src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/1-user.json create mode 100644 src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/2-r_c_checkstyle.json create mode 100644 src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/3-r_c_c_pulls_1.json create mode 100644 src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/4-r_c_c_commits_4889a777.json diff --git a/src/main/java/org/kohsuke/github/GHCommitPointer.java b/src/main/java/org/kohsuke/github/GHCommitPointer.java index 6c2c759529..80470705a0 100644 --- a/src/main/java/org/kohsuke/github/GHCommitPointer.java +++ b/src/main/java/org/kohsuke/github/GHCommitPointer.java @@ -35,9 +35,12 @@ */ public class GHCommitPointer { - private String ref, sha, label; + /** The pull request this pointer belongs to, used when {@link #repo} is unavailable. */ + private transient GHPullRequest pullRequest; + private String ref, sha, label; private GHRepository repo; + private GHUser user; /** * Create default GHCommitPointer instance @@ -48,16 +51,21 @@ public GHCommitPointer() { /** * Obtains the commit that this pointer is referring to. * - * When repository information is unavailable, this method throws {@link IOException}. + * If the repository is unavailable (for example, the head repository of a pull request opened from a fork that has + * since been deleted), the commit is looked up in the repository of the pull request instead. GitHub keeps pull + * request commits in the base repository, so they remain reachable there. * * @return the commit * @throws IOException - * if repository information is unavailable or the commit cannot be retrieved + * the io exception */ public GHCommit getCommit() throws IOException { GHRepository repository = getRepository(); + if (repository == null && pullRequest != null) { + repository = pullRequest.getRepository(); + } if (repository == null) { - throw new IOException("Repository information is unavailable for this commit pointer"); + throw new IOException("Repository information is unavailable for commit " + getSha()); } return repository.getCommit(getSha()); } @@ -83,9 +91,10 @@ public String getRef() { /** * The repository that contains the commit. * - * This value is {@code null} when repository information is unavailable. + * This value is {@code null} when GitHub does not report a repository, for example when the head repository of a + * pull request has been deleted. {@link #getCommit()} still works in that case. * - * @return the repository, or {@code null} when repository information is unavailable + * @return the repository, or {@code null} when it is unavailable */ @SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior") public GHRepository getRepository() { @@ -113,4 +122,15 @@ public GHUser getUser() { return user; } + /** + * Binds this pointer to the pull request it belongs to. + * + * @param pullRequest + * the pull request + * @return this pointer + */ + GHCommitPointer wrapUp(GHPullRequest pullRequest) { + this.pullRequest = pullRequest; + return this; + } } diff --git a/src/main/java/org/kohsuke/github/GHPullRequest.java b/src/main/java/org/kohsuke/github/GHPullRequest.java index 47636ce108..7f392ccea3 100644 --- a/src/main/java/org/kohsuke/github/GHPullRequest.java +++ b/src/main/java/org/kohsuke/github/GHPullRequest.java @@ -272,7 +272,7 @@ public AutoMerge getAutoMerge() { * @return the base */ public GHCommitPointer getBase() { - return base; + return base == null ? null : base.wrapUp(this); } /** @@ -340,7 +340,7 @@ public URL getDiffUrl() { * @return the head */ public GHCommitPointer getHead() { - return head; + return head == null ? null : head.wrapUp(this); } /** diff --git a/src/test/java/org/kohsuke/github/GHCommitPointerTest.java b/src/test/java/org/kohsuke/github/GHCommitPointerTest.java index 28546a71ec..57e6934214 100644 --- a/src/test/java/org/kohsuke/github/GHCommitPointerTest.java +++ b/src/test/java/org/kohsuke/github/GHCommitPointerTest.java @@ -4,10 +4,8 @@ import java.io.IOException; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.get; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThrows; @@ -24,29 +22,36 @@ public GHCommitPointerTest() { } /** - * A head repository of JSON {@code null} throws {@link IOException} instead of {@link NullPointerException}. + * The head of checkstyle/checkstyle#1 comes from a fork that no longer exists, so GitHub reports its repository as + * {@code null}. The head commit is still reachable through the base repository. * * @throws Exception * the exception */ @Test - public void getCommitThrowsWhenRepositoryIsNull() throws Exception { - snapshotNotAllowed(); - mockGitHub.apiServer() - .stubFor(get(urlEqualTo("/repos/base-owner/base-repo")).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody("{\"full_name\":\"base-owner/base-repo\"}"))); - mockGitHub.apiServer() - .stubFor(get(urlEqualTo("/repos/base-owner/base-repo/pulls/1")).willReturn(aResponse().withStatus(200) - .withHeader("Content-Type", "application/json") - .withBody( - "{\"head\":{\"sha\":\"1111111111111111111111111111111111111111\",\"repo\":null}}"))); - - GHCommitPointer head = gitHub.getRepository("base-owner/base-repo").getPullRequest(1).getHead(); + public void getCommitFromDeletedHeadRepository() throws Exception { + GHPullRequest pullRequest = gitHub.getRepository("checkstyle/checkstyle").getPullRequest(1); + GHCommitPointer head = pullRequest.getHead(); assertThat(head.getRepository(), nullValue()); + assertThat(head.getSha(), equalTo("4889a777da54c6848b92da273a28223cfaf8dfe9")); + + GHCommit commit = head.getCommit(); + assertThat(commit, notNullValue()); + assertThat(commit.getSHA1(), equalTo("4889a777da54c6848b92da273a28223cfaf8dfe9")); + assertThat(commit.getOwner().getFullName(), equalTo("checkstyle/checkstyle")); + } - IOException failure = assertThrows(IOException.class, head::getCommit); - assertThat(failure.getMessage(), - equalTo("Repository information is unavailable for this commit pointer")); + /** + * A pointer that has neither a repository nor a pull request cannot resolve its commit. + * + * @throws Exception + * the exception + */ + @Test + public void getCommitWithoutRepositoryOrPullRequest() throws Exception { + snapshotNotAllowed(); + GHCommitPointer pointer = new GHCommitPointer(); + IOException failure = assertThrows(IOException.class, pointer::getCommit); + assertThat(failure.getMessage(), equalTo("Repository information is unavailable for commit null")); } } diff --git a/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/1-user.json b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/1-user.json new file mode 100644 index 0000000000..e7656edba7 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/1-user.json @@ -0,0 +1,36 @@ +{ + "login": "mvanhorn", + "id": 455140, + "node_id": "MDQ6VXNlcjQ1NTE0MA==", + "avatar_url": "https://avatars.githubusercontent.com/u/455140?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/mvanhorn", + "html_url": "https://github.com/mvanhorn", + "followers_url": "https://api.github.com/users/mvanhorn/followers", + "following_url": "https://api.github.com/users/mvanhorn/following{/other_user}", + "gists_url": "https://api.github.com/users/mvanhorn/gists{/gist_id}", + "starred_url": "https://api.github.com/users/mvanhorn/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/mvanhorn/subscriptions", + "organizations_url": "https://api.github.com/users/mvanhorn/orgs", + "repos_url": "https://api.github.com/users/mvanhorn/repos", + "events_url": "https://api.github.com/users/mvanhorn/events{/privacy}", + "received_events_url": "https://api.github.com/users/mvanhorn/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false, + "name": "Matt Van Horn", + "company": null, + "blog": "", + "location": null, + "email": null, + "hireable": null, + "bio": "Co-founded June (\"self-driving oven\" acquired by @webergrills) & the co that became @Lyft. Building again, more soon. OS: @slashlast30days 47k★ @ppressdev 5.4k★", + "twitter_username": "mvanhorn", + "notification_email": null, + "public_repos": 2860, + "public_gists": 1, + "followers": 5590, + "following": 7, + "created_at": "2010-10-26T19:09:59Z", + "updated_at": "2026-10-22T06:09:50Z" +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/2-r_c_checkstyle.json b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/2-r_c_checkstyle.json new file mode 100644 index 0000000000..81086c38a8 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/2-r_c_checkstyle.json @@ -0,0 +1,148 @@ +{ + "id": 12499251, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjQ5OTI1MQ==", + "name": "checkstyle", + "full_name": "checkstyle/checkstyle", + "private": false, + "owner": { + "login": "checkstyle", + "id": 5179750, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk3NTA=", + "avatar_url": "https://avatars.githubusercontent.com/u/5179750?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/checkstyle", + "html_url": "https://github.com/checkstyle", + "followers_url": "https://api.github.com/users/checkstyle/followers", + "following_url": "https://api.github.com/users/checkstyle/following{/other_user}", + "gists_url": "https://api.github.com/users/checkstyle/gists{/gist_id}", + "starred_url": "https://api.github.com/users/checkstyle/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/checkstyle/subscriptions", + "organizations_url": "https://api.github.com/users/checkstyle/orgs", + "repos_url": "https://api.github.com/users/checkstyle/repos", + "events_url": "https://api.github.com/users/checkstyle/events{/privacy}", + "received_events_url": "https://api.github.com/users/checkstyle/received_events", + "type": "Organization", + "user_view_type": "public", + "site_admin": false + }, + "html_url": "https://github.com/checkstyle/checkstyle", + "description": "Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard. By default it supports the Google Java Style Guide and Sun Code Conventions, but is highly configurable. It can be invoked with an ANT task and a command line program.", + "fork": false, + "url": "https://api.github.com/repos/checkstyle/checkstyle", + "forks_url": "https://api.github.com/repos/checkstyle/checkstyle/forks", + "keys_url": "https://api.github.com/repos/checkstyle/checkstyle/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/checkstyle/checkstyle/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/checkstyle/checkstyle/teams", + "hooks_url": "https://api.github.com/repos/checkstyle/checkstyle/hooks", + "issue_events_url": "https://api.github.com/repos/checkstyle/checkstyle/issues/events{/number}", + "events_url": "https://api.github.com/repos/checkstyle/checkstyle/events", + "assignees_url": "https://api.github.com/repos/checkstyle/checkstyle/assignees{/user}", + "branches_url": "https://api.github.com/repos/checkstyle/checkstyle/branches{/branch}", + "tags_url": "https://api.github.com/repos/checkstyle/checkstyle/tags", + "blobs_url": "https://api.github.com/repos/checkstyle/checkstyle/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/checkstyle/checkstyle/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/checkstyle/checkstyle/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/checkstyle/checkstyle/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/checkstyle/checkstyle/statuses/{sha}", + "languages_url": "https://api.github.com/repos/checkstyle/checkstyle/languages", + "stargazers_url": "https://api.github.com/repos/checkstyle/checkstyle/stargazers", + "contributors_url": "https://api.github.com/repos/checkstyle/checkstyle/contributors", + "subscribers_url": "https://api.github.com/repos/checkstyle/checkstyle/subscribers", + "subscription_url": "https://api.github.com/repos/checkstyle/checkstyle/subscription", + "commits_url": "https://api.github.com/repos/checkstyle/checkstyle/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/checkstyle/checkstyle/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/checkstyle/checkstyle/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/checkstyle/checkstyle/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/checkstyle/checkstyle/contents/{+path}", + "compare_url": "https://api.github.com/repos/checkstyle/checkstyle/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/checkstyle/checkstyle/merges", + "archive_url": "https://api.github.com/repos/checkstyle/checkstyle/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/checkstyle/checkstyle/downloads", + "issues_url": "https://api.github.com/repos/checkstyle/checkstyle/issues{/number}", + "pulls_url": "https://api.github.com/repos/checkstyle/checkstyle/pulls{/number}", + "milestones_url": "https://api.github.com/repos/checkstyle/checkstyle/milestones{/number}", + "notifications_url": "https://api.github.com/repos/checkstyle/checkstyle/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/checkstyle/checkstyle/labels{/name}", + "releases_url": "https://api.github.com/repos/checkstyle/checkstyle/releases{/id}", + "deployments_url": "https://api.github.com/repos/checkstyle/checkstyle/deployments", + "created_at": "2013-08-31T02:05:05Z", + "updated_at": "2026-10-23T12:15:07Z", + "pushed_at": "2026-10-23T13:53:31Z", + "git_url": "git://github.com/checkstyle/checkstyle.git", + "ssh_url": "git@github.com:checkstyle/checkstyle.git", + "clone_url": "https://github.com/checkstyle/checkstyle.git", + "svn_url": "https://github.com/checkstyle/checkstyle", + "homepage": "https://checkstyle.org", + "size": 321106, + "stargazers_count": 9616, + "watchers_count": 9616, + "language": "Java", + "has_issues": true, + "has_projects": true, + "has_downloads": false, + "has_wiki": true, + "has_pages": false, + "has_discussions": true, + "forks_count": 4300, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 821, + "license": { + "key": "lgpl-2.1", + "name": "GNU Lesser General Public License v2.1", + "spdx_id": "LGPL-2.1", + "url": "https://api.github.com/licenses/lgpl-2.1", + "node_id": "MDc6TGljZW5zZTEx" + }, + "allow_forking": true, + "is_template": false, + "web_commit_signoff_required": false, + "has_pull_requests": true, + "pull_request_creation_policy": "all", + "topics": [ + "code-quality", + "command-line-tool", + "hacktoberfest", + "java", + "static-analysis", + "static-code-analysis" + ], + "visibility": "public", + "forks": 4300, + "open_issues": 821, + "watchers": 9616, + "default_branch": "master", + "permissions": { + "admin": false, + "maintain": false, + "push": false, + "triage": false, + "pull": true + }, + "temp_clone_token": "", + "custom_properties": {}, + "organization": { + "login": "checkstyle", + "id": 5179750, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk3NTA=", + "avatar_url": "https://avatars.githubusercontent.com/u/5179750?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/checkstyle", + "html_url": "https://github.com/checkstyle", + "followers_url": "https://api.github.com/users/checkstyle/followers", + "following_url": "https://api.github.com/users/checkstyle/following{/other_user}", + "gists_url": "https://api.github.com/users/checkstyle/gists{/gist_id}", + "starred_url": "https://api.github.com/users/checkstyle/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/checkstyle/subscriptions", + "organizations_url": "https://api.github.com/users/checkstyle/orgs", + "repos_url": "https://api.github.com/users/checkstyle/repos", + "events_url": "https://api.github.com/users/checkstyle/events{/privacy}", + "received_events_url": "https://api.github.com/users/checkstyle/received_events", + "type": "Organization", + "user_view_type": "public", + "site_admin": false + }, + "network_count": 4300, + "subscribers_count": 223 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/3-r_c_c_pulls_1.json b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/3-r_c_c_pulls_1.json new file mode 100644 index 0000000000..a791d7c0f8 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/3-r_c_c_pulls_1.json @@ -0,0 +1,319 @@ +{ + "url": "https://api.github.com/repos/checkstyle/checkstyle/pulls/1", + "id": 8014025, + "node_id": "MDExOlB1bGxSZXF1ZXN0ODAxNDAyNQ==", + "html_url": "https://github.com/checkstyle/checkstyle/pull/1", + "diff_url": "https://github.com/checkstyle/checkstyle/pull/1.diff", + "patch_url": "https://github.com/checkstyle/checkstyle/pull/1.patch", + "issue_url": "https://api.github.com/repos/checkstyle/checkstyle/issues/1", + "commits_url": "https://api.github.com/repos/checkstyle/checkstyle/pulls/1/commits", + "review_comments_url": "https://api.github.com/repos/checkstyle/checkstyle/pulls/1/comments", + "review_comment_url": "https://api.github.com/repos/checkstyle/checkstyle/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/checkstyle/checkstyle/issues/1/comments", + "statuses_url": "https://api.github.com/repos/checkstyle/checkstyle/statuses/4889a777da54c6848b92da273a28223cfaf8dfe9", + "number": 1, + "state": "closed", + "locked": false, + "title": "Github migration", + "user": { + "login": "isopov", + "id": 462800, + "node_id": "MDQ6VXNlcjQ2MjgwMA==", + "avatar_url": "https://avatars.githubusercontent.com/u/462800?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/isopov", + "html_url": "https://github.com/isopov", + "followers_url": "https://api.github.com/users/isopov/followers", + "following_url": "https://api.github.com/users/isopov/following{/other_user}", + "gists_url": "https://api.github.com/users/isopov/gists{/gist_id}", + "starred_url": "https://api.github.com/users/isopov/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/isopov/subscriptions", + "organizations_url": "https://api.github.com/users/isopov/orgs", + "repos_url": "https://api.github.com/users/isopov/repos", + "events_url": "https://api.github.com/users/isopov/events{/privacy}", + "received_events_url": "https://api.github.com/users/isopov/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + }, + "body": "Minor changes according to migration from Sourceforge's mercurial repo to github.\n", + "labels": [], + "milestone": { + "id": 13165181, + "node_id": "MI_kwDOAL65M84AyOJ9", + "url": "https://api.github.com/repos/checkstyle/checkstyle/milestones/147", + "html_url": "https://github.com/checkstyle/checkstyle/milestone/147", + "labels_url": "https://api.github.com/repos/checkstyle/checkstyle/milestones/147/labels", + "number": 147, + "state": "closed", + "title": "11.0.0", + "description": "", + "creator": { + "login": "github-actions[bot]", + "id": 41898282, + "node_id": "MDM6Qm90NDE4OTgyODI=", + "avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github-actions%5Bbot%5D", + "html_url": "https://github.com/apps/github-actions", + "followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers", + "following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}", + "gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions", + "organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs", + "repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos", + "events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}", + "received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events", + "type": "Bot", + "user_view_type": "public", + "site_admin": false + }, + "open_issues": 1, + "closed_issues": 50, + "created_at": "2025-06-29T19:27:00Z", + "updated_at": "2026-03-09T01:14:55Z", + "closed_at": "2025-08-07T05:37:33Z", + "due_on": "2025-07-27T00:00:00Z" + }, + "active_lock_reason": null, + "created_at": "2013-09-02T06:54:44Z", + "updated_at": "2025-07-25T13:26:14Z", + "closed_at": "2013-09-11T04:16:50Z", + "merged_at": "2013-09-11T04:16:50Z", + "merge_commit_sha": "8fc22d3b2e35a343c6168a82214c224e551cdc3b", + "assignee": null, + "assignees": [], + "requested_reviewers": [], + "requested_teams": [], + "head": { + "label": "isopov:github-migration", + "ref": "github-migration", + "repo": null, + "sha": "4889a777da54c6848b92da273a28223cfaf8dfe9", + "user": { + "login": "isopov", + "id": 462800, + "node_id": "MDQ6VXNlcjQ2MjgwMA==", + "avatar_url": "https://avatars.githubusercontent.com/u/462800?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/isopov", + "html_url": "https://github.com/isopov", + "followers_url": "https://api.github.com/users/isopov/followers", + "following_url": "https://api.github.com/users/isopov/following{/other_user}", + "gists_url": "https://api.github.com/users/isopov/gists{/gist_id}", + "starred_url": "https://api.github.com/users/isopov/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/isopov/subscriptions", + "organizations_url": "https://api.github.com/users/isopov/orgs", + "repos_url": "https://api.github.com/users/isopov/repos", + "events_url": "https://api.github.com/users/isopov/events{/privacy}", + "received_events_url": "https://api.github.com/users/isopov/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + } + }, + "base": { + "label": "checkstyle:master", + "ref": "master", + "repo": { + "id": 12499251, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjQ5OTI1MQ==", + "name": "checkstyle", + "full_name": "checkstyle/checkstyle", + "owner": { + "login": "checkstyle", + "id": 5179750, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk3NTA=", + "avatar_url": "https://avatars.githubusercontent.com/u/5179750?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/checkstyle", + "html_url": "https://github.com/checkstyle", + "followers_url": "https://api.github.com/users/checkstyle/followers", + "following_url": "https://api.github.com/users/checkstyle/following{/other_user}", + "gists_url": "https://api.github.com/users/checkstyle/gists{/gist_id}", + "starred_url": "https://api.github.com/users/checkstyle/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/checkstyle/subscriptions", + "organizations_url": "https://api.github.com/users/checkstyle/orgs", + "repos_url": "https://api.github.com/users/checkstyle/repos", + "events_url": "https://api.github.com/users/checkstyle/events{/privacy}", + "received_events_url": "https://api.github.com/users/checkstyle/received_events", + "type": "Organization", + "user_view_type": "public", + "site_admin": false + }, + "private": false, + "fork": false, + "archived": false, + "disabled": false, + "description": "Checkstyle is a development tool to help programmers write Java code that adheres to a coding standard. By default it supports the Google Java Style Guide and Sun Code Conventions, but is highly configurable. It can be invoked with an ANT task and a command line program.", + "homepage": "https://checkstyle.org", + "language": "Java", + "visibility": "public", + "is_template": false, + "topics": [ + "code-quality", + "command-line-tool", + "hacktoberfest", + "java", + "static-analysis", + "static-code-analysis" + ], + "forks_count": 4300, + "forks": 4300, + "stargazers_count": 9616, + "watchers_count": 9616, + "watchers": 9616, + "open_issues_count": 821, + "open_issues": 821, + "size": 321106, + "default_branch": "master", + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": false, + "has_discussions": true, + "has_pull_requests": true, + "pushed_at": "2026-10-23T13:53:31Z", + "created_at": "2013-08-31T02:05:05Z", + "updated_at": "2026-10-23T12:15:07Z", + "allow_forking": true, + "web_commit_signoff_required": false, + "pull_request_creation_policy": "all", + "license": { + "key": "lgpl-2.1", + "name": "GNU Lesser General Public License v2.1", + "url": "https://api.github.com/licenses/lgpl-2.1", + "spdx_id": "LGPL-2.1", + "node_id": "MDc6TGljZW5zZTEx" + }, + "url": "https://api.github.com/repos/checkstyle/checkstyle", + "html_url": "https://github.com/checkstyle/checkstyle", + "archive_url": "https://api.github.com/repos/checkstyle/checkstyle/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/checkstyle/checkstyle/assignees{/user}", + "blobs_url": "https://api.github.com/repos/checkstyle/checkstyle/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/checkstyle/checkstyle/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/checkstyle/checkstyle/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/checkstyle/checkstyle/comments{/number}", + "commits_url": "https://api.github.com/repos/checkstyle/checkstyle/commits{/sha}", + "compare_url": "https://api.github.com/repos/checkstyle/checkstyle/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/checkstyle/checkstyle/contents/{+path}", + "contributors_url": "https://api.github.com/repos/checkstyle/checkstyle/contributors", + "deployments_url": "https://api.github.com/repos/checkstyle/checkstyle/deployments", + "downloads_url": "https://api.github.com/repos/checkstyle/checkstyle/downloads", + "events_url": "https://api.github.com/repos/checkstyle/checkstyle/events", + "forks_url": "https://api.github.com/repos/checkstyle/checkstyle/forks", + "git_commits_url": "https://api.github.com/repos/checkstyle/checkstyle/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/checkstyle/checkstyle/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/checkstyle/checkstyle/git/tags{/sha}", + "git_url": "git://github.com/checkstyle/checkstyle.git", + "issue_comment_url": "https://api.github.com/repos/checkstyle/checkstyle/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/checkstyle/checkstyle/issues/events{/number}", + "issues_url": "https://api.github.com/repos/checkstyle/checkstyle/issues{/number}", + "keys_url": "https://api.github.com/repos/checkstyle/checkstyle/keys{/key_id}", + "labels_url": "https://api.github.com/repos/checkstyle/checkstyle/labels{/name}", + "languages_url": "https://api.github.com/repos/checkstyle/checkstyle/languages", + "merges_url": "https://api.github.com/repos/checkstyle/checkstyle/merges", + "milestones_url": "https://api.github.com/repos/checkstyle/checkstyle/milestones{/number}", + "notifications_url": "https://api.github.com/repos/checkstyle/checkstyle/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/checkstyle/checkstyle/pulls{/number}", + "releases_url": "https://api.github.com/repos/checkstyle/checkstyle/releases{/id}", + "ssh_url": "git@github.com:checkstyle/checkstyle.git", + "stargazers_url": "https://api.github.com/repos/checkstyle/checkstyle/stargazers", + "statuses_url": "https://api.github.com/repos/checkstyle/checkstyle/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/checkstyle/checkstyle/subscribers", + "subscription_url": "https://api.github.com/repos/checkstyle/checkstyle/subscription", + "tags_url": "https://api.github.com/repos/checkstyle/checkstyle/tags", + "teams_url": "https://api.github.com/repos/checkstyle/checkstyle/teams", + "trees_url": "https://api.github.com/repos/checkstyle/checkstyle/git/trees{/sha}", + "clone_url": "https://github.com/checkstyle/checkstyle.git", + "mirror_url": null, + "hooks_url": "https://api.github.com/repos/checkstyle/checkstyle/hooks", + "svn_url": "https://github.com/checkstyle/checkstyle" + }, + "sha": "b0c33771f097f4895746eaddd40cb159e5bc5bee", + "user": { + "login": "checkstyle", + "id": 5179750, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk3NTA=", + "avatar_url": "https://avatars.githubusercontent.com/u/5179750?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/checkstyle", + "html_url": "https://github.com/checkstyle", + "followers_url": "https://api.github.com/users/checkstyle/followers", + "following_url": "https://api.github.com/users/checkstyle/following{/other_user}", + "gists_url": "https://api.github.com/users/checkstyle/gists{/gist_id}", + "starred_url": "https://api.github.com/users/checkstyle/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/checkstyle/subscriptions", + "organizations_url": "https://api.github.com/users/checkstyle/orgs", + "repos_url": "https://api.github.com/users/checkstyle/repos", + "events_url": "https://api.github.com/users/checkstyle/events{/privacy}", + "received_events_url": "https://api.github.com/users/checkstyle/received_events", + "type": "Organization", + "user_view_type": "public", + "site_admin": false + } + }, + "_links": { + "comments": { + "href": "https://api.github.com/repos/checkstyle/checkstyle/issues/1/comments" + }, + "commits": { + "href": "https://api.github.com/repos/checkstyle/checkstyle/pulls/1/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/checkstyle/checkstyle/statuses/4889a777da54c6848b92da273a28223cfaf8dfe9" + }, + "html": { + "href": "https://github.com/checkstyle/checkstyle/pull/1" + }, + "issue": { + "href": "https://api.github.com/repos/checkstyle/checkstyle/issues/1" + }, + "review_comments": { + "href": "https://api.github.com/repos/checkstyle/checkstyle/pulls/1/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/checkstyle/checkstyle/pulls/comments{/number}" + }, + "self": { + "href": "https://api.github.com/repos/checkstyle/checkstyle/pulls/1" + } + }, + "author_association": "CONTRIBUTOR", + "auto_merge": null, + "draft": false, + "merged": true, + "mergeable": null, + "rebaseable": null, + "mergeable_state": "unknown", + "merged_by": { + "login": "romani", + "id": 812984, + "node_id": "MDQ6VXNlcjgxMjk4NA==", + "avatar_url": "https://avatars.githubusercontent.com/u/812984?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/romani", + "html_url": "https://github.com/romani", + "followers_url": "https://api.github.com/users/romani/followers", + "following_url": "https://api.github.com/users/romani/following{/other_user}", + "gists_url": "https://api.github.com/users/romani/gists{/gist_id}", + "starred_url": "https://api.github.com/users/romani/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/romani/subscriptions", + "organizations_url": "https://api.github.com/users/romani/orgs", + "repos_url": "https://api.github.com/users/romani/repos", + "events_url": "https://api.github.com/users/romani/events{/privacy}", + "received_events_url": "https://api.github.com/users/romani/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + }, + "comments": 1, + "review_comments": 0, + "maintainer_can_modify": false, + "commits": 3, + "additions": 23, + "deletions": 2, + "changed_files": 3 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/4-r_c_c_commits_4889a777.json b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/4-r_c_c_commits_4889a777.json new file mode 100644 index 0000000000..eee0fd2739 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/__files/4-r_c_c_commits_4889a777.json @@ -0,0 +1,101 @@ +{ + "sha": "4889a777da54c6848b92da273a28223cfaf8dfe9", + "node_id": "MDY6Q29tbWl0MTI0OTkyNTE6NDg4OWE3NzdkYTU0YzY4NDhiOTJkYTI3M2EyODIyM2NmYWY4ZGZlOQ==", + "commit": { + "author": { + "name": "Ivan Sopov", + "email": "isopov@data357.com", + "date": "2013-09-02T06:52:59Z" + }, + "committer": { + "name": "Ivan Sopov", + "email": "isopov@data357.com", + "date": "2013-09-10T14:33:22Z" + }, + "message": "adding travis config file - additionally it is needed to activate travis on this github repo", + "tree": { + "sha": "6d003a0c9709e476ace405a50ffdb66720bcda5e", + "url": "https://api.github.com/repos/checkstyle/checkstyle/git/trees/6d003a0c9709e476ace405a50ffdb66720bcda5e" + }, + "url": "https://api.github.com/repos/checkstyle/checkstyle/git/commits/4889a777da54c6848b92da273a28223cfaf8dfe9", + "comment_count": 0, + "verification": { + "verified": false, + "reason": "unsigned", + "signature": null, + "payload": null, + "verified_at": null + } + }, + "url": "https://api.github.com/repos/checkstyle/checkstyle/commits/4889a777da54c6848b92da273a28223cfaf8dfe9", + "html_url": "https://github.com/checkstyle/checkstyle/commit/4889a777da54c6848b92da273a28223cfaf8dfe9", + "comments_url": "https://api.github.com/repos/checkstyle/checkstyle/commits/4889a777da54c6848b92da273a28223cfaf8dfe9/comments", + "author": { + "login": "isopov", + "id": 462800, + "node_id": "MDQ6VXNlcjQ2MjgwMA==", + "avatar_url": "https://avatars.githubusercontent.com/u/462800?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/isopov", + "html_url": "https://github.com/isopov", + "followers_url": "https://api.github.com/users/isopov/followers", + "following_url": "https://api.github.com/users/isopov/following{/other_user}", + "gists_url": "https://api.github.com/users/isopov/gists{/gist_id}", + "starred_url": "https://api.github.com/users/isopov/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/isopov/subscriptions", + "organizations_url": "https://api.github.com/users/isopov/orgs", + "repos_url": "https://api.github.com/users/isopov/repos", + "events_url": "https://api.github.com/users/isopov/events{/privacy}", + "received_events_url": "https://api.github.com/users/isopov/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + }, + "committer": { + "login": "isopov", + "id": 462800, + "node_id": "MDQ6VXNlcjQ2MjgwMA==", + "avatar_url": "https://avatars.githubusercontent.com/u/462800?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/isopov", + "html_url": "https://github.com/isopov", + "followers_url": "https://api.github.com/users/isopov/followers", + "following_url": "https://api.github.com/users/isopov/following{/other_user}", + "gists_url": "https://api.github.com/users/isopov/gists{/gist_id}", + "starred_url": "https://api.github.com/users/isopov/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/isopov/subscriptions", + "organizations_url": "https://api.github.com/users/isopov/orgs", + "repos_url": "https://api.github.com/users/isopov/repos", + "events_url": "https://api.github.com/users/isopov/events{/privacy}", + "received_events_url": "https://api.github.com/users/isopov/received_events", + "type": "User", + "user_view_type": "public", + "site_admin": false + }, + "parents": [ + { + "sha": "c8b42075fe76f67ee744f1b1d3ef24e27ae932d7", + "url": "https://api.github.com/repos/checkstyle/checkstyle/commits/c8b42075fe76f67ee744f1b1d3ef24e27ae932d7", + "html_url": "https://github.com/checkstyle/checkstyle/commit/c8b42075fe76f67ee744f1b1d3ef24e27ae932d7" + } + ], + "stats": { + "total": 4, + "additions": 4, + "deletions": 0 + }, + "files": [ + { + "sha": "68e0a3c8a069fe91c02ec9cb9b5f1f92c8ed83c5", + "filename": ".travis.yml", + "status": "added", + "additions": 4, + "deletions": 0, + "changes": 4, + "blob_url": "https://github.com/checkstyle/checkstyle/blob/4889a777da54c6848b92da273a28223cfaf8dfe9/.travis.yml", + "raw_url": "https://github.com/checkstyle/checkstyle/raw/4889a777da54c6848b92da273a28223cfaf8dfe9/.travis.yml", + "contents_url": "https://api.github.com/repos/checkstyle/checkstyle/contents/.travis.yml?ref=4889a777da54c6848b92da273a28223cfaf8dfe9", + "patch": "@@ -0,0 +1,4 @@\n+language: java\n+jdk:\n+ - openjdk6\n+ - openjdk7" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/1-user.json b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/1-user.json new file mode 100644 index 0000000000..88d7dbfc27 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/1-user.json @@ -0,0 +1,49 @@ +{ + "id": "0115dad1-3a5e-4bb6-a50f-9f316e079840", + "name": "user", + "request": { + "url": "/user", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "1-user.json", + "headers": { + "Date": "Fri, 23 Oct 2026 14:17:57 GMT", + "Content-Type": "application/json; charset=utf-8", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With", + "ETag": "W/\"fafe7dcb12a6ebbd5be01963886c9ec01feac3f276d143610c6051170faeab5f\"", + "Last-Modified": "Thu, 22 Oct 2026 06:09:50 GMT", + "X-OAuth-Scopes": "admin:public_key, gist, read:org, read:packages, repo, workflow", + "X-Accepted-OAuth-Scopes": "", + "x-oauth-client-id": "178c6fc778ccc68e1d6a", + "X-GitHub-Media-Type": "github.v3; format=json", + "x-github-api-version-selected": "2022-11-28", + "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning", + "Access-Control-Allow-Origin": "*", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Frame-Options": "deny", + "X-Content-Type-Options": "nosniff", + "X-XSS-Protection": "0", + "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin", + "Content-Security-Policy": "default-src 'none'", + "Server": "github.com", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "2863", + "X-RateLimit-Reset": "1792765442", + "X-RateLimit-Used": "2137", + "X-RateLimit-Resource": "core", + "X-GitHub-Request-Id": "EE1E:306C43:C6D8C:D9EC9:6ADB6C95", + "x-github-edge-region": "westus3" + } + }, + "uuid": "0115dad1-3a5e-4bb6-a50f-9f316e079840", + "persistent": true, + "insertionIndex": 1 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/2-r_c_checkstyle.json b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/2-r_c_checkstyle.json new file mode 100644 index 0000000000..c9087a9d02 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/2-r_c_checkstyle.json @@ -0,0 +1,49 @@ +{ + "id": "ae7cad8a-a9d3-4834-a285-7c9184fa5381", + "name": "repos_checkstyle_checkstyle", + "request": { + "url": "/repos/checkstyle/checkstyle", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "2-r_c_checkstyle.json", + "headers": { + "Date": "Fri, 23 Oct 2026 14:17:58 GMT", + "Content-Type": "application/json; charset=utf-8", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With", + "ETag": "W/\"9e492775d392eb8d29fa10faa7bb58ab40f8092c9b92fdcd0b157722ebf0b72a\"", + "Last-Modified": "Fri, 23 Oct 2026 12:15:07 GMT", + "X-OAuth-Scopes": "admin:public_key, gist, read:org, read:packages, repo, workflow", + "X-Accepted-OAuth-Scopes": "repo", + "x-oauth-client-id": "178c6fc778ccc68e1d6a", + "X-GitHub-Media-Type": "github.v3; format=json", + "x-github-api-version-selected": "2022-11-28", + "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning", + "Access-Control-Allow-Origin": "*", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Frame-Options": "deny", + "X-Content-Type-Options": "nosniff", + "X-XSS-Protection": "0", + "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin", + "Content-Security-Policy": "default-src 'none'", + "Server": "github.com", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "2861", + "X-RateLimit-Reset": "1792765442", + "X-RateLimit-Used": "2139", + "X-RateLimit-Resource": "core", + "X-GitHub-Request-Id": "EE44:22B09C:D5FC3:E9016:6ADB6C96", + "x-github-edge-region": "westus3" + } + }, + "uuid": "ae7cad8a-a9d3-4834-a285-7c9184fa5381", + "persistent": true, + "insertionIndex": 2 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/3-r_c_c_pulls_1.json b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/3-r_c_c_pulls_1.json new file mode 100644 index 0000000000..fd731a0070 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/3-r_c_c_pulls_1.json @@ -0,0 +1,52 @@ +{ + "id": "36c8b4d1-82f0-40a2-84f7-bd80f658e7a6", + "name": "repos_checkstyle_checkstyle_pulls_1", + "request": { + "url": "/repos/checkstyle/checkstyle/pulls/1", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "3-r_c_c_pulls_1.json", + "headers": { + "Access-Control-Allow-Origin": "*", + "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Content-Security-Policy": "default-src 'none'", + "Content-Type": "application/json; charset=utf-8", + "Date": "Fri, 23 Oct 2026 14:17:59 GMT", + "ETag": "\"c9179d4e109f401cec8c830d851ab84e62ad6b57889022a612af0a8ba5a0ef0f\"", + "Last-Modified": "Sun, 11 Oct 2026 14:04:53 GMT", + "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP, Accept-Encoding, Accept, X-Requested-With", + "X-Accepted-OAuth-Scopes": "repo", + "X-Content-Type-Options": "nosniff", + "X-Frame-Options": "deny", + "x-github-api-version-selected": "2022-11-28", + "X-GitHub-Media-Type": "github.v3; format=json", + "x-oauth-client-id": [ + "178c6fc778ccc68e1d6a", + "178c6fc778ccc68e1d6a" + ], + "X-OAuth-Scopes": "admin:public_key, gist, read:org, read:packages, repo, workflow", + "X-XSS-Protection": "0", + "Server": "github.com", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "2860", + "X-RateLimit-Reset": "1792765442", + "X-RateLimit-Used": "2140", + "X-RateLimit-Resource": "core", + "X-GitHub-Request-Id": "EE4D:6CC3C:D4542:E746A:6ADB6C96", + "x-github-edge-region": "westus3" + } + }, + "uuid": "36c8b4d1-82f0-40a2-84f7-bd80f658e7a6", + "persistent": true, + "insertionIndex": 3 +} \ No newline at end of file diff --git a/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/4-r_c_c_commits_4889a777.json b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/4-r_c_c_commits_4889a777.json new file mode 100644 index 0000000000..d4931ff351 --- /dev/null +++ b/src/test/resources/org/kohsuke/github/GHCommitPointerTest/wiremock/getCommitFromDeletedHeadRepository/mappings/4-r_c_c_commits_4889a777.json @@ -0,0 +1,49 @@ +{ + "id": "7a7947f1-9660-4301-93e8-47b16df70d0a", + "name": "repos_checkstyle_checkstyle_commits_4889a777da54c6848b92da273a28223cfaf8dfe9", + "request": { + "url": "/repos/checkstyle/checkstyle/commits/4889a777da54c6848b92da273a28223cfaf8dfe9", + "method": "GET", + "headers": { + "Accept": { + "equalTo": "application/vnd.github+json" + } + } + }, + "response": { + "status": 200, + "bodyFileName": "4-r_c_c_commits_4889a777.json", + "headers": { + "Date": "Fri, 23 Oct 2026 14:17:59 GMT", + "Content-Type": "application/json; charset=utf-8", + "Cache-Control": "private, max-age=60, s-maxage=60", + "Vary": "Accept, Authorization, Cookie, X-GitHub-OTP,Accept-Encoding, Accept, X-Requested-With", + "ETag": "W/\"79bdf2285f98568108cead37873b624a52a59fb4fbcbea65bc5a59aa0fd9d495\"", + "Last-Modified": "Tue, 10 Sep 2013 14:33:22 GMT", + "X-OAuth-Scopes": "admin:public_key, gist, read:org, read:packages, repo, workflow", + "X-Accepted-OAuth-Scopes": "", + "x-oauth-client-id": "178c6fc778ccc68e1d6a", + "X-GitHub-Media-Type": "github.v3; format=json", + "x-github-api-version-selected": "2022-11-28", + "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset, Warning", + "Access-Control-Allow-Origin": "*", + "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload", + "X-Frame-Options": "deny", + "X-Content-Type-Options": "nosniff", + "X-XSS-Protection": "0", + "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin", + "Content-Security-Policy": "default-src 'none'", + "Server": "github.com", + "X-RateLimit-Limit": "5000", + "X-RateLimit-Remaining": "2858", + "X-RateLimit-Reset": "1792765442", + "X-RateLimit-Used": "2142", + "X-RateLimit-Resource": "core", + "X-GitHub-Request-Id": "EE55:229582:C3FF9:D6E14:6ADB6C97", + "x-github-edge-region": "westus3" + } + }, + "uuid": "7a7947f1-9660-4301-93e8-47b16df70d0a", + "persistent": true, + "insertionIndex": 4 +} \ No newline at end of file From 9ce33d1df54c8d84299a061c895da99e31e436ea Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Fri, 23 Oct 2026 20:33:34 -0700 Subject: [PATCH 3/3] test: cover GHCommitPointer base commit and unset head/base Codecov reported three partial branches on this PR: the null checks in GHPullRequest.getHead()/getBase() and the repository check in GHCommitPointer.getCommit(). Add a base-pointer getCommit() case that resolves through the base repository, and a test for a pull request whose head and base are unset. Production code is unchanged. --- .../kohsuke/github/GHCommitPointerTest.java | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/test/java/org/kohsuke/github/GHCommitPointerTest.java b/src/test/java/org/kohsuke/github/GHCommitPointerTest.java index 57e6934214..9096b5f2df 100644 --- a/src/test/java/org/kohsuke/github/GHCommitPointerTest.java +++ b/src/test/java/org/kohsuke/github/GHCommitPointerTest.java @@ -1,5 +1,6 @@ package org.kohsuke.github; +import com.github.tomakehurst.wiremock.client.WireMock; import org.junit.Test; import java.io.IOException; @@ -39,6 +40,26 @@ public void getCommitFromDeletedHeadRepository() throws Exception { assertThat(commit, notNullValue()); assertThat(commit.getSHA1(), equalTo("4889a777da54c6848b92da273a28223cfaf8dfe9")); assertThat(commit.getOwner().getFullName(), equalTo("checkstyle/checkstyle")); + + // Base still has its repository, so getCommit() must use that repository instead of the pull-request fallback. + // The recorded commit body is only for the head SHA; serve the same shape for the base SHA. + GHCommitPointer base = pullRequest.getBase(); + assertThat(base, notNullValue()); + assertThat(base.getRepository(), notNullValue()); + assertThat(base.getRepository().getFullName(), equalTo("checkstyle/checkstyle")); + String baseSha = base.getSha(); + mockGitHub.apiServer() + .stubFor(WireMock.get(WireMock.urlPathEqualTo("/repos/checkstyle/checkstyle/commits/" + baseSha)) + .withHeader("Accept", WireMock.equalTo("application/vnd.github+json")) + .willReturn(WireMock.aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json; charset=utf-8") + .withBody("{\"sha\":\"" + baseSha + "\"}"))); + + GHCommit baseCommit = base.getCommit(); + assertThat(baseCommit, notNullValue()); + assertThat(baseCommit.getSHA1(), equalTo(baseSha)); + assertThat(baseCommit.getOwner().getFullName(), equalTo("checkstyle/checkstyle")); } /** @@ -54,4 +75,15 @@ public void getCommitWithoutRepositoryOrPullRequest() throws Exception { IOException failure = assertThrows(IOException.class, pointer::getCommit); assertThat(failure.getMessage(), equalTo("Repository information is unavailable for commit null")); } + + /** + * {@link GHPullRequest#getHead()} and {@link GHPullRequest#getBase()} return null when the pointer was not set. + */ + @Test + public void getHeadAndBaseWhenUnset() { + snapshotNotAllowed(); + GHPullRequest pullRequest = new GHPullRequest(); + assertThat(pullRequest.getHead(), nullValue()); + assertThat(pullRequest.getBase(), nullValue()); + } }