Где-то полгода назад я опубликовал туториал, посвящённый добавлению в проект библиотек, которых нет в репозиториях maven. Речь шла о маленьких проектах, и я порекомендовал ставить тег repository прямо в pom.xml, чтобы можно было собирать проект без необходимости править settings.xml.



В комментариях этот подход критиковали sshikov, igor_suhorukov, jbaruch и многие другие. Там же в комментариях мне дали ссылку на статью Брайана Фокса, в которой чётко и понятно изложено, чем чреваты repository в pom.xml. Статья 2009 года, но не потеряла актуальности до сих пор. Перевода на Хабре я не нашел — поэтому предлагаю вашему вниманию свой.


Есть один вопрос, который мне задают очень часто, и, думаю, пришло время изложить свои мысли по этому поводу в письменном виде, чтобы не приходилось повторять их снова и снова.


Вот он, этот вопрос: Куда класть пути к репозиториям: в помники или в settings.xml?
Короткий ответ: settings.xml.
Длинный ответ: По обстоятельствам.


Тут нужно рассмотреть два сценария: для энтерпрайза (программного обеспечения, которое недоступно извне) и общедоступного программного обеспечения.
Мы начнём с энтерпрайза.


Энтерпрайз


В случае с энтерпрайзом общепринятая практика — ставить какой-нибудь менеджер репозиториев наподобие Nexus. Он работает как прокси для всех внешних репозиториев, а также управляет внутренними.


Когда maven ищет артефакты, он ходит по перечню репозиториев, определённых в помниках и в settings.xml, пока не найдёт то, что ищет. Если в качестве репозитория в помнике указать ваш менеджер репозиториев, maven всё равно откатится к поиску в репозитории Central, когда не найдёт артефакты там.


Один из способов решить эту проблему — переопределить id репозитория Central и вписать туда адрес вашего менеджера репозиториев. Такое решение может подойти, но, чтобы не повторять процедуру для каждого проекта, делать это нужно в каком-то одном корпоративном pom файле. После переопределения Central вы попадаете на "Уловку-22". Для того, чтобы найти помник, в котором указан путь к репозиторию, нужно знать, где репозиторий, в котором этот помник находится. Закончится всё тем, что в любом случае придётся добавить этот репозиторий в settings.xml просто чтобы что-то вообще работало.


С простым переопределением Central есть и другие проблемы. Конкретно, этот способ не позволяет обрабатывать обращения к другим репозиториям, которые могут быть в транзитивных зависимостях проекта и редиректить запросы к ним. Это порождает проблемы: во-первых, замедляется сборка, так как maven обходит все внешниe репозитории в поисках артефакта… Возможно, он будет искать артефакт даже в ваших внутренниx репозиториях, в которых этого артефакта точно нет. Во-вторых, это означает, что что-то может собраться у одного разработчика и не собраться у другого. В-третьих, получается, что как организация вы понятия не имеете, откуда берутся ваши артефакты.


Вам, как организации, в большинстве случаев удобно, когда все разработчики используют одни и те же репозитории при сборке и все запросы проходят через один контролируемый механизм. Этого легче всего достичь с помощью записи mirrorOf * в settings.xml, которая будет редиректить все запросы на ваш менеджер репозиториев. В помнике mirrorOf определить нельзя. Примеры того, как выглядит хорошая настройка, можно посмотреть в этом разделе Nexus Book.


Если учесть все эти моменты, понятно, что определение репозиториев в pom.xml на самом деле не очень помогает и в основном только создаёт дополнительные проблемы.
Выше подразумевается, что ваши артефакты не имеют внешнего потребителя. Если это не так, то кроме первой категории, вы попадаете ещё и во вторую. (Категории не исключают друг-друга.)


Проекты с открытым кодом


Если вы выкладываете своё программное обеспечение в открытый доступ и его будет скачивать и собирать кто-то, кроме вас, тогда нужно учесть и другие моменты. Если все ваши зависимости есть в репозитории Central, тогда больше ничего делать не надо. Однако если ваши артефакты можно положить только в ваш собственный репозиторий (речь идёт, например, о SNAPSHOT версиях ваших родительских помников), или в репозитории третьих лиц, тогда со сборкой вашего кода у разработчиков будут проблемы. В этом и только в этом случае имеет смысл добавлять repository в ваши помники. Но тут есть побочные эффекты, о которых нужно знать.


  • Записи, которые вы внесли, будут навечно отлиты в зарелизенных помниках. Это означает, что если урлы потом поменяются, все, кто использует ваши старые помники, получат битые ссылки, и им придётся выискивать рабочие ссылки вручную.
  • Каждая из этих записей попадёт в их сборки, так как maven нужно будет просматривать их при поиске зависимостей. В данный момент maven не в состоянии понять, что зависимости вашего репозитория находятся в нём самом (а если учесть транзитивность, ситуация выглядит ещё хуже).

Если результат сборки — утилита (как Nexus), а не компонент, который будет использоваться как зависимость, то добавление repository в помник более-менее безопасно. В этом случае, маловероятно, что кто-то ещё будет зависеть от вашего артефакта в своей сборке напрямую, и изложенные выше опасения становятся беспочвенными, так как в новые версии кода скорее всего попадут правильные пути.


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


Итого


В сухом остатке имеем следующее. Если вам нужны воспроизводимые сборки и хороший контроль над происходящим внутри организации, используйте менеджер репозиториев, а также задавайте mirrorOf в settings.xml у всех сотрудников, чтобы указать адрес этого менеджера.


Если вы отдаёте исходники наружу и хотите, чтобы их удобно было собирать, рассмотрите возможность добавления repository в помник, но не подходите к выбору урла легкомысленно, мыслите стратегически, и используйте урл, который всегда будет под вашим контролем. Если урл придётся изменить, убедитесь, что вы всегда сможете отследить ошибки 404 и напишите соответствующие правила mod_rewrite для того, чтобы убедиться, что будущие сборки смогут найти подходящие артефакты.


P.S.


И в заключение небольшая байка из моей собственной жизни. Когда-то давно я поучаствовал в создании концепт пруфа одной интересной идеи. Сделано всё было на java, и деплоить код надо было естественно в среде заказчика, иначе — какой же это пруф :). Сборка была оформлена согласно рекомендациям лучших собаководов, репозитории подключались в settings.xml, артефакты лежали в Nexus — всё как в этой статье.


Но во время деплоя внезапно выяснилось, что на settings.xml, который используется при сборке, нам не дадут даже посмотреть, не говоря уже о редактировании оного. Энтерпрайз, секьюрити, все дела. И пришлось класть repository в помник. Репозитории для разработки и для CI были разные, и для того, чтобы как-то обработать эту ситуацию, сделали 2 профиля: один — с репозиториями для CI, который был дефолтным, а другой — с репозиториями для разработки, который помечался как дефолтный в settings.xml на машине разработчика. Эмоций, помню, мы тогда хлебнули изрядно. Вот так вот бывает.


UPD.
Тут поступило 2 полезных совета от jbaruch, которые хорошо дополняют статью, добавлю их сюда. Он, конечно, топит за Artifactory, но советы дельные, а адвокатов Nexus в темку почему-то не набижало.


  1. Пользуйтесь вычищением репозиториев из pom.xml в Artifactory. Если кто-то в команде таки поленился сделать правильно, и наговнякал, билд на CI упадёт, так как из pom.xml будут удалены репозитории.


  2. По крайней мере на CI сервере (а лучше везде) пользуйтесь Maven Wrapper-ом. В файле maven-wrapper.properties вы можете прописать distributionUrl к произвольному дистрибутиву Мавена и это дает вам возможность скачивать из Artifactory дистибутив, в котором уже будет лежать правильный settings.xml с правильными репозиториями и использовать только его. Никто не запретит, опять же, разработчикам поставить Мавен из коробки и нафигачить любых репозиториев в settings.xml и pom.xml, но если ваш CI пользуется wrapper-ом, который скачивает дистрибутив с правильными репозиториями, ваша сборка будет проходить правильно, и падать, если что не так.

Спойлер для тех, кому интересна работа в ЛАНИТ

Комментарии (7)


  1. sshikov
    05.12.2017 20:34

    Вы знаете, есть и еще один вариант. Вместо менеджера репозиториев может быть сам репозиторий. Если вы поставляете продукт, репозиторий в виде папок с файлами вполне может иметь место. Главное — научиться его автоматически собирать. В таком виде поставляется например Apache Karaf.


    Если вам нужны воспроизводимые сборки

    Ну и так, мелкое замечание — не всегда это тоже хорошо. Скажем, у вас в зависимостях была некая уязвимая для атаки штуковина (широко известен пример с commons collections). Если через много лет кто-то решит собрать ваш проект, далеко не очевидно, что нужно воспроизводить вашу уязвимую для атаки сборку. Может быть лучше упасть. Но я боюсь, что это уже за пределами возможностей отдельно взятого мавена.


    1. poxvuibr Автор
      07.12.2017 13:56

      Если вы поставляете продукт, репозиторий в виде папок с файлами вполне может иметь место.

      Вместе с исходниками поставлять ещё репозиторий, в котором лежат собранные джарки?


      1. sshikov
        07.12.2017 20:07

        Где вы видите слово исходники? Я говорю про собранный продукт, типа условной Вебсферы. В котором обычно куча jar-ов, лежащих иногда там и тут в папках lib. Вот вместо них я бы поставлял репозиторий, по структуре соответствующий maven local.


        1. poxvuibr Автор
          08.12.2017 07:50

          Где вы видите слово исходники?

          Не вижу, подумал про исходники потому что статья в основном про них :).


  1. jbaruch
    06.12.2017 07:49
    +1

    Блог пост на сайте компании, фаундеры которой это безобразие и придумали — это, конечно, прекрасно. Но, как говорится, слишком мало, и слишком поздно. Дебильная система Мавена прямо таки толкает вас прописывать репозитории в pom.xml. Например, как вообще можно поменять settings.xml на облачном сервере CI, где у вас не доступа к инфраструктуре?
    Вот вам 2 полезных добавления к статье (@poxvuibr можешь их дописать как P.S. если хочешь):


    1. Пользуйтесь вычищением репозиториев из pom.xml в Artifactory. Если кто-то в команде таки поленился сделать правильно, и наговнякал, билд на CI упадёт, так как из pom.xml будут удалены репозитории.


    2. По крайней мере на CI сервере (а лучше везде) пользуйтесь Maven Wrapper-ом. В файле maven-wrapper.properties вы можете прописать distributionUrl к произвольному дистрибутиву Мавена и это дает вам возможность скачивать из Artifactory дистибутив, в котором уже будет лежать правильный settings.xml с правильными репозиториями и использовать только его. Никто не запретит, опять же, разработчикам поставить Мавен из коробки и нафигачить любых репозиториев в settings.xml и pom.xml, но если ваш CI пользуется wrapper-ом, который скачивает дистрибутив с правильными репозиториями, ваша сборка будет проходить правильно, и падать, если что не так.


    1. zharko_mi
      06.12.2017 12:36

      Например, как вообще можно поменять settings.xml на облачном сервере CI, где у вас не доступа к инфраструктуре?


      Закомитить settings.xml в проект и указать его через параметр майвена --settings?


  1. foal
    06.12.2017 21:15

    Да, репозитории в помах это еще та чехарда — при большом количестве проектов и длинной истории могут создавать вполне реальные проблемы. Я для себя со временем пришел к такому решению — на рабочий комп ставится sonatype nexus (maven repository), a settings.xml содержит единственную запись:


        <mirrors>
            <mirror>
                <!--This sends everything else to /public -->
                <id>lnexus</id>
                <mirrorOf>*</mirrorOf>
                <url>http://localhost:8081/repository/public</url>
            </mirror>
        </mirrors>

    Сначала о недостатках — репозиторий отбирает часть ресурсов у компа, особенно место на диске. Но это достаточно просто нивелируется железом. Достаточно сказать, что я полгода не замечал фоновый WebSphere Full Profile + Liferay + Application (забыл выключить после смены проекта), чтобы понять, что этот недостаток не так существеннен.


    Теперь о достоинствах:


    • весь роутинг — мои репозитории + OSS репозитории + проектовые репозитории управляются через nexus (Web UI), что гораздо удобнее чем руками в XML
    • мне не надо теперь иметь несколько settings.xml (для работы и для себя). Nexus автоматически отслеживает доступность репозиториев и автоматически их подключает.
    • оптимизация трафика — Nexus "умнее" общается с удалёнными репозиториями
    • локальный репозиторий (не nexus) со временем забивается мусором, но в моём случае это не важно — я легко могу его стереть — всё нужное моментально восстановится из nexus