Публикация Java библиотеки в Maven Central

Публикация артефакта библиотеки в Maven Central через Sonatype OSSHR.

· 9 мин.

Вы написали библиотеку на Java и теперь хотите сделать ее общедоступной. Самый простой способ распространения библиотек – это загрузить ее в Maven Central.

Если вы думаете, что можно просто зарегистрироваться в Maven Central и загрузить свой шедевр через форму загрузки, то вы ошибаетесь. Придется пройти небольшой квест.

Помимо Maven Central есть и другие Nexus хранилища – Open Source Software Repository Hosting Service (OSSRH). Крупнейшим является Sonatype OSSRH.

Sonatype OSSRH синхронизируется с Maven Central. Поэтому в этой статье мы разберемся, как зарегистрироваться в Sonatype OSSRH и настроить Maven pom для деплоя в Sonatype OSSRH.

Регистрируем groupId

Зависимость в maven централ выглядит следующим образом.

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.1.1</version>
</dependency>

groupId и artifactId составляют идентификатор вашей библиотеки. groupId - это идентификатор группы проекта. Как правило, он уникален среди организаций и представляет собой доменное имя наоборот. artifactId - это идентификатор проекта в группе проектов.

Чтобы иметь возможность загружать библиотеки, необходимо купить доменное имя, которое будет вашим groupId.

Если вы впервые покупаете там домен, то можете воспользоваться промо-кодом на скидку 5% в REG.RU: E403-2760-B801-2B3B

И так мы купили домен, теперь необходимо подать заявку на регистрацию groupId и подтвердить свои права на домен. Делается это через issues в Jira Sonatype.

Поддомены отдельно регистрировать не надо. После регистрации основного домена, вы сможете использовать и его поддомены.

Тема: Publish rights for domain.name

Описание: I would like to publish my name.domain artifacts. The rights to the domain belong to me.

Group Id: ru.upagge

Project URL: Ссылка на ваш проект в GitLab или GitHub

SCM url: Тоже ссылка на ваш проект, но добавьте в конце .git

Already Synced to Central: No

Через какое-то время в ваше issue пришлют инструкцию, как подтвердить владение доменом. Мне прислали через минуту, скорее всего это автоматическое сообщение.

Пойдем самым быстрым путем - добавим TXT запись для домена. Сделать это можно у регистратора домена.

  • Тип записи: TXT
  • Subdomain: @
  • Text: Ссылка на тикет в Jira

После этого ждем пока DNS записи обновятся. У каждого регистратора это занимает разное время. У REG.RU записи обновились за 15 минут.

После того, как вы убедились, что запись появилась, возвращаемся в тикет. Оставляем там короткий комментарий: done.

Буквально через 10 минут приходит оповещение, что все успешно.

Создание GPG ключа

Генерация GPG ключа

Перед настройкой .pom необходимо создать GPG ключ для подписи релиза.

gpg --full-gen-key

gpg (GnuPG) 2.2.4; Copyright (C) 2017 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.

Выберите тип ключа:
   (1) RSA и RSA (по умолчанию)
   (2) DSA и Elgamal
   (3) DSA (только для подписи)
   (4) RSA (только для подписи)
Ваш выбор? 1
длина ключей RSA может быть от 1024 до 4096.
Какой размер ключа Вам необходим? (3072) 2048
Запрошенный размер ключа - 2048 бит
Выберите срок действия ключа.
         0 = не ограничен
        = срок действия ключа - n дней
      w = срок действия ключа - n недель
      m = срок действия ключа - n месяцев
      y = срок действия ключа - n лет
Срок действия ключа? (0) 0
Срок действия ключа не ограничен
Все верно? (y/N) y

GnuPG должен составить идентификатор пользователя для идентификации ключа.

Ваше полное имя: Struchkov Mark
Адрес электронной почты: example@upagge.ru
Примечание:
Вы выбрали следующий идентификатор пользователя:
    "Struchkov Mark "

Сменить (N)Имя, (C)Примечание, (E)Адрес; (O)Принять/(Q)Выход? O
Необходимо получить много случайных чисел. Желательно, чтобы Вы
в процессе генерации выполняли какие-то другие действия (печать
на клавиатуре, движения мыши, обращения к дискам); это даст генератору
случайных чисел больше возможностей получить достаточное количество энтропии.
Необходимо получить много случайных чисел. Желательно, чтобы Вы
в процессе генерации выполняли какие-то другие действия (печать
на клавиатуре, движения мыши, обращения к дискам); это даст генератору
случайных чисел больше возможностей получить достаточное количество энтропии.
gpg: ключ C7F6E84396D004C4 помечен как абсолютно доверенный
gpg: сертификат отзыва записан в '/home/upagge/.gnupg/openpgp-revocs.d/736AD6032D072F9E452911D5C7F6E84396D004C4.rev'.
открытый и секретный ключи созданы и подписаны.

pub   rsa2048 2021-04-02 [SC]
      736AD6032D072F9E452911D5C7F6E84396D004C4
uid                      Struchkov Mark 
sub   rsa2048 2021-04-02 [E]
  • Тип ключа: RSA и RSA
  • Размер ключа: 2048
  • Срок действия: неограничен

Публикация ключа

Отправьте свой ключ на сервер GPG:

gpg --keyserver keyserver.ubuntu.com --send-keys 736AD6032D072F9E452911D5C7F6E84396D004C4

Не волнуйтесь, эта команда отправит только публичный ключ.

Если через консоль ваш ключ не отправляется, то вы можете загрузить его вручную на сайт http://keyserver.ubuntu.com.

Чтобы получить публичный ключ в нужном формате, используйте команду:

gpg --armor --export 736AD6032D072F9E452911D5C7F6E84396D004C4

Настройка settings.xml

Сначала перейдем в глобальные настройки Maven в папке .m2/settings.xml. Необходимо указать логин и пароль от OSSRH.

<settings>
    <servers>
        <server>
            <id>ossrh</id>
            <username>your-jira-id</username>
            <password>your-jira-pwd</password>
        </server>
    </servers>
</settings>

Создадим новый профиль мавена, чтобы указать данные PGP ключа.

// ... ... ... ... ...

    <profiles>
        <profile>
            <id>ossrh</id>
            <activation>
                <activeByDefault>false</activeByDefault>
            </activation>
            <properties>
                <gpg.keyname>PGP_KEY</gpg.keyname>
                <gpg.passphrase>YOU_PASSWORD</gpg.passphrase>
            </properties>
        </profile>
    </profiles>

// ... ... ... ... ...

После сохранения файла settings.xml перезапустите Idea, чтобы новый профиль появился в списке.

Переходим в pom.xml вашего приложения. Укажем название, описание и ссылку на проект.

// ... ... ... ... ...

<name>uPagge Utils</name>
<description>An example to demonstrate the ability to publish to Maven Central</description>
<url>https://blog.upagge.ru/posts/guide/2021/deploy-to-maven-central/</url>

// ... ... ... ... ...

Эта информация будет использоваться в описании компонента в Maven Central.

Также указываем информацию о системе контроля версий.

// ... ... ... ... ...

<scm>
    <connection>scm:git:https://gitlab.com/uPagge/upagge-utils.git</connection>
    <url>https://gitlab.com/uPagge/upagge-utils</url>
    <developerConnection>scm:git:https://gitlab.com/uPagge/upagge-utils.git</developerConnection>
</scm>

// ... ... ... ... ...

После этого добавляем ссылки на репозитории

// ... ... ... ... ...

<distributionManagement>
    <snapshotRepository>
        <id>ossrh</id>
        <url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
    </snapshotRepository>
</distributionManagement>

// ... ... ... ... ...

С февраля 2021 года все новые проекты начали размещаться на https://s01.oss.sonatype.org/

Также обязательно нужно указать разработчиков:

// ... ... ... ... ...

<developers>
    <developer>
        <id>uPagge</id>
        <name>Struchkov Mark</name>
        <roles>
            <role>Lead</role>
        </roles>
        <url>https://uPagge.ru</url>
    </developer>
</developers>

// ... ... ... ... ...

Плагины для публикации

Для публикации будем использовать плагин nexus-staging-maven-plugin. А чтобы случайно не запустить публикацию, мы создадим отдельный maven профиль – release.

Но сначала в проперти укажем версии необходимых плагинов.

<properties>
	... ... ... ... ...
    
	<plugin.nexus.staging.ver>1.6.8</plugin.nexus.staging.ver>
    <plugin.maven.source.ver>3.2.1</plugin.maven.source.ver>
    <plugin.maven.javadoc.ver>3.3.1</plugin.maven.javadoc.ver>
    <plugin.maven.gpg.ver>3.0.1</plugin.maven.gpg.ver>
</properties>

Добавляем конфигурацию для плагинов:

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.sonatype.plugins</groupId>
                <artifactId>nexus-staging-maven-plugin</artifactId>
                <version>${plugin.nexus.staging.ver}</version>
                <extensions>true</extensions>
                <configuration>
                    <serverId>ossrh</serverId>
                    <nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
                    <autoReleaseAfterClose>true</autoReleaseAfterClose>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>com.thoughtworks.xstream</groupId>
                        <artifactId>xstream</artifactId>
                        <version>${xstream.ver}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <version>${plugin.maven.source.ver}</version>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar-no-fork</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-javadoc-plugin</artifactId>
                <version>${plugin.maven.javadoc.ver}</version>
                <executions>
                    <execution>
                        <id>attach-javadocs</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-gpg-plugin</artifactId>
                <version>${plugin.maven.gpg.ver}</version>
                <executions>
                    <execution>
                        <id>sign-artifacts</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>sign</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <gpgArguments>
                        <gpgArgument>--pinentry-mode</gpgArgument>
                        <gpgArgument>loopback</gpgArgument>
                    </gpgArguments>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>

    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>17</source>
                <target>17</target>
            </configuration>
        </plugin>
    </plugins>
</build>

Плагин nexus-staging-maven-plugin это основной плагин для публикации. Он изменяет стандартное поведение maven deploy. Зависимость com.thoughtworks.xstream используется, чтобы исправить проблему несовместимости плагина с Java 17. Со временем плагин обновят, и эта зависимость будет не нужна.

Опционально вы можете использовать плагины maven-source-plugin и maven-javadoc-plugin используются для генерации исходников и javadoc соответсвенно. Сгенерированные ими jar будут также загружены в maven central.

Плагин maven-gpg-plugin используется для финальной подписи ваших jar файлов. Его использование обязательно.

Теперь в pom.xml добавляем новый профиль release:

<profiles>
    <profile>
        <id>release</id>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.sonatype.plugins</groupId>
                    <artifactId>nexus-staging-maven-plugin</artifactId>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-source-plugin</artifactId>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-gpg-plugin</artifactId>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-javadoc-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </profile>
</profiles>

Все, можно опубликовывать. Делается это просто. Активируйте профиля, которые мы создали ранее и запустите команду мавена:

mvn clean deploy
// ... ... ... ... ...

[INFO]  * Upload of locally staged artifacts finished.
[INFO]  * Closing staging repository with ID "ruupagge-1003".

Waiting for operation to complete...
...

[INFO] Remote staged 1 repositories, finished with success.
[INFO] Remote staging repositories are being released...

Waiting for operation to complete...
...

[INFO] Remote staging repositories released.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  34.159 s
[INFO] Finished at: 2021-04-02T19:49:31+03:00
[INFO] ------------------------------------------------------------------------

Переходим в и воспользовавшись поиском находим нашу библиотеку в Sonatype OSSHR.

Теперь возвращаемся в наш изначальный тикет и пишем, что все готово и библиотека загружена. Они включат автоматическую синхронизацию. Это надо делать только в первый раз, дальше все ваши библиотеки будут синхронизироваться автоматически.

Через 8-24 часа переходим в Maven Central и через поиск находим нашу либу.

🎉🎉🎉 Успех! Теперь любой разработчик сможет воспользоваться вашей библиотекой.