Что можно сделать с GitLab API

Gitlab предоставляет достаточно мощное REST API, с помощью которого можно делать множество рутинных действий. Расскажу о его подключении и нескольких типичных задачах, которые им можно решить, с примерами.

Документация здесь.

Подключаем к Gradle библиотечку-обёртку:

implementation("org.gitlab4j:gitlab4j-api:4.14.27")

Затем нам нужно вызвать апиху

val gitlabApi = GitLabApi("https://gitlab-01.mgmt.oboz.app/", GITLAB_API_KEY)

Здесь используется самый простой способ авторизации — через access token. Получить его можно в гитлабе в user settings → access tokens:

Всё готово! Далее мы получаем апи какого-нибудь ресурса, например:

val groupApi = gitlabApi.groupApi
val repositoryApi = gitLabApi.repositoryApi
val repositoryFileApi = gitlabApi.repositoryFileApi
val mergeRequestApi = gitLabApi.mergeRequestApi
val tagsApi = gitLabApi.tagsApi
val jobApi = gitLabApi.jobApi

и можем работать с ним, вызывая методы, немного заглядывая в документацию.

Задачи, которые можно решат с помощью апи (большинство из них я как раз и реализовал):

  • Релиз менеджмент:
    • создание релизных веток у всех проектов
    • Merge Request’ы в релизные ветки из дева
    • Автоматический мёрдж открытых MergeRequest’ов из дева в релиз
    • Навешевание тегов на релизы
    • Запуск мануальных job’ов и pipeline’ов на всех микросервисах
  • Добавление зависимостей через build.gradle
  • Обновление версий зависимостей через gradle.properties

Последнее мне особенно нравится. Одно из решений для микросервисов — выносить общий код в библиотеки. Да, кодить приходится один раз, но обновлять библиотеки нужно сразу везде. А если сервисов много, то не самое приятное занятие.. Через апишку же мы можем обновить сразу везде, а плюс сразу запустить таски на тестирование и вылить. Если прикрутить интерфейсик, то вообще красота.

Погружение:

Приведу здесь пример добавление зависимости в gradle. Например, развернули и настроили opentracing jaeger. Всё, что остаётся для базового функционала, добавить одну зависимость, но сразу во все проекты. Идеальная задача для gitlab api.
(да, ещё нужно добавить хост jaeger агента, но это можно сделать через конфиг сервер на всех. Ну или аналогично через api)

Код немного грязноват, но вполне нагляден:

fun main() = gitlabApi.groupApi.getGroup(groupPath).projects.forEach { project ->
    println("-------------------------\n${project.pathWithNamespace}:")
    if (project.archived == true) {
        println("WARN: Project archived! Skipping")
        return@forEach
    }

    val file = nullIfThrows { repositoryFileApi.getFile(project, "build.gradle", branch) }
        ?: nullIfThrows { repositoryFileApi.getFile(project, "build.gradle.kts", branch) }
    if (file == null) {
        println("ERROR! Build.gradle not found!")
        return@forEach
    }

    val content = file.decodedContentAsString

    if (content.contains("sleuth")) {
        println("ERROR: sleuth found! Remove sleuth and zipkin and add jaeger manually")
        return@forEach
    }
    if (content.contains("zipkin")) {
        println("ERROR: zipkin found! Remove sleuth and zipkin and add jaeger manually")
        return@forEach
    }
    if (content.contains("opentracing-spring")) {
        println("WARN: opentracing already exists!")
        return@forEach
    }

    val updContent = content.replaceFirst(
        "\ndependencies {\n", 
        "\ndependencies {\n" +
        "    implementation(\"io.opentracing.contrib:opentracing-spring-jaeger-cloud-starter:3.1.1\")\n"
    )
    if (!updContent.contains("opentracing")) {
        println("ERROR: opentracing add failed. Add it manually")
        return@forEach
    }
    file.encodeAndSetContent(updContent)
    repositoryFileApi.updateFile(project, file, branch, commitMsg)

    val committedFile = repositoryFileApi.getFileInfo(project, file.filePath, file.ref)
    println("Committed! ${committedFile.filePath} on ${committedFile.ref} commitId: ${committedFile.commitId}")
}

fun <T> nullIfThrows(block: () -> T?) = try {
    block()
} catch (e: GitLabApiException) {
    if (e.message?.contains("404") == false) {
        e.printStackTrace()
    }
    null
}

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *