Pull to refresh

Запускаем инспекции IntelliJ IDEA на Jenkins

Reading time7 min
Views10K

IntelliJ IDEA на сегодня обладает наиболее продвинутым статическим анализатором кода Java, по своим возможностям оставившим далеко позади таких «ветеранов», как Checkstyle и Spotbugs. Её многочисленные «инспекции» проверяют код в различных аспектах, от стиля кодирования до характерных багов.


Однако пока результаты анализа отображаются лишь в локальном интерфейсе IDE разработчика, от них мало пользы для процесса разработки. Статический анализ необходимо выполнять в качестве первого шага конвейера сборки, его результаты должны определять quality gates, а сборка должна фейлиться, если quality gates не пройдены. Известно, что TeamCity CI интегрирован с IDEA. Но даже если вы не используете TeamCity, вы вполне можете попробовать запускать инспекции IDEA в любом другом CI-сервере. Предлагаю посмотреть, как это можно сделать, используя IDEA Community Edition, Jenkins и Warnings NG plugin.


Шаг 1. Запускаем анализ в контейнере и получаем отчёт


Поначалу затея запускать IDE (десктопное приложение!) внутри CI-системы, не имеющей графического интерфейса, может показаться сомнительной и очень хлопотной. К счастью, разработчики IDEA предоставили возможность запускать форматирование кода и инспекции из командной строки. Причём для запуска IDEA в таком режиме не требуется графическая подсистема и эти задачи можно выполнять на серверах с текстовой оболочкой.


Запуск инспекций осуществляется при помощи скрипта bin/inspect.sh из установочной директории IDEA. В качестве параметров требуются:


  • полный путь к проекту (относительные не поддерживаются),
  • путь к .xml-файлу с настройками инспекций (обычно находится внутри проекта в .idea/inspectionProfiles/Project_Default.xml),
  • полный путь к папке, в которую будут сложены .xml-файлы с отчётами о результатах анализа.

Кроме того, ожидается, что


  • в IDE будет настроен путь к Java SDK, иначе анализ работать не будет. Эти настройки содержатся в конфигурационном файле jdk.table.xml в папке глобальной конфигурации IDEA. Сама глобальная конфигурация IDEA по умолчанию лежит в домашней директории пользователя, но это местоположение может быть явно задано в файле idea.properties.
  • анализируемый проект должен быть валидным проектом IDEA, для чего на контроль версий придётся закоммитить некоторые файлы, которые обычно игнорируются, а именно:
    • .idea/inspectionProfiles/Project_Default.xml — настройки анализатора, они явно будут использованы при запуске инспекций в контейнере,
    • .idea/modules.xml — иначе получим ошибку 'This project contains no modules',
    • .idea/misc.xml — иначе получим ошибку 'The JDK is not configured properly for this project',
    • *.iml-файлы — иначе получим ошибку про не настроенный JDK в модуле.

Хотя обычно эти файлы включают в .gitignore, они не содержат никакой специфичной для окружения конкретного разработчика информации — в отличие от, например, файла workspace.xml, где такая информация, как раз, содержится, и потому коммитить его не надо.


Сам собою напрашивается выход запаковать JDK вместе с IDEA Community Edition в контейнер в виде, готовом к «натравливанию» на анализируемые проекты. Выберем подходящий базовый контейнер, и вот какой у нас получится Dockerfile:


Dockerfile
FROM openkbs/ubuntu-bionic-jdk-mvn-py3

ARG INTELLIJ_VERSION="ideaIC-2019.1.1"

ARG INTELLIJ_IDE_TAR=${INTELLIJ_VERSION}.tar.gz

ENV IDEA_PROJECT_DIR="/var/project"

WORKDIR /opt

COPY jdk.table.xml /etc/idea/config/options/

RUN wget https://download-cf.jetbrains.com/idea/${INTELLIJ_IDE_TAR} && \
    tar xzf ${INTELLIJ_IDE_TAR} && \
    tar tzf ${INTELLIJ_IDE_TAR} | head -1 | sed -e 's/\/.*//' | xargs -I{} ln -s {} idea && \
    rm ${INTELLIJ_IDE_TAR} && \
    echo idea.config.path=/etc/idea/config >> idea/bin/idea.properties && \
    chmod -R 777 /etc/idea

CMD idea/bin/inspect.sh ${IDEA_PROJECT_DIR} ${IDEA_PROJECT_DIR}/.idea/inspectionProfiles/Project_Default.xml ${IDEA_PROJECT_DIR}/target/idea_inspections -v2

При помощи опции idea.config.path мы заставили IDEA искать свою глобальную конфигурацию в папке /etc/idea, т. к. домашняя папка пользователя в условиях работы в CI — вещь неопределённая и зачастую вовсе отсутствующая.


Так выглядит копируемый в контейнер файл jdk.table.xml, в котором прописаны пути к OpenJDK, установленной внутри контейнера (за основу может быть взят аналогичный файл из вашей собственной директории с настройками IDEA):


jdk.table.xml
<application>
 <component name="ProjectJdkTable">
   <jdk version="2">
     <name value="1.8" />
     <type value="JavaSDK" />
     <version value="1.8" />
     <homePath value="/usr/java" />
     <roots>
       <annotationsPath>
         <root type="composite">
           <root url="jar://$APPLICATION_HOME_DIR$/lib/jdkAnnotations.jar!/" type="simple" />
         </root>
       </annotationsPath>
       <classPath>
         <root type="composite">
           <root url="jar:///usr/java/jre/lib/charsets.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/deploy.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/access-bridge-64.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/cldrdata.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/dnsns.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/jaccess.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/jfxrt.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/localedata.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/nashorn.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/sunec.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/sunjce_provider.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/sunmscapi.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/sunpkcs11.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/ext/zipfs.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/javaws.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/jce.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/jfr.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/jfxswt.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/jsse.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/management-agent.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/plugin.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/resources.jar!/" type="simple" />
           <root url="jar:///usr/java/jre/lib/rt.jar!/" type="simple" />
         </root>
       </classPath>
     </roots>
     <additional />
   </jdk>
 </component>
</application>

Образ в готовом виде доступен на Docker Hub.


Перед тем, как двинуться дальше, проверим запуск анализатора IDEA в контейнере:


docker run --rm -v <путь/к/вашему/проекту>:/var/project inponomarev/intellij-idea-analyzer

Анализ должен успешно отработать, а в подпапке target/idea_inspections должны появиться многочисленные .xml-файлы с отчётами анализатора.


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


Шаг 2. Отображаем и анализируем отчёт


Получить отчёт в виде .xml-файлов — полдела, теперь его нужно сделать человекочитаемым. А также его результаты должны быть использованы в quality gates — логике определения того, проходит или не проходит принимаемое изменение по критериям качества.


В этом нам поможет Jenkins Warnings NG Plugin, релиз которого был сделан в январе 2019 года. С его появлением многие отдельные плагины для работы с результатами статического анализа в Jenkins (CheckStyle, FindBugs, PMD и т. п.) теперь помечены как устаревшие (obsolete).


Плагин состоит из двух частей:


  • многочисленных сборщиков сообщений анализаторов (полный список включает в себя все известные науке анализаторы от AcuCobol до ZPT Lint),
  • единого для всех них просмотрщика отчётов.

В перечне того, что умеет анализировать Warnings NG, находятся в том числе предупреждения компилятора Java и предупреждения из логов выполнения Maven: хотя они постоянно на виду, их редко когда целенаправленно анализируют. Отчёты IntelliJ IDEA также входят в перечень распознаваемых форматов.


Т. к. плагин новый, он изначально хорошо взаимодействует Jenkins Pipeline. Шаг сборки с его участием будет выглядеть следующим образом (мы просто говорим плагину, какой формат отчёта распознаём и какие файлы следует просканировать):


stage ('Static analysis'){
    sh 'rm -rf target/idea_inspections'
    docker.image('inponomarev/intellij-idea-analyzer').inside {
       sh '/opt/idea/bin/inspect.sh $WORKSPACE $WORKSPACE/.idea/inspectionProfiles/Project_Default.xml $WORKSPACE/target/idea_inspections -v2'
    }
    recordIssues(
       tools: [ideaInspection(pattern: 'target/idea_inspections/*.xml')]
    )
}

Интерфейс отчёта выглядит так:



Удобно, что этот интерфейс является универсальным для всех распознаваемых анализаторов. Он содержит интерактивную диаграмму распределения находок по категориям и график динамики изменения количества находок. В гриде внизу страницы можно выполнять быстрый поиск. Единственное, что для испекций IDEA не заработало корректно — возможность браузить код непосредственно в Jenkins (хотя для других отчётов, например Checkstyle, этот плагин умеет это делать красиво). Похоже, это баг парсера отчётов IDEA, который предстоит починить.


Среди возможностей Warnings NG — возможность агрегировать в одном отчёте находки из разных источников и программировать Quality Gates, в том числе — «храповик» по референтной сборке. Некоторая документация по программированию Quality Gates доступна здесь — впрочем, она не полная, и приходится смотреть в исходники. С другой стороны, для полного контроля над происходящим «храповик» можно реализовать и самостоятельно (см. мой предыдущий пост на эту тему).


Заключение


Перед тем, как начать готовить данный материал, я решил поискать: а не писал ли уже кто-нибудь на эту тему на Хабре? Я нашёл лишь интервью 2017 года с lany, где он говорит:


Насколько мне известно, интеграции с Jenkins или maven-плагина нету [...] В принципе, любой энтузиаст мог бы подружить IDEA Community Edition и Jenkins, многие бы от этого только выиграли.

Что же: спустя два года у нас есть Warnings NG Plugin, и наконец-то эта дружба осуществилась!

Tags:
Hubs:
Total votes 34: ↑33 and ↓1+32
Comments7

Articles