preface
This article is used to record how to upload your library to maven central warehouse,
- First, we need to register the jira account of sonatype, and then apply to create our own repo. After the official review, we can have our own space;
- We use gradle's maven publish and signing plug-ins to simplify the operation of packaging and uploading. After configuration, we can upload to the maven warehouse through gradle task;
- When uploading, you can choose to upload to the snapshot storage area or staging storage area. These two storage areas can be accessed immediately after uploading. The snapshot area can be accessed publicly, while staging can only be used by yourself or someone with permission. You need to verify the user name and password;
- If you need to publish the version of staging area to everyone, you can publish it through the release operation on sonatype website;
Create sonatype account and group
reference resources: OSSRH Guide - The Central Repository Documentation (sonatype.org)
-
Create issues: Create question - Sonatype JIRA
The warehouse can be uploaded only after the issues are created and approved by the administrator. After creation, it is equivalent to having a group. After that, other items can be uploaded to the group without creating issues again;
After creating new issues, the system will reply and process them according to the requirements of the reply. After passing the reply, there will be a similar reply:
com.github.hanlyjiang has been prepared, now user(s) hanlyjiang can: Deploy snapshot artifacts into repository https://oss.sonatype.org/content/repositories/snapshots Deploy release artifacts into the staging repository https://oss.sonatype.org/service/local/staging/deploy/maven2 Release staged artifacts into repository 'Releases' please comment on this ticket when you promoted your first release, thanks
That means we have our own group;
Generate signature key
All files uploaded to the warehouse must be signed, otherwise they cannot be published. Therefore, we need to generate keys for signature, and push the keys to the public key server, which can be accessed by the sonatype server for verification;
Generate key
You need to enter the password in the process. Please remember the password after entering
gpg --gen-key gpg (GnuPG) 2.2.22; Copyright (C) 2020 Free Software Foundation, Inc. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. gpg: Key box'/Users/hanlyjiang/.gnupg/pubring.kbx'Created Note: use“ gpg --full-generate-key" To get a fully functional key generation dialog box. GnuPG You need to build a user ID to identify your key. Real name: hanlyjiang e-mail address: hanlyjiang@outlook.com You have selected this user ID: "hanlyjiang <hanlyjiang@outlook.com>" Change name( N),Notes( C),Email address( E)Or determine( O)/Quit( Q)? o We need to generate a large number of random bytes. Do something else during prime generation (tap the keyboard) ,It would be a good idea to move the mouse, read and write hard disk, etc; This makes random numbers The generator has a better chance of obtaining enough entropy. We need to generate a large number of random bytes. Do something else during prime generation (tap the keyboard) ,It would be a good idea to move the mouse, read and write hard disk, etc; This makes random numbers The generator has a better chance of obtaining enough entropy. gpg: secret key E8A99FE282B70849 Marked as absolute trust gpg: Revocation certificate has been stored as'/Users/hanlyjiang/.gnupg/openpgp-revocs.d/0B372361CC1A9AE2452D43FDE8A99FE282B70849.rev' The public and private keys have been generated and signed. pub rsa3072 2021-05-24 [SC] [Valid until: 2023-05-24] 0B372361CC1A9AE2452D43FDE8A99FE282B70849 uid hanlyjiang <hanlyjiang@outlook.com> sub rsa3072 2021-05-24 [E] [Valid until: 2023-05-24]
List key s
gpg --list-keys gpg: Checking trust database gpg: Absolute trust key 8 F5EC255E5A0D063 Public key not found for gpg: Absolute trust key 4150 E419D483B9A6 Public key not found for gpg: marginals needed: 3 completes needed: 1 trust model: pgp gpg: Depth: 0 validity: 3 signed: 0 Trust: 0-,0q,0n,0m,0f,3u gpg: The next trust database check will take place in 2023-05-24 conduct /Users/hanlyjiang/.gnupg/pubring.kbx ------------------------------------ pub rsa3072 2021-05-24 [SC] [Valid until: 2023-05-24] 0B372361CC1A9AE2452D43FDE8A99FE282B70849 uid [ absolutely ] hanlyjiang <hanlyjiang@outlook.com> sub rsa3072 2021-05-24 [E] [Valid until: 2023-05-24]
Send key to server
Tips:
If a key server doesn't work, you can change it and do it again. As long as you upload one successfully.
The key needs to be sent to the server so that sonatype can obtain and verify the signature. Upload it through the following command:
Set key information:
KEY_SERVER=hkp://pool.sks-keyservers.net KEY_ID=0B372361CC1A9AE2452D43FDE8A99FE282B70849
Upload:
$ gpg --keyserver $KEY_SERVER --send-keys $KEY_ID gpg: Sending key E8A99FE282B70849 reach hkp://pool.sks-keyservers.net
Check for success:
KEY_SERVER=hkp://pool.sks-keyservers.net gpg --keyserver $KEY_SERVER --recv-keys $KEY_ID
Available key servers:
hkp://keyserver.ubuntu.com hkp://pool.sks-keyservers.net hkp://keys.openpgp.org hkp://keys.gnupg.net hkp://keys.openpgp.org
Export key
Export public key:
gpg -a -o ~/.gnupg/maven-pub.key --export $KEY_ID
Export private key: (password required)
gpg -a -o ~/.gnupg/maven-prv.key --export-secret-keys $KEY_ID
Export gpgkey:
gpg --keyring secring.gpg --export-secret-keys > ~/.gnupg/secring.gpg
gradle configuration
The official instructions for uploading under Gradle are here Gradle - The Central Repository Documentation (sonatype.org) , maven plug-in is used in this article, and there is another maven publish plug-in. We use maven publish plug-in;
gradle.properties
Modify gradle configuration:
# ~/. gradle/gradle.properties write the following: ossrhUsername=hanlyjiang # jira's user name ossrhPassword=#jira's password # Last 8 bits of public key ID: 0B372361CC1A9AE2452D43FDE8A99FE282B70849 signing.keyId=82B70849 signing.password=generate key Password at signing.secretKeyRingFile=/Users/hanlyjiang/.gnupg/secring.gpg
The user name and password can be named at will, as long as you are in build Gradle corresponds to
The configuration of signing needs to keep the same name.
build.gradle:
reference resources:
- [using Maven Publish plugin | Android developers | Android developers (Google. CN)]( https://developer.android.google.cn/studio/build/maven-publish-plugin?hl=zh -Cn#: ~: text = use Maven Publish plug-in Android Gradle plug-in 3.6.0. Gradle plug-in will create a component for each build variant artifact in the application or library module. You can use it to customize the publishing content to be published to Maven code base.)
- Maven Publish Plugin (gradle.org)
We need to configure in the items that need to be uploaded. Note that the information in the pom also needs to be completed. Otherwise, it cannot pass the inspection of sonatype and cannot be published after uploading;
⚠️ Note: some people's upload address may be https://s01.oss.sonatype.org Domain name, such as:
-
https://s01.oss.sonatype.org/content/repositories/snapshots
-
https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
plugins { id 'com.android.library' id 'signing' id 'maven-publish' } def VERSION="1.0.1" android { defaultConfig { minSdkVersion 22 targetSdkVersion 30 versionCode 1 versionName VERSION } } // Because the components are created only during the afterEvaluate phase, you must // configure your publications using the afterEvaluate() lifecycle method. afterEvaluate { publishing { repositories { maven { name "local" // change to point to your repo, e.g. http://my.org/repo url = "$buildDir/repo" } maven { name "sonartype-Staging" // change to point to your repo, e.g. http://my.org/repo url = "https://oss.sonatype.org/service/local/staging/deploy/maven2" // https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ credentials { username = ossrhUsername password = ossrhPassword } } // Define snapshot warehouse maven { name "sonatype-Snapshots" // change to point to your repo, e.g. http://my.org/repo url = "https://oss.sonatype.org/content/repositories/snapshots/" credentials { username = ossrhUsername password = ossrhPassword } } } publications { // Creates a Maven publication called "release". release(MavenPublication) { // Applies the component for the release build variant. from components.release // You can then customize attributes of the publication as shown below. groupId = 'com.github.hanlyjiang' artifactId = 'apf_library' version = VERSION pom { name = 'HJ Android Plugin Framework' description = 'A Android Plugin Framework' url = 'https://github.com/hanlyjiang/apf-library' licenses { license { name='The Apache Software License, Version 2.0' url='http://www.apache.org/licenses/LICENSE-2.0.txt' } } developers { developer { id = 'hanlyjiang' name = 'hanly jiang' email = 'hanlyjiang@outlook.com' } } scm { connection = 'https://github.com/hanlyjiang/apf-library' developerConnection = 'https://github.com/hanlyjiang/apf-library.git' url = 'https://github.com/hanlyjiang/apf-library' } } } // Creates a Maven publication called "debug". debug(MavenPublication) { // Applies the component for the debug build variant. from components.debug groupId = 'com.github.hanlyjiang' artifactId = 'apf_library-debug' version = String.format("%s-SNAPSHOT",VERSION) pom { name = 'HJ Android Plugin Framework' description = 'A Android Plugin Framework' url = 'https://github.com/hanlyjiang/apf-library' licenses { license { name='The Apache Software License, Version 2.0' url='http://www.apache.org/licenses/LICENSE-2.0.txt' } } developers { developer { id = 'hanlyjiang' name = 'hanly jiang' email = 'hanlyjiang@outlook.com' } } scm { connection = 'https://github.com/hanlyjiang/apf-library' developerConnection = 'https://github.com/hanlyjiang/apf-library.git' url = 'https://github.com/hanlyjiang/apf-library' } } } } signing { sign publishing.publications.release , publishing.publications.debug } } }
Note the following in the publishing script configuration:
- groupId: you need to configure the groupId you applied for;
- artifactId: it needs to be modified to the artifactId of your own project;
- The file description in pom needs to be modified to the description of your own project;
- The repositories section is configured with the user name and password corresponding to the remote warehouse. The publishing address needs to be modified according to whether it is a new project. The domain name of the old project is oss.sonatype.org , the domain name of the new project is: s01.oss.sonatype.org
- The signing part needs to configure the corresponding gpg key and account information
Notes on maven warehouse:
- The version number of the library uploaded from the snapshots warehouse needs to end with - SNAPSHOT, otherwise a 400 error may occur;
Execute upload task
Execute the task corresponding to gradle to upload
$ module=apf-library; ./gradlew "$module":publishReleasePublicationToCenterRepository
For the specific generated tasks, you can view the publishing grouped tasks in the Gradle tool window of Android studio; Or view through the following command
$ module=apf-library; ./gradlew "$module":tasks| grep -E "publish|generate" generateMetadataFileForDebugPublication - Generates the Gradle metadata file for publication 'debug'. generateMetadataFileForReleasePublication - Generates the Gradle metadata file for publication 'release'. generatePomFileForDebugPublication - Generates the Maven POM file for publication 'debug'. generatePomFileForReleasePublication - Generates the Maven POM file for publication 'release'. publish - Publishes all publications produced by this project. publishAllPublicationsToCenterRepository - Publishes all Maven publications produced by this project to the center repository. publishAllPublicationsToLocalRepository - Publishes all Maven publications produced by this project to the local repository. publishDebugPublicationToCenterRepository - Publishes Maven publication 'debug' to Maven repository 'center'. publishDebugPublicationToLocalRepository - Publishes Maven publication 'debug' to Maven repository 'local'. publishDebugPublicationToMavenLocal - Publishes Maven publication 'debug' to the local Maven repository. publishReleasePublicationToCenterRepository - Publishes Maven publication 'release' to Maven repository 'center'. publishReleasePublicationToLocalRepository - Publishes Maven publication 'release' to Maven repository 'local'. publishReleasePublicationToMavenLocal - Publishes Maven publication 'release' to the local Maven repository. publishToMavenLocal - Publishes all Maven publications produced by this project to the local Maven cache.
release
- close
- release
reference resources:
First of all, we need to explain what the release here means: after our warehouse is uploaded, it is actually a place where it is stored with a temporary independent and public warehouse, which can only be accessed by ourselves. If we need to provide the warehouse to others, we need to release it; The publishing process can be operated manually on the web page or through the command line;
Publish the operation on the website that needs to take sonatype. The following are the operation steps:
-
Then log in. After logging in, you can see the Build Promotion menu, and then open the Staging Repository, where the uploaded warehouses will be displayed:
-
Select a staging repo, click Close, and confirm
-
The results can be viewed in the Activity
The above error is that the key cannot be found. We upload the key to ubuntu again:
gpg --keyserver $KEY_SERVER --send-keys 0B372361CC1A9AE2452D43FDE8A99FE282B70849 gpg --keyserver $KEY_SERVER --recv-keys 0B372361CC1A9AE2452D43FDE8A99FE282B70849
Then close again
-
Then release
-
After completion, an email notification will be sent, and then the issues of the project created on jira will be updated:
Introduction and use
Introducing a snapshot or staging version
You can confirm whether your library has been uploaded successfully through the following path (please replace the following path with your own):
- Snapshot: https://oss.sonatype.org/content/repositories/snapshots/com/github/hanlyjiang/
- Release: https://repo1.maven.org/maven2/com/github/hanlyjiang/
The versions in the repository of snapshot and staging can be accessed immediately after push, but they can only be accessed by themselves. The user name and password need to be verified.
Add the following repo configuration:
allprojects { repositories { maven { name = "Sonatype-Snapshots" setUrl("https://oss.sonatype.org/content/repositories/snapshots") // setUrl("https://s01.oss.sonatype.org/content/repositories/snapshots") credentials(PasswordCredentials::class.java) { username = property("ossrhUsername").toString() password = property("ossrhPassword").toString() } } maven { name = "Sonatype-Staging" setUrl("https://oss.sonatype.org/service/local/staging/deploy/maven2/") // setUrl("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") credentials(PasswordCredentials::class.java) { username = property("ossrhUsername").toString() password = property("ossrhPassword").toString() } } google() jcenter() mavenCentral() } }
Introduce release version
If you need to release it publicly to yourself or others, you need to release it. There is a certain time period before you can access it after the release operation. The following is the official email received after a release:
Central sync is activated for com.github.hanlyjiang. After you successfully release, your component will be published to Central https://repo1.maven.org/maven2/, typically within 10 minutes, though updates to https://search.maven.org can take up to two hours.
In other words, it takes about 10 minutes to query from the Maven central warehouse and 2 hours to search from the web. You can visit: https://search.maven.org To search, you can access https://repo1.maven.org/maven2/ To confirm whether it is indexed. If it is indexed, it can be introduced into the project;
For use in gradle, you only need to import mavenCenter();
rootProject build.gradle:
allprojects { repositories { google() jcenter() mavenCentral() } }
app build.gradle:
implementation("com.github.hanlyjiang:apf_library:1.0")
Used in kotlin
to configure
import org.gradle.api.publish.maven.MavenPom plugins { id("com.android.library") id("signing") `maven-publish` // kotlin("android") // kotlin("android.extensions") } android { // Omit android configuration } fun getMyPom(): Action<in MavenPom> { return Action<MavenPom> { name.set("Android Common Utils Lib") description.set("Android Common Utils Library For HJ") url.set("https://github.com/hanlyjiang/lib_common_utils") properties.set(mapOf( "myProp" to "value", "prop.with.dots" to "anotherValue" )) licenses { license { name.set("The Apache License, Version 2.0") url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") } } developers { developer { id.set("hanlyjiang") name.set("Hanly Jiang") email.set("hanlyjiang@outlook.com") } } scm { connection.set("scm:git:git://github.com/hanlyjiang/lib_common_utils.git") developerConnection.set("scm:git:ssh://github.com/hanlyjiang/lib_common_utils.git") url.set("https://github.com/hanlyjiang/lib_common_utils") } } } afterEvaluate { publishing { publications { create<MavenPublication>("release") { from(components.getByName("release")) groupId = "com.github.hanlyjiang" artifactId = "android_common_utils" version = android.defaultConfig.versionName pom(getMyPom()) } } repositories { val ossrhCredentials = Action<PasswordCredentials> { username = properties["ossrhUsername"].toString() password = properties["ossrhPassword"].toString() } // The warehouse address of sonar determines whether it is a snapshot or an official warehouse according to the version number of the project maven { name = "Sonartype" val releasesRepoUrl = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2") val snapshotsRepoUrl = uri("https://oss.sonatype.org/content/repositories/snapshots/") url = if (android.defaultConfig.versionName.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl credentials(ossrhCredentials) // Address of snapshot: // https://oss.sonatype.org/content/repositories/snapshots/com/github/hanlyjiang/android_common_utils/ } // Local warehouse of the project maven { name = "ProjectLocal" val releasesRepoUrl = uri(layout.buildDirectory.dir("repos/releases")) val snapshotsRepoUrl = uri(layout.buildDirectory.dir("repos/snapshots")) url = if (android.defaultConfig.versionName.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl } } } // https://stackoverflow.com/questions/54654376/why-is-publishing-function-not-being-found-in-my-custom-gradle-kts-file-within signing { sign(publishing.publications.getByName("release")) } }
Question:
A problem occurred configuring project ':lib_common_utils'. > SoftwareComponentInternal with name 'java' not found.
Maven Publish Plugin (gradle.org)
Upload javadoc and source
Add tasks for javadoc and jarsource
tasks.register("javadoc", Javadoc::class.java) { group = "publishing" dependsOn("assemble") source = android.sourceSets["main"].java.getSourceFiles() classpath += project.files(android.bootClasspath + File.pathSeparator) if (JavaVersion.current().isJava9Compatible) { (options as StandardJavadocDocletOptions).addBooleanOption("html5", true) } android.libraryVariants.forEach { libraryVariant -> classpath += libraryVariant.javaCompileProvider.get().classpath } options.apply { encoding("UTF-8") charset("UTF-8") isFailOnError = false (this as StandardJavadocDocletOptions).apply { addStringOption("Xdoclint:none") links?.add("https://developer.android.google.cn/reference/") links?.add("http://docs.oracle.com/javase/8/docs/api/") } } } tasks.register("jarSource", Jar::class.java) { group = "publishing" from(android.sourceSets["main"].java.srcDirs) archiveClassifier.set("sources") } tasks.register("jarJavadoc", Jar::class.java) { group = "publishing" dependsOn("javadoc") val javadoc: Javadoc = tasks.getByName("javadoc") as Javadoc from(javadoc.destinationDir) archiveClassifier.set("javadoc") }
Used in publish
afterEvaluate { publishing { publications { create<MavenPublication>("release") { from(components.getByName("release")) groupId = "com.github.hanlyjiang" artifactId = "android_common_utils" version = android.defaultConfig.versionName pom(getMyPom()) // Add javadoc artifact(tasks.getByName("jarJavadoc") as Jar) // Add source artifact(tasks.getByName("jarSource") as Jar) } } repositories { val ossrhCredentials = Action<PasswordCredentials> { username = properties["ossrhUsername"].toString() password = properties["ossrhPassword"].toString() } // The warehouse address of sonar determines whether it is a snapshot or an official warehouse according to the version number of the project maven { name = "Sonartype" val releasesRepoUrl = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2") val snapshotsRepoUrl = uri("https://oss.sonatype.org/content/repositories/snapshots/") url = if (android.defaultConfig.versionName.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl credentials(ossrhCredentials) // Address of snapshot: // https://oss.sonatype.org/content/repositories/snapshots/com/github/hanlyjiang/android_common_utils/ } // Local warehouse of the project maven { name = "ProjectLocal" val releasesRepoUrl = uri(layout.buildDirectory.dir("repos/releases")) val snapshotsRepoUrl = uri(layout.buildDirectory.dir("repos/snapshots")) url = if (android.defaultConfig.versionName.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl } } } // https://stackoverflow.com/questions/54654376/why-is-publishing-function-not-being-found-in-my-custom-gradle-kts-file-within signing { sign(publishing.publications.getByName("release")) } }
❌ Error record
400 error
maven publish Received status code 400 from server
Possible reasons: maven warehouse is divided into two, one is snapshot warehouse and the other is Release warehouse. If the package of snapshot Version (version number with snapshot) is uploaded to the address of Release warehouse, an error will be reported.
Reference articles
- Android maven warehouse and jcenter upload (shimo.im)
- [OSSRH-55238] Create a open source project for android - Sonatype JIRA
- Gradle - The Central Repository Documentation (sonatype.org)
- Jcenter stops the service and tells us about our Migration Scheme - InfoQ writing platform
- [using Maven Publish plugin | Android developers | Android developers (Google. CN)]( https://developer.android.google.cn/studio/build/maven-publish-plugin?hl=zh -Cn#: ~: text = use Maven Publish plug-in Android Gradle plug-in 3.6.0. Gradle plug-in will create a component for each build variant artifact in the application or library module. You can use it to customize the publishing content to be published to Maven code base.)