Моя цель показать Вам, как с помощью нескольких maven plugin-ов можно сделать вывод в лог Вашей java программы названия ветки и хэш коммита из Git. Это полезно при анализе логов, если у Вас давно не было деплоя и история Вашего инструмента CI затерлась.
Начало
В моем примере я буду использовать: java, maven, spring-core, git. Ссылка на пример. Предположим, что у нас уже имеется проект, написанный на Java и для сборки используется Maven, также он хранится на GitHub.
Maven SCM
Для того чтобы maven мог получить доступ к Вашему репозиторию, нужно указать ему методы и пути подключения. Для этого нужно использовать maven scm:
...
</dependencies>
<scm>
<connection>scm:git:https://github.com/<your_username>/<your_projectname>.git</connection>
<developerConnection>scm:git:https://github.com/<your_username>/<your_projectname>.git</developerConnection>
<tag>HEAD</tag>
<url>https://github.com/<your_username>/<your_projectname>.git</url>
</scm>
<build>
...
Ссылку для доступа формируется на веб-странице Git-а Вашего проекта в разделе Quick setup. Подробнее об использовании scm и вариантах получения доступа к репозиторию можно прочитать здесь и здесь.
Получение информации о текущей ветки git
Далее нам потребуется добавить maven plugin buildnumber-maven-plugin, который будет формировать buildNumber — хэш коммита и scmBranch — название ветки.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<configuration>
<doCheck>false</doCheck>
<doUpdate>false</doUpdate>
</configuration>
</plugin>
Формирование buildNumber и scmBranch происходит на этапе validate, который происходит после clean в Maven.
Параметры:
- doCheck — если true, то проект не скомпилируется пока в нем есть локальные изменения
- doUpdate — позволяет Maven обновить Вашу текущую ревизию
Больше информации об этом.
Работа с properties
После этого вы можете смело добавлять в Ваш application.properties файл следующие переменные: ${buildNumber} и ${scmBranch}
Например я в application.properties добавил следующие параметры:
branch.name=${scmBranch}
commit.hash=${buildNumber}
application.version=${project.version}
Чтобы наш application.properties был найдет, в тэг build нужно добавить:
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
Сборка проекта
Давайте теперь соберем наш проект командой:
mvn clean install -DskipTests
Если вы посмотрите лог, который Вам вывел maven при сборке, то заметите что он выполняет команду git для получения искомых данных:
[INFO] --- buildnumber-maven-plugin:1.4:create (default) @ GitRevision ---
[INFO] Executing: /bin/sh -c cd '/home/<user>/Development/GitRevision' && 'git' 'rev-parse' '--verify' 'HEAD'
[INFO] Working directory: /home/<user>/Development/GitRevision
[INFO] Storing buildNumber: 47c0d1df153b8610392d51d1a7fa0b7b39716e09 at timestamp: 1474375934082
[INFO] Storing buildScmBranch: master
Также Вы можете проверить корректность полученных данных в папке target/classes файл application.properties:
Содержимое:
branch.name=master
commit.hash=47c0d1df153b8610392d51d1a7fa0b7b39716e09
application.version=0.0.1-SNAPSHOT
Добавление в лог
Для получения свойств в любом классе, с которым работает spring используется аннотация @Value.
Пример:
public class LoggerExampleImpl implements LoggerExample {
private static final Logger log = LoggerFactory.getLogger(LoggerExampleImpl.class);
@Value("${branch.name}")
private String branchName;
@Value("${commit.hash}")
private String commitHash;
@Value("${application.version}")
private String version;
public void printLog() {
log.info("Project version: {}, git branch: {}, commit hash: {}",
version, branchName, commitHash);
}
}
Пример класса с main методом:
public class GitRevisionApplication {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
LoggerExample loggerExample = context.getBean(LoggerExampleImpl.class);
loggerExample.printLog();
}
}
Если мы запустим наше программу, то получим:
2016-09-20 17:06:07 INFO [main] ClassPathXmlApplicationContext:581 - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@66cd51c3: startup date [Tue Sep 20 17:06:07 EEST 2016]; root of context hierarchy
2016-09-20 17:06:07 INFO [main] XmlBeanDefinitionReader:317 - Loading XML bean definitions from class path resource [spring.xml]
2016-09-20 17:06:07 INFO [main] PropertyPlaceholderConfigurer:172 - Loading properties file from URL [file:/home/rado/Development/GitRevision/target/classes/application.properties]
2016-09-20 17:06:08 INFO [main] LoggerExampleImpl:25 - Project version: 0.0.1-SNAPSHOT, git branch: master, commit hash: 52c05227fb27271314d80d39b5026193ff310f04
Сокращение хэша
Хэш нашего коммита слишком длинный, чтобы его урезать нужно указать в buildnumber-maven-plugin максимальное количество символов, которые мы хотим вывести:
<configuration>
<shortRevisionLength>5</shortRevisionLength>
</configuration>
Расширить git данными MANIFEST.MF
Есть возможность добавить эти данные в MANIFEST.MF. Для этого нам необходимо подключить еще один maven plugin: maven-jar-plugin.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<archive>
<manifest>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest>
<manifestEntries>
<Implementation-Version>${project.version}-${buildNumber}</Implementation-Version>
<Implementation-Build>${scmBranch}</Implementation-Build>
<Main-Class>com.habrahabr.example.GitRevisionApplication</Main-Class>
</manifestEntries>
</archive>
</configuration>
</plugin>
После сборки нашего проекта мы сможем увидеть в MANIFEST.MF:
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: rado
Build-Jdk: 1.8.0_102
Specification-Title: GitRevision
Specification-Version: 0.0.1-SNAPSHOT
Implementation-Title: GitRevision
Implementation-Vendor-Id: com.habrahabr.example
Implementation-Build: master
Implementation-Version: 0.0.1-SNAPSHOT-47c0d1df153b8610392d51d1a7fa0b7
b39716e09
Main-Class: com.habrahabr.example.GitRevisionApplication
Спасибо. Надеюсь, статья окажется полезна.
Комментарии (12)
mais
22.09.2016 23:06Git flow решает такие проблемы, посмотрите перевод статьи https://habrahabr.ru/post/106912/
osigida
23.09.2016 08:05неплохо бы добавить в секцию build следующий блок:
<resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> </resource> </resources>
иначе application.property останется не процеснут и значения не будут подставленны
Akon32
23.09.2016 14:41Использую в качестве идентификатора билда строку, аналогичную полученной командами
`git describe --tags || git log -n1 --pretty=%h || echo unknown`
Команды вызываются через sbt, формируются ресурсы для программы и системы упаковки.
Идентификатор билда не сформируется, если в PATH нет git, но у меня обычно есть.Heavoc
23.09.2016 16:16git describe --tags
Срабатывает если имеется метка. в ином случае вылет с ошибкой.
Топик стартер почему-то не хотели или не продумали снабжать ветки метками.Akon32
26.09.2016 11:54Срабатывает если имеется метка. в ином случае вылет с ошибкой.
В случае ошибок вызываются следующие команды.
killik
19.01.2017 06:31У необслуживаемых аккумуляторов и не должно быть видимой поверхности электролита — у гелевых он весь содержится, как ни странно, в геле, а у AGM в стекловате, так что и взять пробу на анализ ареометром несколько затруднительно. В Вашем аккумуляторе скорее всего электролит элементарно замерз, а лед ток не проводит. Он бы скорее всего и сам отогрелся, без танцев с бубном :)
GreenStore
Используем немного другой процесс, связанный с выпуском версий.
Ключевые моменты нашего подхода:
1. Номер версии — уникальный идентификатор. Если создается ветка, то это отражается в компоненте версии в виде уникального значения (например, «1.0.0.github»).
2. Источник номера версии находится в файле «version.txt», который может быть легко прочитан скриптом автоматической сборки/дистрибуции и не зависит от системы сборки.
3. Основанием для сборки является выпуск новой версии, при этом в Git-репозиторий добавляется метка (tag) со значением версии. Т.е. триггером сборки является появление метки. Метка является привычным маркером релиза для сервисов хостинга git-репозиториев.
4. Для автоматизации переключения версии написана небольшая кросс-платформенная утилита «verctl» (см. https://github.com/gelsrc/verctl/). По ссылке есть и описание утилиты, если это интересно.
Исходный код упомянутой утилиты является примером описанного подхода.
Исходя из этого, нам не нужно предоставлять информацию о хэше коммита, ветке и т.п., т.к. по нашим правилам версия однозначно соответствует исходному коду. И сам подход, собственно, не привязан конкретно к Git и Maven.
Grief
Это все разумно и понятно, но как быть с дев-билдами? Как отличить билд с фиксом от билда без фикса?
GreenStore
Можно предложить разные варианты.
1. Префикс метки Git: («v» — release, «d» — dev).
2. Суффикс метки/версии (например, «1.0.0» — release, «1.0.0.dev» — dev).
3. По количеству компонент версии (например, «1.0.0» — release, «1.0.0.0.0» — dev).
Если делается ответвление от ранее выпущенной версии (накладывается на нее фикс), то это можно выделить увеличением числа компонент.
Т.е. если базовая версия был «1.2.3», то на ее основе создается ветка «1.2.3.0», «1.2.3.1» и т.д.
Grief
Да нет, это понятно. Вопрос в том, что придется каждый коммит меткой снабжать или творить для каждого коммита ветки, а это лишняя работа в случае веток (которые в 95% случаев сливаются без проблем, а в 5 — оказываются причиной головной боли) или сомнительная польза в случае меток. Ну вот, например, dev-бранча, за неделю на нее пополо 2-3 новых фичи и 5-10 фиксов/мелких улучшений, каждый билд деплоится для тестирования. Практически каждый день — новый билд или два. Велика ли польза от меток на всех соответствующих коммитах, если можно вытащить автоматически хэш коммита и подтянуть в ui список изменений из git-лога автоматически?
GreenStore
Операции увеличения версии и проставления метки производятся автоматически скриптом релиза, который запускается ответственным за релиз. Сборка и деплой делаются полностью автоматически. Поэтому лишней работы здесь не видно.
Польза от меток — в из читаемости и последовательности.
Если есть метка, то по ней можно однозначно вычислить хэш коммита. Поэтому отдельная информация о хэше вроде как и лишняя.