From 4d66a1f827639017adcd132039e513e2d5f848d3 Mon Sep 17 00:00:00 2001 From: Advait Mahashabde Date: Mon, 4 Aug 2025 15:09:35 -0700 Subject: [PATCH 1/2] Update release scripts. --- .github/workflows/deploy-snapshot.yml | 6 -- .github/workflows/publish-maven-central.yml | 8 +-- gradle/publishing.gradle | 6 ++ gradle/publishing_aar.gradle | 7 ++ gradle/sonatype-central.gradle | 71 +++++++++++++++++++++ 5 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 gradle/sonatype-central.gradle diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 6672f84..8864e71 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -33,9 +33,3 @@ jobs: - name: Build run: ./gradlew build - - - name: Publish package - run: ./gradlew publishAllPublicationsToSonatypeSnapshotRepository - env: - SONATYPE_USER: ${{ secrets.SONATYPE_USER }} - SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} diff --git a/.github/workflows/publish-maven-central.yml b/.github/workflows/publish-maven-central.yml index 5d467d3..9d43b22 100644 --- a/.github/workflows/publish-maven-central.yml +++ b/.github/workflows/publish-maven-central.yml @@ -19,10 +19,10 @@ jobs: - name: Build run: ./gradlew build - - name: Publish package - run: ./gradlew publishAllPublicationsToMavenCentralRepository + - name: Upload to Sonatype Central + run: ./gradlew uploadToSonatypeCentral env: - SONATYPE_USER: ${{ secrets.SONATYPE_USER }} - SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USER: ${{ secrets.CENTRAL_SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.CENTRAL_SONATYPE_PASSWORD }} DEXMAKER_GPG_PRIVATE_KEY: ${{ secrets.DEXMAKER_GPG_PRIVATE_KEY }} DEXMAKER_GPG_PRIVATE_KEY_PASSWORD: ${{ secrets.DEXMAKER_GPG_PRIVATE_KEY_PASSWORD }} diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index 9d504ae..aca3683 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -1,11 +1,17 @@ apply plugin: 'maven-publish' apply plugin: 'signing' +apply from: "$rootDir/gradle/sonatype-central.gradle" java { withSourcesJar() withJavadocJar() } +// Task to publish to local repository for bundle creation +tasks.register("publishToLocalMaven", Task) { + dependsOn "publishMavenPublicationToLocalRepository" +} + publishing { publications { maven(MavenPublication) { diff --git a/gradle/publishing_aar.gradle b/gradle/publishing_aar.gradle index 0864833..a93de56 100644 --- a/gradle/publishing_aar.gradle +++ b/gradle/publishing_aar.gradle @@ -1,5 +1,6 @@ apply plugin: 'maven-publish' apply plugin: 'signing' +apply from: "$rootDir/gradle/sonatype-central.gradle" android { // Explicitly set the NDK release to ensure we use the latest stable, as @@ -20,6 +21,12 @@ android { } } +// Task to publish to local repository for bundle creation +tasks.register("publishToLocalMaven", Task) { + dependsOn "publishMavenPublicationToLocalRepository" +} + + // AGP creates the components in afterEvaluate, so we need to use it too // https://developer.android.com/studio/build/maven-publish-plugin afterEvaluate { diff --git a/gradle/sonatype-central.gradle b/gradle/sonatype-central.gradle new file mode 100644 index 0000000..33e1731 --- /dev/null +++ b/gradle/sonatype-central.gradle @@ -0,0 +1,71 @@ +// Common Sonatype Central publishing functionality +// This script can be applied by both publishing.gradle and publishing_aar.gradle + +// Custom task to upload deployment bundle to Sonatype Central API +tasks.register("uploadToSonatypeCentral") { + dependsOn publishToLocalMaven + doLast { + def sonatypeUsername = System.getenv("SONATYPE_USER") + def sonatypePassword = System.getenv("SONATYPE_PASSWORD") + + if (!sonatypeUsername || !sonatypePassword) { + throw new GradleException("SONATYPE_USER and SONATYPE_PASSWORD environment variables must be set") + } + + // Create deployment bundle (zip file containing all artifacts) + def bundleFile = new File(project.buildDir, "distributions/deployment-bundle.zip") + bundleFile.parentFile.mkdirs() + + ant.zip(destfile: bundleFile) { + fileset(dir: "${project.buildDir}/repo") { + include(name: "**/*") + } + } + + // Create base64 encoded credentials + def credentials = "${sonatypeUsername}:${sonatypePassword}" + def encodedCredentials = Base64.getEncoder().encodeToString(credentials.getBytes()) + + // Upload bundle to Sonatype Central API + def connection = new URL("https://central.sonatype.com/api/v1/publisher/upload").openConnection() + connection.setRequestMethod("POST") + connection.setRequestProperty("Authorization", "Bearer ${encodedCredentials}") + connection.setRequestProperty("Content-Type", "multipart/form-data") + connection.setDoOutput(true) + + // Set up multipart form data + def boundary = "----WebKitFormBoundary" + System.currentTimeMillis() + connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary) + + def outputStream = connection.getOutputStream() + def writer = new PrintWriter(new OutputStreamWriter(outputStream, "UTF-8"), true) + + // Add file part + writer.append("--" + boundary).append("\r\n") + writer.append("Content-Disposition: form-data; name=\"bundle\"; filename=\"" + bundleFile.name + "\"").append("\r\n") + writer.append("Content-Type: application/zip").append("\r\n") + writer.append("\r\n") + writer.flush() + + // Write file content + bundleFile.withInputStream { inputStream -> + outputStream << inputStream + } + outputStream.flush() + + writer.append("\r\n") + writer.append("--" + boundary + "--").append("\r\n") + writer.close() + + // Get response + def responseCode = connection.getResponseCode() + if (responseCode == 200 || responseCode == 201) { + println "Bundle uploaded successfully to Sonatype Central" + def response = connection.getInputStream().getText() + println "Response: ${response}" + } else { + def errorResponse = connection.getErrorStream()?.getText() ?: "No error details available" + throw new GradleException("Failed to upload bundle. Response code: ${responseCode}, Error: ${errorResponse}") + } + } +} From 27c147da126d443cf3473d1b966204739347266a Mon Sep 17 00:00:00 2001 From: Advait Mahashabde Date: Mon, 4 Aug 2025 15:57:44 -0700 Subject: [PATCH 2/2] Few more updates to script --- .github/workflows/deploy-snapshot.yml | 6 ++++++ gradle/publishing.gradle | 12 ++---------- gradle/publishing_aar.gradle | 12 ++---------- gradle/sonatype-central.gradle | 28 +++++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/.github/workflows/deploy-snapshot.yml b/.github/workflows/deploy-snapshot.yml index 8864e71..c918168 100644 --- a/.github/workflows/deploy-snapshot.yml +++ b/.github/workflows/deploy-snapshot.yml @@ -33,3 +33,9 @@ jobs: - name: Build run: ./gradlew build + + - name: Publish package + run: ./gradlew publishSnapshotToCentral + env: + SONATYPE_USER: ${{ secrets.SONATYPE_USER }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle index aca3683..4932a1e 100644 --- a/gradle/publishing.gradle +++ b/gradle/publishing.gradle @@ -44,16 +44,8 @@ publishing { def sonatypeUsername = System.getenv("SONATYPE_USER") def sonatypePassword = System.getenv("SONATYPE_PASSWORD") maven { - name = "sonatypeSnapshot" - url = "https://oss.sonatype.org/content/repositories/snapshots" - credentials { - username = sonatypeUsername - password = sonatypePassword - } - } - maven { - name = "mavenCentral" - url = "https://oss.sonatype.org/service/local/staging/deploy/maven2" + name = "centralSnapshot" + url = "https://central.sonatype.com/api/v1/publisher/snapshots" credentials { username = sonatypeUsername password = sonatypePassword diff --git a/gradle/publishing_aar.gradle b/gradle/publishing_aar.gradle index a93de56..39b1b7a 100644 --- a/gradle/publishing_aar.gradle +++ b/gradle/publishing_aar.gradle @@ -62,16 +62,8 @@ afterEvaluate { def sonatypeUsername = System.getenv("SONATYPE_USER") def sonatypePassword = System.getenv("SONATYPE_PASSWORD") maven { - name = "sonatypeSnapshot" - url = "https://oss.sonatype.org/content/repositories/snapshots" - credentials { - username = sonatypeUsername - password = sonatypePassword - } - } - maven { - name = "mavenCentral" - url = "https://oss.sonatype.org/service/local/staging/deploy/maven2" + name = "centralSnapshot" + url = "https://central.sonatype.com/api/v1/publisher/snapshots" credentials { username = sonatypeUsername password = sonatypePassword diff --git a/gradle/sonatype-central.gradle b/gradle/sonatype-central.gradle index 33e1731..626e7dc 100644 --- a/gradle/sonatype-central.gradle +++ b/gradle/sonatype-central.gradle @@ -69,3 +69,31 @@ tasks.register("uploadToSonatypeCentral") { } } } + +// Task to publish snapshots to Sonatype Central Portal +tasks.register("publishSnapshotToCentral") { + description = "Publishes snapshot artifacts to Sonatype Central Portal" + group = "publishing" + + doFirst { + // Ensure version ends with -SNAPSHOT + if (!project.version.toString().endsWith('-SNAPSHOT')) { + throw new GradleException("Version must end with -SNAPSHOT to publish as snapshot. Current version: ${project.version}") + } + } + + dependsOn "publishAllPublicationsToCentralSnapshotRepository" +} + +// Task to publish artifacts to local Maven repository +tasks.register("publishToLocalMavenRepository") { + description = "Publishes all artifacts to local Maven repository (~/.m2/repository)" + group = "publishing" + + dependsOn "publishToMavenLocal" + + doLast { + println "Artifacts published to local Maven repository at: ${System.getProperty('user.home')}/.m2/repository" + println "You can now use the artifacts in other local projects with version: ${project.version}" + } +}