Toggle navigation
Toggle navigation
This project
Loading...
Sign in
open-source
/
warply_android_sdk_maven_plugin
Go to a project
Toggle navigation
Toggle navigation pinning
Projects
Groups
Snippets
Help
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Snippets
Network
Create a new issue
Builds
Commits
Issue Boards
Authored by
Panagiotis Triantafyllou
2025-06-30 16:53:37 +0300
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
e29d6801d93ffc7298d3e30dd474936010b56372
e29d6801
1 parent
718dd745
migrate to maven central
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
377 additions
and
28 deletions
build.gradle
scripts/publish-module.gradle
scripts/publish-root.gradle
warply_android_sdk/build.gradle
build.gradle
View file @
e29d680
...
...
@@ -11,13 +11,16 @@ buildscript {
classpath
'com.android.tools.build:gradle:8.8.0'
classpath
'com.google.gms:google-services:4.3.10'
classpath
'com.huawei.agconnect:agcp:1.9.1.300'
classpath
'io.github.gradle-nexus:publish-plugin:1.1.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
plugins
{
id
'maven-publish'
}
allprojects
{
repositories
{
mavenCentral
()
...
...
@@ -27,5 +30,4 @@ allprojects {
}
}
apply
plugin:
'io.github.gradle-nexus.publish-plugin'
apply
from:
"${rootDir}/scripts/publish-root.gradle"
...
...
scripts/publish-module.gradle
View file @
e29d680
...
...
@@ -40,7 +40,7 @@ afterEvaluate {
// Sources are now handled by the android block's singleVariant
// artifact javadocJar
//
Mostly self-explanatory metadata
//
POM metadata for Maven Central
pom
{
name
=
PUBLISH_ARTIFACT_ID
description
=
'Warply Android SDK Maven Plugin'
...
...
@@ -60,8 +60,7 @@ afterEvaluate {
// Add all other devs here...
}
// Version control info - if you're using GitHub, follow the
// format as seen here
// Version control info
scm
{
connection
=
'scm:git:git.warp.ly/open-source/warply_android_sdk_maven_plugin.git'
developerConnection
=
'scm:git:ssh://git.warp.ly/open-source/warply_android_sdk_maven_plugin.git'
...
...
@@ -81,3 +80,365 @@ signing {
)
sign
publishing
.
publications
}
// Configuration for Central Publishing (similar to Maven plugin configuration)
ext
.
centralPublishing
=
[
autoPublish:
false
,
// Manual publishing for safety
// waitUntil: "published", // Commented out - don't wait for publishing
deploymentName:
"Warply Android SDK ${PUBLISH_VERSION}"
,
centralBaseUrl:
"https://central.sonatype.com"
]
// Custom task that implements the same functionality as org.sonatype.central:central-publishing-maven-plugin:0.8.0
// This uses the Central Portal API directly to achieve the same result
task
publishToCentralPortal
{
dependsOn
'publishReleasePublicationToMavenLocal'
description
=
'Publishes to Maven Central Portal using the same API as central-publishing-maven-plugin:0.8.0'
group
=
'publishing'
doLast
{
def
username
=
rootProject
.
ext
[
"centralPortalUsername"
]
def
password
=
rootProject
.
ext
[
"centralPortalPassword"
]
def
config
=
project
.
ext
.
centralPublishing
if
(!
username
||
!
password
)
{
throw
new
GradleException
(
"Central Portal credentials not configured. Please set centralPortalUsername and centralPortalPassword in local.properties or environment variables."
)
}
println
"=== Central Portal Publishing ==="
println
"Deployment: ${config.deploymentName}"
println
"Auto-publish: ${config.autoPublish}"
println
"Portal URL: ${config.centralBaseUrl}"
println
""
// Step 1: Create deployment bundle
println
"Step 1: Creating deployment bundle..."
def
bundleFile
=
createDeploymentBundle
()
println
"✓ Bundle created: ${bundleFile.name} (${bundleFile.length()} bytes)"
// Step 2: Upload bundle to Central Portal
println
"\nStep 2: Uploading to Central Portal..."
def
deploymentId
=
uploadBundle
(
bundleFile
,
username
,
password
,
config
)
println
"✓ Upload successful. Deployment ID: ${deploymentId}"
// Step 3: Wait for validation
println
"\nStep 3: Waiting for validation..."
def
validationResult
=
waitForValidation
(
deploymentId
,
username
,
password
,
config
)
if
(
validationResult
.
success
)
{
def
state
=
validationResult
.
state
println
"✓ Validation successful! State: ${state}"
if
(
config
.
autoPublish
&&
state
==
"VALIDATED"
)
{
println
"\nStep 4: Auto-publishing..."
def
publishResult
=
publishDeployment
(
deploymentId
,
username
,
password
,
config
)
if
(
publishResult
.
success
)
{
println
"✓ Published successfully to Maven Central!"
}
else
{
throw
new
GradleException
(
"Auto-publishing failed: ${publishResult.error}"
)
}
}
else
if
(
state
==
"PUBLISHED"
)
{
println
"✓ Already published to Maven Central!"
def
response
=
validationResult
.
response
def
purls
=
response
.
purls
?:
[]
if
(
purls
)
{
println
" Published artifacts:"
purls
.
each
{
purl
->
println
" - ${purl}"
}
}
}
else
{
println
"\n✓ Deployment uploaded and validated successfully!"
println
"📋 Manual action required:"
println
" Visit: ${config.centralBaseUrl}/publishing/deployments"
println
" Find deployment: ${config.deploymentName}"
println
" Click 'Publish' to complete the process"
}
}
else
{
throw
new
GradleException
(
"Validation failed: ${validationResult.error}"
)
}
println
"\n=== Publishing Complete ==="
}
}
def
createDeploymentBundle
()
{
def
bundleDir
=
file
(
"${buildDir}/central-publishing"
)
def
stagingDir
=
file
(
"${bundleDir}/staging"
)
// Clean and create directories
bundleDir
.
deleteDir
()
stagingDir
.
mkdirs
()
// Create Maven repository structure
def
groupPath
=
PUBLISH_GROUP_ID
.
replace
(
'.'
,
'/'
)
def
artifactDir
=
file
(
"${stagingDir}/${groupPath}/${PUBLISH_ARTIFACT_ID}/${PUBLISH_VERSION}"
)
artifactDir
.
mkdirs
()
// Copy artifacts to staging area
def
artifacts
=
[:]
// AAR file
def
aarFile
=
file
(
"${buildDir}/outputs/aar/warply_android_sdk-release.aar"
)
if
(
aarFile
.
exists
())
{
def
targetAar
=
file
(
"${artifactDir}/${PUBLISH_ARTIFACT_ID}-${PUBLISH_VERSION}.aar"
)
copy
{
from
aarFile
into
artifactDir
rename
{
targetAar
.
name
}
}
artifacts
[
'aar'
]
=
targetAar
// Copy AAR signature if exists
def
aarSigFile
=
file
(
"${aarFile.path}.asc"
)
if
(
aarSigFile
.
exists
())
{
def
targetAarSig
=
file
(
"${targetAar.path}.asc"
)
copy
{
from
aarSigFile
into
artifactDir
rename
{
targetAarSig
.
name
}
}
artifacts
[
'aar-sig'
]
=
targetAarSig
}
}
// Sources JAR
def
sourcesFile
=
file
(
"${buildDir}/libs/warply_android_sdk-${PUBLISH_VERSION}-sources.jar"
)
if
(
sourcesFile
.
exists
())
{
def
targetSources
=
file
(
"${artifactDir}/${PUBLISH_ARTIFACT_ID}-${PUBLISH_VERSION}-sources.jar"
)
copy
{
from
sourcesFile
into
artifactDir
rename
{
targetSources
.
name
}
}
artifacts
[
'sources'
]
=
targetSources
// Copy sources signature if exists
def
sourcesSigFile
=
file
(
"${sourcesFile.path}.asc"
)
if
(
sourcesSigFile
.
exists
())
{
def
targetSourcesSig
=
file
(
"${targetSources.path}.asc"
)
copy
{
from
sourcesSigFile
into
artifactDir
rename
{
targetSourcesSig
.
name
}
}
artifacts
[
'sources-sig'
]
=
targetSourcesSig
}
}
// POM file
def
pomFile
=
file
(
"${buildDir}/publications/release/pom-default.xml"
)
if
(
pomFile
.
exists
())
{
def
targetPom
=
file
(
"${artifactDir}/${PUBLISH_ARTIFACT_ID}-${PUBLISH_VERSION}.pom"
)
copy
{
from
pomFile
into
artifactDir
rename
{
targetPom
.
name
}
}
artifacts
[
'pom'
]
=
targetPom
// Copy POM signature if exists
def
pomSigFile
=
file
(
"${pomFile.path}.asc"
)
if
(
pomSigFile
.
exists
())
{
def
targetPomSig
=
file
(
"${targetPom.path}.asc"
)
copy
{
from
pomSigFile
into
artifactDir
rename
{
targetPomSig
.
name
}
}
artifacts
[
'pom-sig'
]
=
targetPomSig
}
}
// Generate checksums for all files
artifacts
.
each
{
type
,
artifactFile
->
if
(
artifactFile
.
exists
())
{
generateChecksums
(
artifactFile
)
}
}
// Create bundle ZIP
def
bundleFile
=
file
(
"${bundleDir}/central-bundle.zip"
)
ant
.
zip
(
destfile:
bundleFile
)
{
fileset
(
dir:
stagingDir
)
}
return
bundleFile
}
def
generateChecksums
(
File
file
)
{
[
'md5'
,
'sha1'
,
'sha256'
,
'sha512'
].
each
{
algorithm
->
def
checksum
=
file
.
withInputStream
{
stream
->
java
.
security
.
MessageDigest
.
getInstance
(
algorithm
.
toUpperCase
()).
digest
(
stream
.
bytes
).
encodeHex
().
toString
()
}
new
File
(
"${file.path}.${algorithm}"
).
text
=
checksum
}
}
def
uploadBundle
(
File
bundleFile
,
String
username
,
String
password
,
Map
config
)
{
def
url
=
"${config.centralBaseUrl}/api/v1/publisher/upload"
def
credentials
=
"${username}:${password}"
.
bytes
.
encodeBase64
().
toString
()
// Add query parameters
def
publishingType
=
config
.
autoPublish
?
"AUTOMATIC"
:
"USER_MANAGED"
def
urlWithParams
=
"${url}?publishingType=${publishingType}&name=${URLEncoder.encode(config.deploymentName, 'UTF-8')}"
println
" Uploading to: ${urlWithParams}"
println
" Publishing type: ${publishingType}"
def
connection
=
new
URL
(
urlWithParams
).
openConnection
()
as
HttpURLConnection
connection
.
setRequestMethod
(
"POST"
)
connection
.
setRequestProperty
(
"Authorization"
,
"Bearer ${credentials}"
)
connection
.
setDoOutput
(
true
)
connection
.
setDoInput
(
true
)
// Create multipart/form-data boundary
def
boundary
=
"----WebKitFormBoundary"
+
System
.
currentTimeMillis
()
connection
.
setRequestProperty
(
"Content-Type"
,
"multipart/form-data; boundary=${boundary}"
)
// Write multipart data
connection
.
outputStream
.
withWriter
(
"UTF-8"
)
{
writer
->
writer
.
write
(
"--${boundary}\r\n"
)
writer
.
write
(
"Content-Disposition: form-data; name=\"bundle\"; filename=\"${bundleFile.name}\"\r\n"
)
writer
.
write
(
"Content-Type: application/octet-stream\r\n"
)
writer
.
write
(
"\r\n"
)
writer
.
flush
()
// Write file content
bundleFile
.
withInputStream
{
input
->
connection
.
outputStream
<<
input
}
writer
.
write
(
"\r\n--${boundary}--\r\n"
)
writer
.
flush
()
}
// Get response
def
responseCode
=
connection
.
responseCode
if
(
responseCode
==
201
)
{
def
deploymentId
=
connection
.
inputStream
.
text
.
trim
()
println
" ✓ Upload successful (HTTP ${responseCode})"
return
deploymentId
}
else
{
def
errorMessage
=
connection
.
errorStream
?.
text
?:
"Unknown error"
throw
new
GradleException
(
"Upload failed (HTTP ${responseCode}): ${errorMessage}"
)
}
}
def
waitForValidation
(
String
deploymentId
,
String
username
,
String
password
,
Map
config
)
{
def
credentials
=
"${username}:${password}"
.
bytes
.
encodeBase64
().
toString
()
def
maxAttempts
=
60
// 5 minutes with 5-second intervals
def
attempt
=
0
while
(
attempt
<
maxAttempts
)
{
attempt
++
def
url
=
"${config.centralBaseUrl}/api/v1/publisher/status?id=${deploymentId}"
def
connection
=
new
URL
(
url
).
openConnection
()
as
HttpURLConnection
connection
.
setRequestMethod
(
"POST"
)
connection
.
setRequestProperty
(
"Authorization"
,
"Bearer ${credentials}"
)
connection
.
setRequestProperty
(
"Content-Type"
,
"application/json"
)
def
responseCode
=
connection
.
responseCode
if
(
responseCode
==
200
)
{
def
response
=
new
groovy
.
json
.
JsonSlurper
().
parseText
(
connection
.
inputStream
.
text
)
def
state
=
response
.
deploymentState
println
" Status check ${attempt}: ${state}"
switch
(
state
)
{
case
"PENDING"
:
case
"VALIDATING"
:
// Continue waiting
Thread
.
sleep
(
5000
)
break
case
"VALIDATED"
:
return
[
success:
true
,
state:
state
,
response:
response
]
case
"PUBLISHED"
:
return
[
success:
true
,
state:
state
,
response:
response
]
case
"FAILED"
:
def
errors
=
response
.
errors
?:
[
"Unknown validation error"
]
return
[
success:
false
,
error:
"Validation failed: ${errors.join(', ')}"
,
response:
response
]
default:
return
[
success:
false
,
error:
"Unknown deployment state: ${state}"
,
response:
response
]
}
}
else
{
def
errorMessage
=
connection
.
errorStream
?.
text
?:
"Unknown error"
throw
new
GradleException
(
"Status check failed (HTTP ${responseCode}): ${errorMessage}"
)
}
}
return
[
success:
false
,
error:
"Timeout waiting for validation (${maxAttempts * 5} seconds)"
]
}
def
publishDeployment
(
String
deploymentId
,
String
username
,
String
password
,
Map
config
)
{
def
credentials
=
"${username}:${password}"
.
bytes
.
encodeBase64
().
toString
()
def
url
=
"${config.centralBaseUrl}/api/v1/publisher/deployment/${deploymentId}"
println
" Calling publish API..."
def
connection
=
new
URL
(
url
).
openConnection
()
as
HttpURLConnection
connection
.
setRequestMethod
(
"POST"
)
connection
.
setRequestProperty
(
"Authorization"
,
"Bearer ${credentials}"
)
def
responseCode
=
connection
.
responseCode
if
(
responseCode
==
204
)
{
println
" ✓ Publish request successful (HTTP ${responseCode})"
// Wait for publishing to complete
println
" Waiting for publishing to complete..."
def
result
=
waitForPublishing
(
deploymentId
,
username
,
password
,
config
)
return
result
}
else
{
def
errorMessage
=
connection
.
errorStream
?.
text
?:
"Unknown error"
throw
new
GradleException
(
"Publish failed (HTTP ${responseCode}): ${errorMessage}"
)
}
}
def
waitForPublishing
(
String
deploymentId
,
String
username
,
String
password
,
Map
config
)
{
def
credentials
=
"${username}:${password}"
.
bytes
.
encodeBase64
().
toString
()
def
maxAttempts
=
120
// 10 minutes with 5-second intervals
def
attempt
=
0
while
(
attempt
<
maxAttempts
)
{
attempt
++
def
url
=
"${config.centralBaseUrl}/api/v1/publisher/status?id=${deploymentId}"
def
connection
=
new
URL
(
url
).
openConnection
()
as
HttpURLConnection
connection
.
setRequestMethod
(
"POST"
)
connection
.
setRequestProperty
(
"Authorization"
,
"Bearer ${credentials}"
)
connection
.
setRequestProperty
(
"Content-Type"
,
"application/json"
)
def
responseCode
=
connection
.
responseCode
if
(
responseCode
==
200
)
{
def
response
=
new
groovy
.
json
.
JsonSlurper
().
parseText
(
connection
.
inputStream
.
text
)
def
state
=
response
.
deploymentState
println
" Publishing status ${attempt}: ${state}"
switch
(
state
)
{
case
"PUBLISHING"
:
// Continue waiting
Thread
.
sleep
(
5000
)
break
case
"PUBLISHED"
:
def
purls
=
response
.
purls
?:
[]
println
" ✓ Successfully published to Maven Central!"
if
(
purls
)
{
println
" Published artifacts:"
purls
.
each
{
purl
->
println
" - ${purl}"
}
}
return
[
success:
true
,
state:
state
,
response:
response
]
case
"FAILED"
:
def
errors
=
response
.
errors
?:
[
"Unknown publishing error"
]
return
[
success:
false
,
error:
"Publishing failed: ${errors.join(', ')}"
,
response:
response
]
default:
return
[
success:
false
,
error:
"Unexpected state during publishing: ${state}"
,
response:
response
]
}
}
else
{
def
errorMessage
=
connection
.
errorStream
?.
text
?:
"Unknown error"
throw
new
GradleException
(
"Publishing status check failed (HTTP ${responseCode}): ${errorMessage}"
)
}
}
return
[
success:
false
,
error:
"Timeout waiting for publishing (${maxAttempts * 5} seconds)"
]
}
...
...
scripts/publish-root.gradle
View file @
e29d680
// Create variables with empty default values
// Central Portal credentials
ext
[
"centralPortalUsername"
]
=
''
ext
[
"centralPortalPassword"
]
=
''
// keyId is the last 8 characters of the GPG key
ext
[
"signing.keyId"
]
=
''
// password is the passphrase of the GPG key
ext
[
"signing.password"
]
=
''
// key is the base64 private GPG key
ext
[
"signing.key"
]
=
''
// osshrUsername and ossrhPassword are the account details for MavenCentral
// which we’ve chosen at the Jira registration step (Sonatype site))
ext
[
"ossrhUsername"
]
=
''
ext
[
"ossrhPassword"
]
=
''
ext
[
"sonatypeStagingProfileId"
]
=
''
File
secretPropsFile
=
project
.
rootProject
.
file
(
'local.properties'
)
if
(
secretPropsFile
.
exists
())
{
...
...
@@ -19,24 +18,11 @@ if (secretPropsFile.exists()) {
new
FileInputStream
(
secretPropsFile
).
withCloseable
{
is
->
p
.
load
(
is
)
}
p
.
each
{
name
,
value
->
ext
[
name
]
=
value
}
}
else
{
// Use system environment variables
ext
[
"ossrhUsername"
]
=
System
.
getenv
(
'OSSRH_USERNAME'
)
ext
[
"ossrhPassword"
]
=
System
.
getenv
(
'OSSRH_PASSWORD'
)
ext
[
"sonatypeStagingProfileId"
]
=
System
.
getenv
(
'SONATYPE_STAGING_PROFILE_ID'
)
// Use system environment variables for signing
ext
[
"signing.keyId"
]
=
System
.
getenv
(
'SIGNING_KEY_ID'
)
ext
[
"signing.password"
]
=
System
.
getenv
(
'SIGNING_PASSWORD'
)
ext
[
"signing.key"
]
=
System
.
getenv
(
'SIGNING_KEY'
)
}
// Set up Sonatype repository
nexusPublishing
{
repositories
{
sonatype
{
stagingProfileId
=
sonatypeStagingProfileId
username
=
ossrhUsername
password
=
ossrhPassword
nexusUrl
.
set
(
uri
(
"https://s01.oss.sonatype.org/service/local/"
))
snapshotRepositoryUrl
.
set
(
uri
(
"https://s01.oss.sonatype.org/content/repositories/snapshots/"
))
}
}
// Central Portal credentials can also come from environment
ext
[
"centralPortalUsername"
]
=
System
.
getenv
(
'CENTRAL_PORTAL_USERNAME'
)
ext
[
"centralPortalPassword"
]
=
System
.
getenv
(
'CENTRAL_PORTAL_PASSWORD'
)
}
...
...
warply_android_sdk/build.gradle
View file @
e29d680
...
...
@@ -5,7 +5,7 @@ android.buildFeatures.buildConfig = true
ext
{
PUBLISH_GROUP_ID
=
'ly.warp'
PUBLISH_VERSION
=
'4.5.5.4m
6
'
PUBLISH_VERSION
=
'4.5.5.4m
7
'
PUBLISH_ARTIFACT_ID
=
'warply-android-sdk'
}
...
...
Please
register
or
login
to post a comment