Недавно у меня появилось желание попробовать создать свою собственную Android библиотеку и выложить ее на Maven Central репозиторий.
Это оказалось не так просто, как я думал. В интернете полно непонятных статей на эту тему, в которых легко запутаться.
Я решил поделиться процессом публикации моей библиотеки Awesome-Buttons.
Ну что ж, приступим.
Регистрация на Sonatype JIRA и создание Issue
Перейдите на сайт Sonatype JIRA и создайте себе аккаунт.
Теперь войдите в аккаунт и создайте новый Issue
:
Заполните всю необходимую информацию.
Обратите внимание на Group Id .
Обычно в качестве его указывается доменное имя в обратном порядке.
Если у вас есть свой сайт или сайт библиотеки, то вы можете использовать его доменное имя (например: ru.freeit256
), но для этого нужно будет дополнительно прописать запись в вашем регистраторе домена (после создания Issue
вы увидите информацию как это сделать).
Я использовал другой подход: указал свой Github
в качестве Group Id
Также для того, чтобы использовать Github в качестве Group Id
вам нужно создать пустой репозиторий с именем OSSRH-74088
Не забудьте отправить ваше Issue
на проверку и дождаться статуса CLOSED
Создаем Android библиотеку в Android Studio
Сначала создайте любое пустое приложение с app
модулем.
Затем вы можете либо добавить модуль Android библиотеки, либо изменить текущий модуль на библиотечный. Читайте об этом на официальном сайте
Генерации GPG ключа
Перед публикацией необходимо сгенерировать пару GPG
ключей.
Для генерации ключей выполните следующую команду:
gpg --full-gen-key
Основные параметры при создании ключей:
формат шифрования: RSA and RSA
размер: 4096 бит
срок годности ключа можно указать 0 (никогда не истечет)
имя, email и комментарий укажите свои
Чтобы посмотреть все сгенерированные ключи выполните команду:
gpg --list-keys
Обратите внимание, последние 8 символов это ID
ключа:
Для подписи библиотеки мы будем использовать приватный ключ, а чтобы пользователи смогли удостовериться, что библиотека принадлежит нам, мы должны выложить публичный ключ на общедоступный сервер, например keyserver.ubuntu.com:
// укажите свой ID ключа
gpg --keyserver keyserver.ubuntu.com --send-keys 0F11924A
Чтобы получить приватный ключ для подписи нужно выполнить:
// вы указываете свой email, который юзали, когда создавали ключ
gpg --armor --export-secret-keys dmitry.tehnicov@gmail.com | awk 'NR == 1 { print "GPG_SIGNING_KEY=" } 1' ORS='\\n'
Вуаля! Позже он нам понадобится.
Настройка библиотеки для публикации:
Добавляем плагин io.github.gradle-nexus
в корневой файл build.gradle
:
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:7.0.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20"
}
}
plugins {
// плагин для автоматизации публикации нашей либы
id "io.github.gradle-nexus.publish-plugin" version "1.1.0"
}
// позже мы добавим скрипты для публикации
apply from: "${rootDir}/scripts/publish-root.gradle"
task clean(type: Delete) {
delete rootProject.buildDir
}
Далее нам нужно создать два скрипта для модуля и корня нашей библиотеки.
Создадим в корне проекта папку scripts
, и добавим новый скрипт publish-module.gradle
:
apply plugin: 'maven-publish'
apply plugin: 'signing'
task androidSourcesJar(type: Jar) {
archiveClassifier.set('sources')
if (project.plugins.findPlugin("com.android.library")) {
// For Android libraries
from android.sourceSets.main.java.srcDirs
from android.sourceSets.main.kotlin.srcDirs
} else {
// For pure Kotlin libraries, in case you have them
from sourceSets.main.java.srcDirs
from sourceSets.main.kotlin.srcDirs
}
}
artifacts {
archives androidSourcesJar
}
afterEvaluate {
publishing {
publications {
release(MavenPublication) {
// мы позже добавимм эти константы в другом файле
groupId PUBLISH_GROUP_ID
artifactId PUBLISH_ARTIFACT_ID
version PUBLISH_VERSION
// Two artifacts, the `aar` (or `jar`) and the sources
if (project.plugins.findPlugin("com.android.library")) {
from components.release
} else {
from components.java
}
artifact androidSourcesJar
// Mostly self-explanatory metadata
pom {
name = PUBLISH_ARTIFACT_ID
// описание нашей библиотеки
description = 'Different animated buttons for android applications'
// ссылка на сайт либы или на Github
url = 'https://github.com/KiberneticWorm/Awesome-Buttons'
licenses {
license {
name = 'Apache License 2.0'
// также вам нужно создать лицензию для своей либы
// я скопировал ее из Open Source проекта
url = 'https://github.com/KiberneticWorm/Awesome-Buttons/blob/master/LICENSE.txt'
}
}
developers {
developer {
// далее указывает id, имя и почту разработчика
id = 'Twilly'
name = 'Dmitry Tsyvtsyn'
email = 'dmitry.tehnicov@gmail.com'
}
}
scm {
// информация о библиотеки на Github'е
connection = 'scm:github.com/KiberneticWorm/Awesome-Buttons.git'
developerConnection = 'scm:git:ssh://github.com/KiberneticWorm/Awesome-Buttons.git'
url = 'https://github.com/KiberneticWorm/Awesome-Buttons/tree/main'
}
}
}
}
}
}
signing {
useInMemoryPgpKeys(
rootProject.ext["signing.keyId"],
rootProject.ext["signing.key"],
rootProject.ext["signing.password"],
)
sign publishing.publications
}
Также нам нужно добавить скрипт publish-root.gradle
:
// мы укажем все эти переменные в файле local.properties
ext["signing.keyId"] = ''
ext["signing.password"] = ''
ext["signing.key"] = ''
ext["ossrhUsername"] = ''
ext["ossrhPassword"] = ''
ext["sonatypeStagingProfileId"] = ''
File secretPropsFile = project.rootProject.file('local.properties')
if (secretPropsFile.exists()) {
// Read local.properties file first if it exists
Properties p = new Properties()
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')
ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
ext["signing.key"] = System.getenv('SIGNING_KEY')
}
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/"))
}
}
}
Теперь нам нужно добавить maven-publish
плагин и некоторые константы в начало build.gradle
файла нашего модуля:
plugins {
id 'com.android.library'
id 'kotlin-android'
id 'maven-publish'
}
// чтобы пользователь смог добавить нашу библиотеку
// мы должны указать group id, artifact id и ее версию
ext {
// group id
PUBLISH_GROUP_ID = 'io.github.kiberneticworm'
// текущая версия библиотеки
PUBLISH_VERSION = '1.0.1'
// artifact id библиотеки
PUBLISH_ARTIFACT_ID = 'awesome-buttons'
}
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"
// ...
И самый важный момент, файл local.properties
:
// добавьте следующие константы
// ID ключа, последние 8 символов
signing.keyId=
// пароль, который вы задали при создании ключа
signing.password=
// имя пользователя JIRA Sonatype
ossrhUsername=Twilly
// пароль
ossrhPassword=
// вернемся чуть позже
sonatypeStagingProfileId=
// приватный ключ, который вы получили ранее
signing.key=
Публикация
После основных настроек, зайдите на Nexus Repository Manager и войдите под учетными данными JIRA Sonatype.
Далее переходим во вкладку Staging Profiles и выбираем необходимый профиль.
Копируем sonatypeStagingProfileId
из адреса сайта и указываем его в local.properties
файле:
Переходим к публикации.
// укажите свой модуль библиотеки
./gradlew awesome-buttons:publishReleasePublicationToSonatypeRepository
Далее у вас есть два варианта зарелизить либу: вручную через графический интерфейс или с помощью gradle
задачи.
Первый
Зайдите на сайт Nexus Repository Manager , перейдите во вкладку Staging Repositories и выберите необходимый репозиторий.
Чтобы выпустить релиз библиотеки нужно воспользоваться двумя командами close
и release
.
Для отмены релиза юзайте drop
Второй
Выполните команду, которая сама закроет и зарелизит вашу либу:
./gradlew closeAndReleaseSonatypeStagingRepository
Заключение
Вы уже убедились, что процесс публикации Andorid
либы весьма затратный и рутинный.
Я постарался изложить только основные моменты. Возможно у вас возникнут какие-либо ошибки, которые вам придется самим решать ну и конечно же гуглить!
Такова жизнь прогера: постоянно гуглить. :)
Надеюсь, что статья оказалась вам полезной.
Желаю всем хорошего кода и побольше успешных релизов! :)
Комментарии (8)
aleksandy
27.10.2021 13:21Светить приватный ключ в интернете, пусть и на скриншоте - так себе затея. :)
Что-то как-то заморочено всё. Когда я такое проделывал, достаточно было после закрытия тикета и прописывания ключей вызвать
mvn deploy
.
Reformat
...а если ваша библиотека open-source, то можно просто использовать JitPack.
Публикуете GitHub-релиз с исходниками и она сразу подцепляется из любого проекта.
Шаманизма с ключами не требуется. Например так.
Firsto
Поддерживаю, такой способ гораздо удобнее.
Kerrigan
После чего jitpack отваливается в самый ответственный момент(вы же наверняка на бесплатной версии сидите). Или отдает 403 при попытке выкачать ваш AAR. Или удаляет билд и нужно сходить к ним на сайт и протыкать кнопку, чтобы заново собрало. Для своих проектов это еще как-то сойдет, но для долгих - я бы не советовал.
Reformat
И сколько раз у вас отваливался JitPack?
Kerrigan
Раза 4 было, прямо сейчас одна из зависимостей 403
Reformat
У меня совсем другой опыт, за все время пользования полет нормальный... Некоторые достаточно востребованные проекты тоже хостятся на JitPack, kotlin-telegram например, или KWeb.
osipxd
Для старта покатит. Но в Maven Central публикация получается качественней как раз из-за того что Maven Central требует, чтобы были:
- исходники
- JavaDoc
- подпись
- данные о библиотеке и о разработчике в pom.xml
Хотя сам процесс публикации не удобный, это да.