Статьи

Весна из окопов: возвращение информации о коммите Git в формате JSON

Существуют ситуации, когда мы должны знать точную версию нашего веб-приложения, развернутого на удаленном сервере. Например, клиент может захотеть узнать, развернули ли мы уже исправление ошибки на сервере X.

Конечно, мы можем попытаться найти ответ на этот вопрос, используя «традиционный» метод. Проблема в том, что:

  • Никто не может вспомнить, кто обновил сервер X или когда он был обновлен.
  • Человек, который обновил его, не может вспомнить, какой последний коммит был включен в сборку.

Другими словами, мы облажались. Мы можем попытаться проверить, присутствует ли ошибка на сервере X, но это не очень помогает нам, потому что наше исправление может не работать.

Этот пост описывает, как мы можем решить эту проблему. Давайте начнем с извлечения состояния сборки нашего Git-репозитория.

Если вы используете Spring Boot, вы должны использовать Spring Boot Actuator . Это поможет вам опубликовать информацию о состоянии вашего Git-репозитория .

Если вы не читали следующие сообщения в блоге, вы должны прочитать их, прежде чем продолжить чтение этого сообщения в блоге :

Извлечение времени сборки нашего Git-репозитория

Мы можем извлечь состояние сборки нашего Git-репозитория с помощью плагина Maven Git Commit Id . Давайте выясним, как мы можем настроить плагин Maven Git Commit Id и добавить извлеченную информацию в файл свойств.

Во-первых , нам нужно настроить местоположение нашего каталога ресурсов и убедиться, что заполнители свойств, найденные в наших файлах свойств, будут заменены фактическими значениями свойств. Мы можем сделать это, добавив следующий XML в раздел build нашего файла pom.xml :

1
2
3
4
5
6
7
8
9
<resources>
    <resource>
        <directory>src/main/resources</directory>
        <filtering>true</filtering>
        <includes>
            <include>**/*.properties</include>
        </includes>
    </resource>
</resources>

Во-вторых , нам нужно настроить плагин Maven Git Commit Id. Мы можем сделать это, выполнив следующие действия:

  1. Добавьте плагин Maven Git Commit Id в нашу сборку.
  2. Убедитесь, что цель ревизии плагина Maven Git Commit Id вызывается на фазе инициализации жизненного цикла по умолчанию.
  3. Настройте местоположение каталога .git .

Нам нужно добавить следующий XML в раздел плагинов файла pom.xml :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
<plugin>
    <groupId>pl.project13.maven</groupId>
    <artifactId>git-commit-id-plugin</artifactId>
    <version>2.1.13</version>
    <!--
         Ensure that the revision goal is invoked during the initialize
         phase.
    -->
    <executions>
        <execution>
            <goals>
                <goal>revision</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <!--
            Configure the location of the .git directory.
        -->
        <dotGitDirectory>${project.basedir}/../.git</dotGitDirectory>
    </configuration>
</plugin>

Если вас не устраивает конфигурация по умолчанию для плагина Maven Git Commit Id, вам следует более внимательно посмотреть на его README:

В-третьих , нам нужно создать файл свойств, который содержит информацию, извлеченную из нашего Git-репозитория. Файл application.properties выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
git.tags=${git.tags}
git.branch=${git.branch}
git.dirty=${git.dirty}
git.remote.origin.url=${git.remote.origin.url}
 
git.commit.id=${git.commit.id}
git.commit.id.abbrev=${git.commit.id.abbrev}
git.commit.id.describe=${git.commit.id.describe}
git.commit.id.describe-short=${git.commit.id.describe-short}
git.commit.user.name=${git.commit.user.name}
git.commit.user.email=${git.commit.user.email}
git.commit.message.full=${git.commit.message.full}
git.commit.message.short=${git.commit.message.short}
git.commit.time=${git.commit.time}
 
git.build.user.name=${git.build.user.name}
git.build.user.email=${git.build.user.email}
git.build.time=${git.build.time}

Теперь мы настроили плагин Maven Git Commit Id. Когда мы компилируем наш проект, заполнители свойств, найденные в файле application.properties , заменяются фактическими значениями свойств, которые извлекаются из нашего Git-репозитория.

Файл application.properties, найденный в каталоге target / classes, выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
git.tags=
git.branch=master
git.dirty=true
[email protected]:pkainulainen/spring-from-the-trenches.git
 
git.commit.id=1bdfe9cf22b550a3ebe170f60df165e5c26448f9
git.commit.id.abbrev=1bdfe9c
git.commit.id.describe=1bdfe9c-dirty
git.commit.id.describe-short=1bdfe9c-dirty
git.commit.user.name=Petri Kainulainen
git.commit.message.full=Declare PropertySourcesPlaceholderConfigurer in a static @Bean method
git.commit.message.short=Declare PropertySourcesPlaceholderConfigurer in a static @Bean method
git.commit.time=16.04.2015 @ 23:35:23 EEST
 
git.build.user.name=Petri Kainulainen
git.build.time=18.04.2015 @ 17:07:55 EEST

Если вы не хотите создавать файл свойств, плагин Maven Git Commit Id может сгенерировать его для вас .

Давайте продолжим и выясним, как мы можем внедрить информацию о коммитах Git в bean-компоненты свойств.

Внедрение информации Git Commit в свойствах Beans

Нам нужно создать три свойства bean-классов, которые описаны ниже:

  • Класс BuildProperties содержит информацию о человеке, который начал сборку.
  • Класс CommitProperties содержит информацию о последнем коммите, который включен в сборку.
  • Класс GitProperties содержит несколько «общих» свойств, таких как branch , tags и remoteOriginUrl . Он также содержит ссылки на объекты BuildProperties и CommitProperties .

Свойства bean-компонентов похожи на bean-компоненты конфигурации, которые были описаны в
мой предыдущий пост в блоге . Причина, по которой я использовал разные суффиксы для этих классов, заключается в том, что они не являются частью конфигурации нашего веб-приложения. Они просто содержат информацию, которая записывается в файл журнала и возвращается как JSON.

Кроме того, если вы не знаете, зачем вводить значения свойств в специальные классы bean-компонентов и как вы можете это сделать, вам следует прочитать мой пост в блоге, который отвечает на оба вопроса .

Сначала нам нужно создать класс BuildProperties . Этот класс имеет поля final , userEmail и userName . Фактические значения полей вводятся в эти поля с помощью конструктора. Исходный код класса BuildProperties выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
@Component
public class BuildProperties {
 
    private final String time;
    private final String userEmail;
    private final String userName;
 
    @Autowired
    public BuildProperties(@Value("${git.build.time}") String time,
                           @Value("${git.build.user.email}") String userEmail,
                           @Value("${git.build.user.name}") String userName) {
        this.time = time;
        this.userEmail = userEmail;
        this.userName = userName;
    }
     
    //Getters are omitted for the sake of clarity
}

Во-вторых , нам нужно создать класс CommitProperties . Этот класс имеет окончательные поля description , descriptionShort , fullMessage , id , idAbbrev , shortMessage , time , userEmail и userName . Фактические значения свойств вводятся в эти поля с помощью конструктора. Исходный код класса CommitProperties выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
@Component
public class CommitProperties {
 
    private final String describe;
    private final String describeShort;
    private final String fullMessage;
    private final String id;
    private final String idAbbrev;
    private final String shortMessage;
    private final String time;
    private final String userEmail;
    private final String userName;
 
    @Autowired
    public CommitProperties(@Value("${git.commit.id.describe}") String describe,
                            @Value("${git.commit.id.describe-short}") String describeShort,
                            @Value("${git.commit.message.full}") String fullMessage,
                            @Value("${git.commit.id}") String id,
                            @Value("${git.commit.id.abbrev}") String idAbbrev,
                            @Value("${git.commit.message.short}") String shortMessage,
                            @Value("${git.commit.time}") String time,
                            @Value("${git.commit.user.email}") String userEmail,
                            @Value("${git.commit.user.name}") String userName) {
        this.describe = describe;
        this.describeShort = describeShort;
        this.fullMessage = fullMessage;
        this.id = id;
        this.idAbbrev = idAbbrev;
        this.shortMessage = shortMessage;
        this.time = time;
        this.userEmail = userEmail;
        this.userName = userName;
    }
 
    //Getters are omitted for the sake of clarity
}

В-третьих , нам нужно создать класс GitProperties . Этот класс имеет поля final , build , commit , dirty , remoteOriginUrl и тегов . Фактические значения полей (или объектов) вводятся в эти поля с помощью конструктора. Исходный код класса GitProperties выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
@Component
public class GitProperties {
 
    private String branch;
    private final BuildProperties build;
    private final CommitProperties commit;
    private final boolean dirty;
    private final String remoteOriginUrl;
    private final String tags;
 
    @Autowired
    public GitProperties(@Value("${git.branch}") String branch,
                         BuildProperties build,
                         CommitProperties commit,
                         @Value("${git.dirty}") boolean dirty,
                         @Value("${git.remote.origin.url}") String remoteOriginUrl,
                         @Value("${git.tags}") String tags) {
        this.branch = branch;
        this.build = build;
        this.commit = commit;
        this.dirty = dirty;
        this.remoteOriginUrl = remoteOriginUrl;
        this.tags = tags;
    }
 
    //Getters are omitted for the sake of clarity
}

Давайте продолжим и запишем информацию о коммите Git в файл журнала.

Запись информации Git Commit в файл журнала

Наш следующий шаг — записать информацию о коммите Git в файл журнала. Давайте выясним, как мы можем это сделать.

Сначала мы должны добавить методы toString () в классы BuildProperties , CommitProperties и GitProperties и реализовать эти методы с помощью класса ToStringBuilder .

Исходный код класса BuildProperties выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
@Component
public class BuildProperties {
 
    private final String time;
    private final String userEmail;
    private final String userName;
 
    @Autowired
    public BuildProperties(@Value("${git.build.time}") String time,
                           @Value("${git.build.user.email}") String userEmail,
                           @Value("${git.build.user.name}") String userName) {
        this.time = time;
        this.userEmail = userEmail;
        this.userName = userName;
    }
 
    //Getters are omitted for the sake of clarity
 
    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("time", this.time)
                .append("userEmail", this.userEmail)
                .append("userName", this.userName)
                .toString();
    }
}

Исходный код класса CommitProperties выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
@Component
public class CommitProperties {
 
    private final String describe;
    private final String describeShort;
    private final String fullMessage;
    private final String id;
    private final String idAbbrev;
    private final String shortMessage;
    private final String time;
    private final String userEmail;
    private final String userName;
 
    @Autowired
    public CommitProperties(@Value("${git.commit.id.describe}") String describe,
                            @Value("${git.commit.id.describe-short}") String describeShort,
                            @Value("${git.commit.message.full}") String fullMessage,
                            @Value("${git.commit.id}") String id,
                            @Value("${git.commit.id.abbrev}") String idAbbrev,
                            @Value("${git.commit.message.short}") String shortMessage,
                            @Value("${git.commit.time}") String time,
                            @Value("${git.commit.user.email}") String userEmail,
                            @Value("${git.commit.user.name}") String userName) {
        this.describe = describe;
        this.describeShort = describeShort;
        this.fullMessage = fullMessage;
        this.id = id;
        this.idAbbrev = idAbbrev;
        this.shortMessage = shortMessage;
        this.time = time;
        this.userEmail = userEmail;
        this.userName = userName;
    }
 
    //Getters are omitted for the sake of clarity
 
    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("describe", this.describe)
                .append("describeShort", this.describeShort)
                .append("fullMessage", this.fullMessage)
                .append("id", this.id)
                .append("idAbbrev", this.idAbbrev)
                .append("shortMessage", this.shortMessage)
                .append("time", this.time)
                .append("userEmail", this.userEmail)
                .append("userName", this.userName)
                .toString();
    }
}

Исходный код класса GitProperties выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
@Component
public class GitProperties {
 
    private String branch;
    private final BuildProperties build;
    private final CommitProperties commit;
    private final boolean dirty;
    private final String remoteOriginUrl;
    private final String tags;
 
    @Autowired
    public GitProperties(@Value("${git.branch}") String branch,
                         BuildProperties build,
                         CommitProperties commit,
                         @Value("${git.dirty}") boolean dirty,
                         @Value("${git.remote.origin.url}") String remoteOriginUrl,
                         @Value("${git.tags}") String tags) {
        this.branch = branch;
        this.build = build;
        this.commit = commit;
        this.dirty = dirty;
        this.remoteOriginUrl = remoteOriginUrl;
        this.tags = tags;
    }
 
    //Getters are omitted for the sake of clarity
 
    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("branch", this.branch)
                .append("build", this.build)
                .append("commit", this.commit)
                .append("dirty", this.dirty)
                .append("remoteOriginUrl", this.remoteOriginUrl)
                .append("tags", this.tags)
                .toString();
    }
}

Во-вторых , мы должны записать информацию о коммите Git в файл журнала при запуске нашего приложения. Мы можем сделать это, выполнив следующие действия:

  1. Добавьте статическое конечное поле Logger в класс GitProperties и создайте новый объект Logger с помощью класса LoggerFactory .
  2. Добавьте метод writeGitCommitInformationToLog () в класс GitProperties и аннотируйте его аннотацией @PostConstruct . Это гарантирует, что контейнер Spring вызывает этот метод после того, как он внедрил в него зависимости созданного объекта EJB.
  3. Реализуйте метод writeGitCommitInformationToLog () , записав информацию фиксации Git в файл журнала.

После того как мы внесли эти изменения, исходный код класса GitProperties выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
import javax.annotation.PostConstruct;
 
@Component
public class GitProperties {
 
    private static final Logger LOGGER = LoggerFactory.getLogger(GitProperties.class);
 
    private String branch;
 
    private final BuildProperties build;
 
    private final CommitProperties commit;
 
    private final boolean dirty;
 
    private final String remoteOriginUrl;
 
    private final String tags;
 
    @Autowired
    public GitProperties(@Value("${git.branch}") String branch,
                         BuildProperties build,
                         CommitProperties commit,
                         @Value("${git.dirty}") boolean dirty,
                         @Value("${git.remote.origin.url}") String remoteOriginUrl,
                         @Value("${git.tags}") String tags) {
        this.branch = branch;
        this.build = build;
        this.commit = commit;
        this.dirty = dirty;
        this.remoteOriginUrl = remoteOriginUrl;
        this.tags = tags;
    }
 
    //Getters are omitted for the sake of clarity
 
    @Override
    public String toString() {
        return new ToStringBuilder(this)
                .append("branch", this.branch)
                .append("build", this.build)
                .append("commit", this.commit)
                .append("dirty", this.dirty)
                .append("remoteOriginUrl", this.remoteOriginUrl)
                .append("tags", this.tags)
                .toString();
    }
 
    @PostConstruct
    public void writeGitCommitInformationToLog() {
        LOGGER.info("Application was built by using the Git commit: {}", this);
    }
}

Когда мы запускаем наше веб-приложение, мы должны найти следующую информацию из его файла журнала:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
INFO  - GitProperties - Application was built by using the Git commit: net.petrikainulainen.spring.trenches.config.GitProperties@47044f7d[
    branch=master,
    build=net.petrikainulainen.spring.trenches.config.BuildProperties@7b14c61[
        time=19.04.2015 @ 00:47:37 EEST,
        [email protected],
        userName=Petri Kainulainen
    ],
    commit=net.petrikainulainen.spring.trenches.config.CommitProperties@8fcc534[
        describe=1bdfe9c-dirty,
        describeShort=1bdfe9c-dirty,
        fullMessage=Declare PropertySourcesPlaceholderConfigurer in a static @Bean method,
        id=1bdfe9cf22b550a3ebe170f60df165e5c26448f9,
        idAbbrev=1bdfe9c,
        shortMessage=Declare PropertySourcesPlaceholderConfigurer in a static @Bean method,
        time=16.04.2015 @ 23:35:23 EEST,
        [email protected],
        userName=Petri Kainulainen
    ],
    dirty=true,
    [email protected]:pkainulainen/spring-from-the-trenches.git,
    tags=
]

Эта информация записана в одну строку, но я немного отформатировал ее, потому что хотел сделать ее проще для чтения.

Давайте выясним, как мы можем вернуть информацию о коммите Git в формате JSON.

Возврат информации о коммите Git в формате JSON

Ранее мы создали класс контроллера, который возвращает конфигурацию среды выполнения веб-приложения в виде JSON. Давайте изменим этот класс, чтобы он возвращал информацию о коммите Git как JSON. Мы можем сделать это, выполнив следующие действия:

  1. Добавьте последнее поле GitProperties в класс PropertiesController .
  2. Вставьте bean-компонент GitProperties в созданный bean-компонент контроллера с помощью инжектора конструктора.
  3. Создайте метод контроллера, который обрабатывает запросы GET, отправляемые на URL / version, и реализуйте его, возвращая объект GitProperties

Исходный код PropertiesController выглядит следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
final class PropertiesController {
 
    private final ApplicationProperties applicationProperties;
    private final GitProperties gitProperties;
 
    @Autowired
    PropertiesController(ApplicationProperties applicationProperties,
                         GitProperties gitProperties) {
        this.applicationProperties = applicationProperties;
        this.gitProperties = gitProperties;
    }
 
    @RequestMapping(value = "/config", method = RequestMethod.GET)
    ApplicationProperties getAppConfiguration() {
        return applicationProperties;
    }
 
    @RequestMapping(value = "/version", method = RequestMethod.GET)
    GitProperties getVersion() {
        return gitProperties;
    }
}

Когда мы отправляем запрос GET на URL «/ version», наш метод контроллера возвращает следующий JSON:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
{
    "branch":"master",
    "build":{
        "time":"19.04.2015 @ 00:47:37 EEST",
        "userEmail":"[email protected]",
        "userName":"Petri Kainulainen"
    },
    "commit":{
        "describe":"1bdfe9c-dirty",
        "describeShort":"1bdfe9c-dirty",
        "fullMessage":"Declare PropertySourcesPlaceholderConfigurer in a static @Bean method",
        "id":"1bdfe9cf22b550a3ebe170f60df165e5c26448f9",
        "idAbbrev":"1bdfe9c",
        "shortMessage":"Declare PropertySourcesPlaceholderConfigurer in a static @Bean method",
        "time":"16.04.2015 @ 23:35:23 EEST",
        "userEmail":"[email protected]",
        "userName":"Petri Kainulainen"
    },
    "dirty":true,
    "remoteOriginUrl":"[email protected]:pkainulainen/spring-from-the-trenches.git",
    "tags":""
}

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

Давайте продолжим и подведем итог тому, что мы узнали из этого блога.

Резюме

Этот пост научил нас трем вещам:

  • Мы можем извлечь состояние времени сборки из репозитория Git с помощью плагина Maven Git Commit Id.
  • Мы можем записать информацию фиксации Git в файл журнала, переопределив методы toString () классов bean-компонентов свойств и записав значения свойств этих компонентов в файл журнала после того, как значения свойств были введены в них.
  • Мы можем вернуть информацию о коммите Git как JSON, создав метод контроллера, который возвращает объект bean-объекта «root» ( GitProperties ).