Как стать автором
Обновить

Идеальный мавен. Часть 1

Время на прочтение8 мин
Количество просмотров36K

Знаю, он не идеальный, но по крайней мере я попытаюсь рассказать, как его к этому приблизить.


В одну заметку всё не войдёт, поэтому сначала план:


  1. Постановка задачи — описание той конфигурации проектов с которой мы будем работать, целей и проблем
  2. Как настроить мавен для разработки в рамках нашей задачи
  3. Как настроить CI/CD (билды, релизы, деплоймент)
  4. Нерешенные проблемы

Задача


Итак, начнем с постановки задачи. Предположим у нас есть группа людей (компания, фирма, кружок), которые разрабатывают проекты на Java. При этом у них есть как проекты с открытым кодом (OSS), так и проекты с закрытым кодом. Проекты, назовём их внутренние, разрабатываются независимо друг от друга, но между ними есть зависимости. Что хочется:


  • Централизованное управление зависимостями на внешние библиотеки
  • OSS проекты в центральном мавен репозитории
  • Закрытые проекты в своём мавен репозитории.
  • «Простой» релиз внутренних проектов с обновлением зависимости в зависимых проектах.
  • Максимальная автоматизация всех хотелок.

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


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


Настраиваем мавен


Централизованное управление зависимостями на внешние библиотеки


Для управления сторонними зависимостями у мавена есть специальная секция dependencyManagement и механизм наследования. Казалась бы – вот и ответ, делаем «корпоративный» POM и наследуем от него корневые POM’ы всех наших проектов. Да, так и будет, но вот детали…. Итак, вот он наш будущий «корпоративный» POM:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.team</groupId>
    <artifactId>org.team.pom</artifactId>
    <version>0-SNAPSHOT</version>
    <packaging>pom</packaging>
    <properties>
        <spring.version>4.3.12.RELEASE</spring.version>
        <junit.ver>4.12</junit.ver>     
    </properties>
    <dependencyManagement>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-framework-bom</artifactId>
                <version>${spring.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- Exclude commons-logging from whole Spring -->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-core</artifactId>
                <version>${spring.version}</version>
                <exclusions>
                    <exclusion>
                        <artifactId>commons-logging</artifactId>
                        <groupId>commons-logging</groupId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.ver}</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.7.0</version>
                    <configuration>
                        `<source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.20.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.0.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-site-plugin</artifactId>
                    <version>3.5.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-jar-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-assembly-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

Всё стандартно, но хочу обратить внимание не некоторые вещи:


  • Будуте копировать, уберите апостроф на в конфигурации maven-compiler-plugin. Он там для того, что бы местный редактор не заменял <source> на три апострофа.
  • Это не финальная версия, а минимальная, которую мы будем дописывать в следующих частях.
  • Версии всех компонент вынесены в секцию переменных. Для 2-3 зависимостей не важно, но, когда у вас их 50 и секция dependencyManagement занимает несколько экранов это здорово облегчает обновление версий.
  • Кроме собственно версий в секции dependencyManagement можно переопределить область применения (sсope) зависимости и убрать некоторые транзитивные зависимости. Данной возможностью лучше не злоупотреблять и всегда писать комментарии зачем и почему это так сделано.
  • Так же в нашем POM’е присутствует секция pluginManagement. Ее роль примерно такая-же – централизованное управление плагинами, используемыми для построения проекта. Почему это важно? По умолчанию мавен использует последние доступные версии плагинов. Так что, билд с одних и тех же исходников сейчас и через год может отличатся или просто не пройти. С помощью этой секции мы фиксируем версии плагинов, что по идеи минимизирует возможные расхождения.
  • Еще одна возможность – это определить общие конфигурационные параметры для плагинов – например, версию байткода, которую мы используем во всех наших проектах.
  • И не очень существенный пункт, пока мы не дошли до релизов, это версия нашего POM’а. Я написал её как 0-SNAPSHOT. На самом деле важно, чтобы это был любой SNAPSHOT. Мы её менять в будущем не будем. Почему, расскажу в следующих статьях.

С какими проблемами/непонятками обычно сталкиваются при использовании «корпоративного» POM’а?


  • Непонятно, как делать релизы (не надо)
  • Надо ли включать отдельные проекты как модули (не надо)
  • Надо ли в «корпоративном» POM’е описывать версии внутренних проектов (не надо)
  • Как отслеживать и как часто обновлять версии внешних библиотек
  • Непонимание различия между «корпоративного» и корневым POM отдельного проекта

Все это мы разберём в следующих частях это статьи. Сейчас кратко остановлюсь на двух последних пунктах.


Различие между «корпоративным» POM’ом и корневым POM отдельного проекта


«Корпоративный» POM описывает общие правила для всех ваших проектов в компании – как-то версия Java, зависимости и их версии, общие ограничения на проекты, за которыми может следить мавен, контакты на разработчиков и т.д. В «корпоративном» POM’е не должно быть информации («зависимостей») о конкретных внутренних проектах — их версий, специфичных профайлов и подобных вещей.


Как отслеживать и как часто обновлять версии внешних библиотек


Версии библиотек/плагинов можно легко отслеживать с помощью самого мавена (плагин versions-maven-plugin). Для этого добавим в наш «корпоративный» POM в секцию pluginManagement следующий фрагмент


<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>versions-maven-plugin</artifactId>
    <version>2.5</version>
    <configuration>
        <rulesUri>file://${user.dir}/rules.xml</rulesUri>
        <generateBackupPoms>false</generateBackupPoms>
    </configuration>
</plugin>

А рядом с pom.xml создадим файл rules.xml со следующим содержимым


<ruleset comparisonMethod="maven" xmlns="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0 http://mojo.codehaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd">
    <ignoreVersions>
        <ignoreVersion type="regex">.*-does-not-exist</ignoreVersion>
        <ignoreVersion type="regex">.*[Aa]lpha.*</ignoreVersion>
        <ignoreVersion type="regex">.*(?i)beta.*</ignoreVersion>
        <ignoreVersion type="regex">.*pre.*</ignoreVersion>
        <ignoreVersion type="regex">.*[Dd]raft.*</ignoreVersion>
        <ignoreVersion type="regex">.*-rc.*</ignoreVersion>
        <ignoreVersion type="regex">20041228\.180559</ignoreVersion>
        <ignoreVersion type="regex">.*jbossorg.*</ignoreVersion>
        <ignoreVersion type="regex">.*dev.*</ignoreVersion>
        <ignoreVersion type="regex">\d+\.\d+</ignoreVersion>
        <ignoreVersion type="regex">.*(19|20)\d\d(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01]).*</ignoreVersion>
        <ignoreVersion type="regex">.*jboss.*</ignoreVersion>
        <ignoreVersion type="regex">.*atlassian.*</ignoreVersion>
        <ignoreVersion type="regex">.*xwiki.*</ignoreVersion>
        <ignoreVersion type="regex">.*b\d\d</ignoreVersion>
        <ignoreVersion type="regex">.*_ALPHA</ignoreVersion>
        <ignoreVersion type="regex">.*M\d</ignoreVersion>
        <ignoreVersion type="regex">.*RC\d</ignoreVersion>
        <ignoreVersion type="regex">.*\.jre7</ignoreVersion>
        <ignoreVersion type="regex">.*\.jre6</ignoreVersion>
        <ignoreVersion type="regex">.*CR\d</ignoreVersion>
        <ignoreVersion type="regex">.*M\d*</ignoreVersion>
        <ignoreVersion type="regex">.*pr\d</ignoreVersion>
        <ignoreVersion type="regex">.*android</ignoreVersion>
        <ignoreVersion type="regex">.*m\d*</ignoreVersion>
        <ignoreVersion type="regex">.*p\d*</ignoreVersion>
    </ignoreVersions>
</ruleset>

В принципе это делать не обязательно, и строчку <rulesUri>file://${user.dir}/rules.xml</rulesUri> можно убрать. У меня это файл служит для фильтрации различных «мусорных» версии которые я предпочитаю игнорировать.
После этого достаточно запустить команды versions:display-dependency-updates и versions:display-plugin-updates и получить результаты:


[INFO] The following dependencies in Dependency Management have newer versions:
[INFO]   com.amazonaws:aws-java-sdk-core ................. 1.11.221 -> 1.11.237
[INFO]   com.amazonaws:aws-java-sdk-dynamodb ............. 1.11.221 -> 1.11.237
[INFO]   com.amazonaws:aws-lambda-java-core .................... 1.1.0 -> 1.2.0
[INFO]   com.amazonaws:aws-lambda-java-events .................. 2.0.1 -> 2.0.2
[INFO]   com.google.guava:guava .......................... 23.3-jre -> 23.5-jre
[INFO]   com.google.guava:guava-gwt ...................... 23.3-jre -> 23.5-jre
[INFO]   com.google.inject:guice ................................. 3.0 -> 4.1.0
[INFO]   io.sentry:sentry-logback .............................. 1.6.1 -> 1.6.3

[INFO] The following plugin updates are available:
[INFO]   org.codehaus.mojo:sonar-maven-plugin ......... 3.3.0.603 -> 3.4.0.905
[INFO] 
[INFO] All plugins have a version specified.

Кстати, вторая команда предупредит вас если вы используете какой-то плагин без указания его версии (правда, для этого ее надо запускать на проектных POM‘ах)


Вкратце это всё про «корпоративный» POM. В следующей части я планирую рассказать о типовой структуре проекта и, возможно, о организации деполоя артефактов в центральный и корпоративный репозитории.

Теги:
Хабы:
+11
Комментарии36

Публикации

Изменить настройки темы

Истории

Работа

DevOps инженер
46 вакансий
Java разработчик
356 вакансий

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн