Публикация Android-библиотеки в репозиторий Maven с помощью Gradle

от
Android    maven, gradle, library

Разрабатывая библиотеку, рано или поздно приходит момент, когда нужно поделиться ею с другими. Нет, я сейчас говорю не про выкладывание исходников на GitHub, а про публикацию библиотеки в репозиторий Maven Central. Тем более, что выкладывать исходники там не обязательно, допускаются и проекты с закрытым исходным кодом.
Процесс публикации не слишком простой, поэтому без мануала не обойтись. Есть статья на Хабре, но в ней описана публикация Java-библиотеки при помощи Maven, а в моём случае библиотека для Android и используется Gradle, так что процесс значительно отличается.

Шаг 1. Регистрация в SonaType
1.1. Заходим на http://issues.sonatype.org/ и регистрируемся, нажав Sign Up.
1.png
2.png
Имя и фамилию следует указывать на английском, отчество не надо.

1.2. После этого заходим сюда https://issues.sonatype.org/browse/OSSRH и создаём тикет, нажав кнопку Create.
3.png
4.png
Не расписывайте слишком сильно свой проект, краткого описания из одного предложения будет достаточно.
Важным пунктом является groupId, на нём я остановлюсь подробнее.

groupId используется для идентификации разработчика, подобно идентификации в Google Play по имени пакета.
Как правило, айди основывается на домене. Например, annimon.com -> com.annimon, vk.com -> com.vk, limil.spaces.ru -> ru.spaces.limil, github.com/xamgore -> com.github.xamgore. Указывайте такие groupId, домены которых принадлежат вам. Это значит, что org.kalter и com.holdfast скорее всего проверку не пройдут, так как сайты kalter.org и holdfast.com принадлежат невесть кому. В таком случае у вас остаётся вариант использовать groupId по пути к репозиторию на GitHub или BitBucket. Например, https://github.com/KalterFive/Lib_rms -> com.github.kalterfive.
Обращаю внимание, что айди должен быть не с учётом конкретного названия библиотеки, а общий, то есть не com.github.kalterfive.lib_rms, а com.github.kalterfive. Делается это на случай, если вы захотите в будущем опубликовать ещё несколько библиотек.
В прочем, если вы что-то укажете неправильно, команда SonaType вам поможет, либо сама изменит groupId на более общий. Но, всё-таки, во избежание потери времени на рассмотрение заявки, лучше уделить groupId больше внимания.

Остальные поля проблем вызвать не должны - указываем ссылку на репозиторий, где размещён проект.

После создания тикета на почту придёт уведомление.
5.png

1.3. Остаётся только подождать, когда рассмотрят вашу заявку. Как я понял, шансы на быстрый ответ увеличиваются ближе к ночи (очевидно, когда у них в часовом поясе рабочий день).
Сотрудник просмотрит всё детально и оставит комментарий с дальнейшими указаниями и просьбой отписаться, когда всё будет сделано.


Шаг 2. Настройка Gradle для публикации
В Maven репозиторий отправляются скомпилированные классы (или aar-архив с классами и ресурсами, если это Android библиотека), исходники (если требуется) и JavaDocs. Всё это дело надо подписать, а чтобы не делать это вручную, необходимо настроить Gradle.

2.1. В корневой директории создаём файл maven_push.gradle с таким содержимым:
93 строки кода
Либо скачайте
maven_push.gradle

2.2. В корневой gradle.properties добавляем константы:
  1. VERSION_NAME=версия библиотеки (допускаются буквы, например -alpha)
  2. VERSION_CODE=код версии (только число)
  3. GROUP=groupId, о котором я говорил ранее
  4.  
  5. POM_DESCRIPTION=Краткое описание библиотеки
  6. POM_URL=Ссылка на репозиторий проекта
  7. POM_SCM_URL=Ссылка на репозиторий проекта
  8. POM_SCM_CONNECTION=Ссылка на подключение к проекту (смотрите пример)
  9. POM_SCM_DEV_CONNECTION=То же, что и выше
  10. POM_LICENCE_NAME=Название лицензии (Apache 2.0, MIT, и т.д.)
  11. POM_LICENCE_URL=Ссылка на полный текст лицензии
  12. POM_LICENCE_DIST=repo
  13. POM_DEVELOPER_ID=Id разработчика (ник) строчными буквами
  14. POM_DEVELOPER_NAME=Имя и фамилия разработчика на английском
Пример конфигурации.
POM_SCM_CONNECTION и POM_SCM_DEV_CONNECTION можно взять из репозитория на GitHub в панели справа.
6.png

2.3. В корневой build.gradle добавляем строки:
  1. def isReleaseBuild() {
  2.     return version.contains("SNAPSHOT") == false
  3. }
  4.  
  5. allprojects {
  6.     version = VERSION_NAME
  7.     group = GROUP
  8.  
  9.     repositories {
  10.         mavenCentral()
  11.     }
  12. }
  13.  
  14. apply plugin: 'android-reporting'

2.4. В папке с каждым модулем, который будем публиковать (в моём случае это лишь модуль library) создаём файл gradle.properties и заполняем содержимым:
  1. POM_NAME=Название библиотеки
  2. POM_ARTIFACT_ID=id (обычно последняя часть пакета)
  3. POM_PACKAGING=для Android библиотек указываем aar
POM_ARTIFACT_ID в итоге будет подпапкой GROUP_ID, в моём случае получится com.annimon.paperstyle
В рядом лежащий build.gradle добавляем всего одну строчку:
  1. apply from: '../maven_push.gradle'

Пример: library/gradle.properties | library/build.gradle


Шаг 3. Подпись модулей
Теперь необходимо создать ключ для подписи и подписать им каждый выгружаемый файл. Как вы поняли, это тоже будет происходить автоматически, надо только всё как следует настроить.

3.0. Для Windows следует поставить программу GPG, на Linux качать ничего не надо, она и так есть в системе. Проверить это можно набрав в консоли gpg --version.
7.png

3.1. Генерируем собственный ключ. В консоли набираем gpg --gen-key и проходим простую процедуру.
Сперва попросят выбрать тип создаваемого ключа, затем его размер и срок действия, смело выбирайте значения по умолчанию.
Консоль в текстовом виде

Затем попросят ввести ваши данные, а именно имя и фамилию, адрес электронной почты и комментарий. В комментарии можно указать ник.
Консоль в текстовом виде

Затем нужно придумать пароль, после чего будет весьма интересный сбор случайных данных. Вам придётся проявить активность и двигать мышкой, набирать что-либо на клавиатуре, в общем, генерировать системные события для лучшей защиты ключа.
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.

Наконец, после этого появятся заветные строчки
  1. gpg: /home/annimon/.gnupg/trustdb.gpg: trustdb created
  2. gpg: key 910D40ED marked as ultimately trusted
  3. public and secret key created and signed.
  4.  
  5. gpg: checking the trustdb
  6. gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
  7. gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
  8. pub   2048R/910D40ED 2015-03-03
  9.       Key fingerprint = 27A4 910D 4D13 F591 8A62  E14F 0B27 8E47 2BE1 40ED
  10. uid                  Victor Melnik (aNNiMON) <annimon119@gmail.com>
  11. sub   2048R/9EF4ADE0 2015-03-03

Здесь 2048 - размер ключа, R - тип (выбранный на первом шаге), далее идёт id ключа (910D40ED), это значение нам понадобится для следующего пункта.
Скриншот терминала
8.png

3.2. Отправляем публичный ключ на сервер хранения ключей, выполнив команду gpg --keyserver hkp://pool.sks-keyservers.net --send-keys id_ключа
9.png

3.3. Заполняем информацию о созданном ключе в конфигах. В пользовательский gradle.properties (C:\Users\<user>\.gradle\gradle.properties или ~/.gradle/gradle.properties) добавляем константы (если файла нет, нужно создать):
  1. signing.keyId=id ключа (напр. 910D40ED)
  2. signing.password=пароль от ключа, который вы указывали при создании
  3. signing.secretKeyRingFile=путь к файлу secring.gpg (~/.gnupg/secring.gpg или C:\Users\<user>\.gnupg\secring.gpg или вообще C:\Users\<user>\AppData\Roaming\gnupg\secring.gpg)
  4.  
  5. nexusUsername=ваш логин на sonatype.org
  6. nexusPassword=пароль


Шаг 4. Выгрузка на сервер
4.1. Теперь нужно запустить скрипт отправки библиотеки на сервер. Запускаем задачу uploadArchives
10.png
Если всё пройдёт успешно, вы увидите нечто подобное:
  1. BUILD SUCCESSFUL
  2.  
  3. Total time: 23.843 secs
  4. 11:40:51: External task execution finished 'uploadArchives'.
У меня получилось со второго раза, почему-то путь к ключу ~/.gnupg/secring.gpg не подхватился, пришлось в ~/.gradle/gradle.properties написать полный путь home/annimon/.gnupg/secring.gpg

4.2. Заходим сюда https://oss.sonatype.org/#stagingRepositories логинимся и ищем свой репозиторий.
11.png
Напротив него стоит статус open, нам нужно проверить, всё ли выгрузилось. Чтоб уж точно убедиться в работоспособности, на этом этапе даже можно попробовать подключить свою библиотеку через maven, так как она доступна в публичном отслеживающем (staging) репозитории
12.png
Если что-то не так - удалите репозиторий, выделив его в списке и нажав кнопку Drop, затем выгрузите библиотеку по-новой.
Если же всё нормально, тогда выделяем в списке наш репозиторий и нажимаем кнопку Close.
13.png
Сервер проведёт свою проверку и если всё будет нормально, уведомит вас письмом на электронную почту.

После этого, точно так же отправляем репозиторий в релиз, нажав ранее недоступную кнопку Release.
14.png

4.3. Как только всё будет сделано, возвращаемся к истокам, а именно на баг-трекер https://issues.sonatype.org/browse/OSSRH и оставляем комментарий, о котором просил сотрудник.
15.png

Когда всё будет готово к запуску синхронизации, вас оповестят следующим комментарием
16.png


Шаг 5. Празднование
На этом процедура публикации вашей первой библиотеки закончена. Радует одно - эти мучения нужно проходить только один раз. Для последующих библиотек создавать задачи и ожидать подтверждений не придётся.
Спустя некоторое время, библиотека появится в публичном репозитории, её можно будет найти в http://search.maven.org/ и в Gradle, Please, а также использовать в своих проектах, добавив строки:
  1. dependencies {
  2.     compile 'com.annimon:paperstyle:1.1.0'
  3. }


Использованные ресурсы1. Публикация артефакта в Maven Central через Sonatype OSS Repository Hosting Service / Хабрахабр
2. Publish an aar file to Maven Central with Gradle
3. OSSRH Guide
4. How to Generate PGP Signatures with Maven | Sonatype Blog
  • +6
  • views 10121