Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy-snapshot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
run: ./gradlew build

- name: Publish package
run: ./gradlew publishAllPublicationsToSonatypeSnapshotRepository
run: ./gradlew publishSnapshotToCentral
env:
SONATYPE_USER: ${{ secrets.SONATYPE_USER }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
8 changes: 4 additions & 4 deletions .github/workflows/publish-maven-central.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}
18 changes: 8 additions & 10 deletions gradle/publishing.gradle
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -38,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
Expand Down
19 changes: 9 additions & 10 deletions gradle/publishing_aar.gradle
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -55,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
Expand Down
99 changes: 99 additions & 0 deletions gradle/sonatype-central.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// 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}")
}
}
}

// 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}"
}
}
Loading