Как сообщество RuneLite построило плагин-маркетплейс на pull request

Открываю статистику репозитория plugin-hub от команды RuneLite. 2812 форков при 394 звёздах. Обычно звёзд заметно больше. Здесь наоборот, и это не ошибка метрик: форкнуть репозиторий обязан каждый, кто выкладывает свой плагин. Форк тут стал частью рабочего процесса, а не способом поставить отметку.
Хаб, который ничего не хранит
Может показаться, будто plugin-hub это склад плагинов. Открываешь, скачиваешь, ставишь. На деле всё иначе. В репозитории лежат только маркеры: небольшие текстовые файлы. Код самих плагинов живёт в чужих репозиториях на GitHub.
Один маркер выглядит так:
repository=https://github.com/dekvall/helmet-check.git
commit=9db374fc205c5aae1f99bd5fd127266076f40ec8
Два поля. Ссылка на репозиторий автора и хеш коммита. Клиент RuneLite читает файлы, скачивает нужные плагины и собирает их у себя. Всё остальное остаётся заботой разработчика.
Зачем такая прослойка? RuneLite держит у себя только те плагины, за которые готов отвечать. Остальное пишет сообщество, а хаб работает витриной. Команда проекта честно предупреждает: плагины идут «как есть», гарантий по ним никаких.
Как это работает на практике
Не уверен, что встречал ещё где-то процесс, где регистрация проекта сведена к одному pull request с двумя строчками. Разберём по шагам, потому что это самая интересная часть.
Сначала автор создаёт репозиторий из шаблона example-plugin или генерирует проект скриптом create_new_plugin.py из plugin-hub-tooling. Пишет код в IntelliJ, тестирует через Gradle, заполняет runelite-plugin.properties:
displayName=Helmet check
author=dekvall
description=Alerts you when you have nothing equipped in your head slot
tags=hint,gear,head
plugins=com.helmetcheck.HelmetCheckPlugin
version=
build=standard
Поле tags помогает пользователям находить плагин по поиску. version можно оставить пустым, тогда подставится хеш коммита.

Дальше форкается сам plugin-hub, в папку plugins добавляется файл-маркер, и открывается PR. CI прогоняет сборку и проверки. Если горит красный крестик, автор читает лог, правит и пушит новый коммит. Дальше ждёт ревью.
Обновление выглядит так же скромно. Меняете хеш коммита в маркере и создаёте pull request со свежей ветки:
git fetch upstream
git checkout -B <your-plugin-name> upstream/master
# обновить commit= в plugins/<your-plugin-name>
git add plugins/<your-plugin-name>
git commit -m "update <your-plugin-name>"
git push -f -u origin <your-plugin-name>
Что проверяют перед публикацией
Ревью тут не формальность. Команда смотрит, не вредит ли плагин пользователям, не нарушает ли правила Jagex по сторонним клиентам и не дублирует ли функции, которые уже отклоняли раньше. Формулировка из README нравится своей прямотой: если мейнтейнерам сложно убедиться, что плагин не против правил, его не смержат.
Отдельная история с зависимостями. Любая библиотека, которая не тянется транзитивно из runelite-client, обязана пройти проверку криптографического хеша во время сборки. Так защищаются от атак на цепочку поставок и добиваются воспроизводимости сборок. Работает через dependency verification в Gradle:
./gradlew --write-verification-metadata sha256
Затем мейнтейнер вручную проверяет добавленные зависимости. Авторы хаба сами признают: процедура заметно удлиняет ревью, поэтому новые зависимости просят добавлять только при крайней необходимости. По-человечески честно.
Две ловушки для новичков
Первая связана с ресурсами. Плагины распространяются в виде .jar, и на диск их никто не распаковывает. Значит getResource() вернёт jar-URL там, где в IDE вы видели file-URL. Поведение окажется не таким, как вы ждёте. Авторы советуют брать getResourceAsStream().
Вторая касается версий клиента. Если плагин внезапно сломался после обновления RuneLite, проверьте, стоит ли в build.gradle параметр runeLiteVersion = 'latest.release'. Если да, а проблема осталась, обновите зависимости Gradle и попробуйте инвалидировать кеши.
Есть и поле build. В режиме standard ваши build.gradle и settings.gradle заменяются при сборке, что ускоряет ревью. Нужны свои зависимости, переключайтесь на gradle и готовьтесь к расширенной проверке.
Кому это нужно
Играете в Old School RuneScape и пользуетесь RuneLite? На официальном сайте есть витрина дополнительных плагинов с поиском и описаниями.
Разрабатываете и хотите что-то сделать для игры? Хаб даёт готовую инфраструктуру: шаблон проекта, распространение, ревью и защиту от подмены зависимостей. Вам остаётся писать код.
Стоит понимать и обратную сторону. Ревью неторопливое, зависимость добавить сложно, правила Jagex ограничивают то, что вообще можно реализовать. Плагинное сообщество держится на добровольцах, и это чувствуется.
Что в итоге
Plugin-hub это редкий пример открытого реестра, построенного на голых pull request'ах без единой базы данных и панели модератора. Индекс плагинов тут просто набор текстовых файлов в git. Решение живёт с 2019 года и пережило больше двух тысяч форков.
Начать проще всего с README репозитория, потом заглянуть в example-plugin, а дальше сгенерировать проект и запустить Gradle-таску run. Первый рабочий плагин реально собрать за вечер. Дальше начнётся самое интересное: ревью.
