From 18ac6e342f6aab1a22339cc50e54efb97fa0d0c1 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 5 Oct 2026 18:27:44 -0400 Subject: [PATCH 1/4] Update build and release workflows - Remove the gradle-git and github-pages plugins, whose dependencies no longer resolve, so the project builds again - Publish through gradle-nexus/publish-plugin and remove the staging repository script - Run CI on ubuntu-latest with current action versions and JDK 8, 11, 17 - Publish before creating the GitHub release so a failed publish does not leave a tag behind --- .github/workflows/ci.yml | 16 +++--- .github/workflows/release.yml | 73 +++++++++------------------- .github/workflows/release_pr.yml | 4 +- build.gradle | 46 ++++-------------- scripts/get_staging_repository_id.py | 29 ----------- 5 files changed, 43 insertions(+), 125 deletions(-) delete mode 100644 scripts/get_staging_repository_id.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e79e996..6bb41408 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,20 +7,24 @@ on: jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - java-version: [ 8, 9, 10, 11, 17, 18 ] + java-version: [ 8, 11, 17 ] steps: - - uses: actions/checkout@v2.3.1 + - uses: actions/checkout@v4 - name: Set up JDK ${{ matrix.java-version }} - uses: actions/setup-java@v2.3.1 + uses: actions/setup-java@v4 with: java-version: "${{ matrix.java-version }}" - distribution: "adopt-openj9" + distribution: "temurin" - name: Build & Test run: ./gradlew jacocoTestReport test - name: Code coverage - run: bash <(curl -s https://codecov.io/bash) + if: ${{ matrix.java-version == 17 }} + uses: codecov/codecov-action@v5 + with: + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 49ed401b..af7086ee 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Prepare tag @@ -26,54 +26,28 @@ jobs: id: release_output if: ${{ steps.prepare_tag.outcome == 'success' }} run: | - echo "::set-output name=tag::${{ env.TAG }}" + echo "tag=${{ env.TAG }}" >> $GITHUB_OUTPUT outputs: tag: ${{ steps.release_output.outputs.tag }} - create-github-release: + publish: runs-on: ubuntu-latest needs: check-release-tag if: ${{ needs.check-release-tag.outputs.tag }} steps: - - uses: actions/checkout@v2 - - name: Prepare tag - run: | - export TAG=v$(awk '/^version = / { gsub("\"", ""); print $3 }' build.gradle) - echo "TAG=$TAG" >> $GITHUB_ENV - - name: Setup git - run: | - git config user.email "pusher-ci@pusher.com" - git config user.name "Pusher CI" - - name: Prepare description - run: | - csplit -s CHANGELOG.md "/##/" {1} - cat xx01 > CHANGELOG.tmp - - name: Create Release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: - tag_name: ${{ env.TAG }} - release_name: ${{ env.TAG }} - body_path: CHANGELOG.tmp - draft: false - prerelease: false - - publish: - runs-on: ubuntu-latest - needs: create-github-release - steps: - - uses: actions/checkout@v2 + java-version: "17" + distribution: "temurin" - name: Create gradle.properties shell: bash run: | mkdir -p _gradle_user_home echo "GRADLE_USER_HOME=_gradle_user_home" >> $GITHUB_ENV cat < _gradle_user_home/gradle.properties - github.username=${{ secrets.PUSHER_CI_GITHUB_PRIVATE_TOKEN }} - github.password="" - maven.username=${{ secrets.MAVEN_USERNAME }} - maven.password=${{ secrets.MAVEN_PASSWORD }} + sonatypeUsername=${{ secrets.MAVEN_USERNAME }} + sonatypePassword=${{ secrets.MAVEN_PASSWORD }} signing.keyId=${{ secrets.SIGNING_KEY_ID }} signing.password=${{ secrets.SIGNING_PASSWORD }} signing.secretKeyRingFile=_gradle_user_home/pusher-maven-gpg-signing-key.gpg @@ -81,23 +55,20 @@ jobs: echo "${{ secrets.PUSHER_MAVEN_GPG_SIGNING_KEY }}" | base64 --decode > _gradle_user_home/pusher-maven-gpg-signing-key.gpg - name: Publish run: | - ./gradlew publish + ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository - finish-release: + create-github-release: runs-on: ubuntu-latest - needs: publish - env: - NEXUS_USERNAME: ${{ secrets.MAVEN_USERNAME }} - NEXUS_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} + needs: [ check-release-tag, publish ] steps: - - uses: actions/checkout@v2 - - id: get_staging_repository_id - name: Get staging repository id + - uses: actions/checkout@v4 + - name: Prepare description run: | - echo "staging_repository_id=$(python3 scripts/get_staging_repository_id.py)" >> $GITHUB_OUTPUT - - name: Release - uses: nexus-actions/release-nexus-staging-repo@main - with: - username: ${{ secrets.MAVEN_USERNAME }} - password: ${{ secrets.MAVEN_PASSWORD }} - staging_repository_id: ${{ steps.get_staging_repository_id.outputs.staging_repository_id }} + csplit -s CHANGELOG.md "/##/" {1} + cat xx01 > CHANGELOG.tmp + - name: Create Release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ needs.check-release-tag.outputs.tag }} + run: | + gh release create "$TAG" --title "$TAG" --notes-file CHANGELOG.tmp --target "$GITHUB_SHA" diff --git a/.github/workflows/release_pr.yml b/.github/workflows/release_pr.yml index 98ce050f..781f143f 100644 --- a/.github/workflows/release_pr.yml +++ b/.github/workflows/release_pr.yml @@ -11,13 +11,13 @@ jobs: name: Prepare release runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Get current version shell: bash run: | CURRENT_VERSION=$(awk '/^version = / { gsub("\"", ""); print $3 }' build.gradle) echo "CURRENT_VERSION=$CURRENT_VERSION" >> $GITHUB_ENV - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: repository: pusher/public_actions path: .github/actions diff --git a/build.gradle b/build.gradle index 8a23bcf4..f35abd6b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,28 +1,12 @@ import org.apache.tools.ant.filters.ReplaceTokens -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath 'org.ajoberstar:gradle-git:1.1.0' - } -} - plugins { id "eclipse" id "jacoco" id "java-library" id "maven-publish" - id "org.ajoberstar.github-pages" version "1.7.2" id "signing" -} - -def getProperty = { property -> - if (!project.hasProperty(property)) { - throw new GradleException("${property} property must be set") - } - return project.property(property) + id "io.github.gradle-nexus.publish-plugin" version "2.0.0" } group = "com.pusher" @@ -111,18 +95,6 @@ task javadocJar(type: Jar, dependsOn: javadoc) { } assemble.dependsOn javadocJar -githubPages { - repoUri = 'https://github.com/pusher/pusher-websocket-java.git' - pages { - from javadoc.outputs.files - } - commitMessage = "JavaDoc gh-pages for ${version}" - credentials { - username = { getProperty("github.username") } - password = { getProperty("github.password") } - } -} - artifacts { archives jar, fatJar, sourcesJar, javadocJar } @@ -174,15 +146,15 @@ publishing { } } } +} + +nexusPublishing { repositories { - maven { - def releaseRepositoryUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2/" - def snapshotRepositoryUrl = "https://oss.sonatype.org/content/repositories/snapshots/" - url = version.endsWith("SNAPSHOT") ? snapshotRepositoryUrl : releaseRepositoryUrl - credentials { - username = findProperty("maven.username") ?: "" - password = findProperty("maven.password") ?: "" - } + sonatype { + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + username = findProperty("sonatypeUsername") ?: "" + password = findProperty("sonatypePassword") ?: "" } } } diff --git a/scripts/get_staging_repository_id.py b/scripts/get_staging_repository_id.py deleted file mode 100644 index 4d73556d..00000000 --- a/scripts/get_staging_repository_id.py +++ /dev/null @@ -1,29 +0,0 @@ -import base64 -import json -import os -import sys -import urllib.request - -username = os.environ.get('NEXUS_USERNAME') -password = os.environ.get('NEXUS_PASSWORD') - -def get(url, username, password): - req = urllib.request.Request(url) - base64_auth = base64.b64encode(bytes('{}:{}'.format(username, password),'ascii')) - req.add_header("Authorization", "Basic {}".format(base64_auth.decode('utf-8'))) - req.add_header('Accept', 'application/json') - with urllib.request.urlopen(req) as response: - return response.read() - -def getRepositories(username, password): - return json.loads(get("https://oss.sonatype.org/service/local/staging/profile_repositories", username, password)) - -repositories = getRepositories(username, password).get("data") -if len(repositories) != 1: - sys.stderr.write("Zero or more than one staging repository. Exiting. Please execute the process manually.") - exit(1) - -repositoryId = repositories[0].get("repositoryId") -print(repositoryId) - - From 85b4798f22e6e06a6da3d848d3abd0e09706dbd3 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 5 Oct 2026 18:38:32 -0400 Subject: [PATCH 2/4] Potential fix for pull request finding 'CodeQL / Workflow does not contain permissions' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6bb41408..feee118a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,9 @@ on: push: branches: [ master, main ] +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest From 3bd02c7747ab3d91acebf0fcc83d39871b898c35 Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 5 Oct 2026 18:40:43 -0400 Subject: [PATCH 3/4] Pin codecov action and allow more time in pong timeout test Pin codecov/codecov-action to the v5.5.5 commit. The pong timeout test waited exactly as long as the close takes to be scheduled, so it failed intermittently on CI. --- .github/workflows/ci.yml | 2 +- .../client/connection/websocket/WebSocketConnectionTest.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index feee118a..c876c51b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: run: ./gradlew jacocoTestReport test - name: Code coverage if: ${{ matrix.java-version == 17 }} - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5.5.5 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: false diff --git a/src/test/java/com/pusher/client/connection/websocket/WebSocketConnectionTest.java b/src/test/java/com/pusher/client/connection/websocket/WebSocketConnectionTest.java index afee2cd6..4a74abc7 100644 --- a/src/test/java/com/pusher/client/connection/websocket/WebSocketConnectionTest.java +++ b/src/test/java/com/pusher/client/connection/websocket/WebSocketConnectionTest.java @@ -367,7 +367,8 @@ public void testPongTimeoutResultsInClosingConnection() { connection.connect(); connection.onMessage(CONN_ESTABLISHED_EVENT); - verify(mockUnderlyingConnection, timeout((int) (ACTIVITY_TIMEOUT + PONG_TIMEOUT))).close(); + // The close is scheduled at ACTIVITY_TIMEOUT + PONG_TIMEOUT, so allow extra time for slow CI runners + verify(mockUnderlyingConnection, timeout((int) (ACTIVITY_TIMEOUT + PONG_TIMEOUT) * 2)).close(); } @Test From b7a8caf671d5c6a75f35d9d3f0ee9b171c32d4ab Mon Sep 17 00:00:00 2001 From: Keith Date: Mon, 5 Oct 2026 18:45:03 -0400 Subject: [PATCH 4/4] Restore staging repository script Keep the file for now so CodeQL's Python analysis has code to scan. It is no longer used by the release workflow and can be removed once Python is removed from the CodeQL configuration. --- scripts/get_staging_repository_id.py | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 scripts/get_staging_repository_id.py diff --git a/scripts/get_staging_repository_id.py b/scripts/get_staging_repository_id.py new file mode 100644 index 00000000..4d73556d --- /dev/null +++ b/scripts/get_staging_repository_id.py @@ -0,0 +1,29 @@ +import base64 +import json +import os +import sys +import urllib.request + +username = os.environ.get('NEXUS_USERNAME') +password = os.environ.get('NEXUS_PASSWORD') + +def get(url, username, password): + req = urllib.request.Request(url) + base64_auth = base64.b64encode(bytes('{}:{}'.format(username, password),'ascii')) + req.add_header("Authorization", "Basic {}".format(base64_auth.decode('utf-8'))) + req.add_header('Accept', 'application/json') + with urllib.request.urlopen(req) as response: + return response.read() + +def getRepositories(username, password): + return json.loads(get("https://oss.sonatype.org/service/local/staging/profile_repositories", username, password)) + +repositories = getRepositories(username, password).get("data") +if len(repositories) != 1: + sys.stderr.write("Zero or more than one staging repository. Exiting. Please execute the process manually.") + exit(1) + +repositoryId = repositories[0].get("repositoryId") +print(repositoryId) + +