У вас есть репозиторий GitHub и сервер Jenkins. У Jenkins есть задания для запуска модульных тестов, расчета покрытия кода и выполнения статического анализа.

Сегодня каждый раз, когда кто-то открывает PR в вашем репозитории, вы просите его 1) запустить эти задания, 2) сделать скриншоты результатов и 3) вставить в описание PR в качестве доказательства качества. .

Это слишком долгое ожидание и слишком много нажатий клавиш. Людям нужна автоматизация: когда они создают PR (или отправляют коммит), эти задания должны начать создаваться. Когда сборки будут завершены, результаты должны быть удобно видны в разделе «Проверки» внизу PR, например:

Способы публикации чеков в PR: статусы, проверки и действия

Вы подумали о своих возможностях. В этом маленьком окошке можно отобразить 3 типа записей. В порядке возрастания сложности настройки —

Статусы, или, как я их называю, Проверки фиксации. Они связаны с коммитами, а не с PR. Это означает, что вы можете запускать их даже без создания PR. Например, вот несколько проверок фиксации коммита, который я отправил непосредственно в ветку master:

Проверки или, точнее, PR-проверки. Они запускаются при фиксации, но хранятся вместе с PR. С их помощью вы можете писать длинные эссе сообщений на вкладке Проверки PR, но они требуют аутентификации в качестве приложения GitHub, а не пользователя, поэтому их сложнее настроить. SonarCloud, анализатор кода, использует PR-проверки через свое одноименное приложение GitHub. Если присмотреться, то их отчет представляет собой просто Markdown:

Действия GitHub (GA). GA — это система CI, отличная от Jenkins. Поскольку эта статья посвящена только Дженкинсу, GA упоминается здесь только для полноты картины.

Когда результат прост: используйте статусы фиксации

Задания Jenkins, которые вы хотите запустить, выполняются всего пару секунд. Каждый выдает только простой результат: пройден или нет. Вам не нужны сложные отчеты, поэтому статусы коммитов кажутся подходящим выбором.

Вы установили плагин GitHub на свой сервер Jenkins и следовали этому руководству, чтобы настроить его. Теперь вы можете создавать простые однострочники в качестве проверок, например:

Он удовлетворил ваши насущные потребности, но по-прежнему не дотягивает по ряду причин:

  • Плагин публикует PR только трижды: при постановке в очередь, при запуске и после завершения. На самом деле ваша работа состоит из много трудоемких этапов (например, создание манифеста, развертывание в промежуточной среде и выполнение тестов), часто упакованных в один сценарий, и вы хотите получать уведомления на каждом этапе. этап.
  • В сообщениях отображается изображение вашего профиля GitHub в качестве значка, поскольку Дженкинс использует ваш PAT! Вы хотите уклоняться от внимания, возможно, потому, что не хотите, чтобы ваши менее разбирающиеся в GitHub сотрудники приняли вас за безжалостного робота, критикующего (и блокирующего) всеобщие пиар-сообщения.

Вот тогда начинаешь смотреть дальше.

Когда отчет длинный: используйте PR-проверки

В конце вашего задания создается длинный отчет, но в разделе Проверки можно разместить только пару слов. Вы визуализировали отчеты как HTML и прикрепляли их к самим Jenkins Builds (возможно, с помощью красивого шаблона Jinja2 и плагина HTML Publisher). Статусы коммитов, которые ваша работа записывает в PR, содержат ссылки на сами сборки Jenkins, так что от HTML-отчета отделяет всего два клика — неплохо.

Чтобы прежние HTML-отчеты не переполняли диск, вы настроили задание так, чтобы оно сохраняло только 20 самых последних сборок. Затем команда выросла, и люди начали жаловаться, что их отчеты удаляются еще до того, как они успевают их просмотреть. Теперь у вас возникла проблема с масштабируемостью.

Давайте уберем эти отчеты подальше от Дженкинса, а что может быть лучше, чем сами PR?

Следуя этому руководству от CloudBees, вы создали приложение GitHub, установили его в свой репозиторий, сгенерировали закрытый ключ SSH для приложения и загрузили его в хранилище учетных данных Jenkins с GitHub App в качестве Kind. (Для половины пути с GitHub в этом руководстве есть скриншоты получше.)

Теперь ваша работа в Jenkins разрешена отправлять PR-чеки; как нам сказать ему что отправлять? Именно тогда в игру вступают эти 3 плагина:

  • Плагин Checks API: позволяет Дженкинсу взаимодействовать с Checks API на различных платформах хостинга git, включая GitHub, GitLab и BitBucket. Это базовый плагин, который предназначен для расширения другими плагинами, однако он не используется напрямую в заданиях Jenkins.
  • Плагин GitHub Checks API: позволяет Дженкинсу напрямую общаться с GitHub. Он расширяет вышеупомянутый Плагин Checks API.
  • Плагин JUnit: публикует отчеты о тестировании XML в стиле JUnit в различных местах назначения. Он публикует текущую сборку и — когда доступен Плагин Checks API — публикует на GitHub/GitLab/… как PR-проверки.

JUnit, ты сказал?

Вопреки тому, что следует из названия, XML-отчеты JUnit предназначены не только для JUnit; это де-факто стандарт представления результатов тестов для различных платформ тестирования (и даже для языков, помимо Java). Вот несколько примеров:

В Python pytest имеет встроенную поддержку для создания отчетов в формате XML JUnit:

pytest --junitxml=path

В Go пакет go-junit-report может переводить результаты тестов Go в формат JUnit:

go test -v 2>&1 ./... | go-junit-report -set-exit-code > report.xml

В C++ среда тестирования Google, GoogleTest, может выдавать результаты теста в виде XML, который соответствует формату JUnit:

export GTEST_OUTPUT="xml:/path/to/junit.xml"
# Run your normal test command here.

Формат JUnit описан в этой статье Общий формат XML JUnit и примеры вместе с некоторыми примерами. См. также первые 4 строки статьи Использование XML-отчетов JUnit от Xray.

Таким образом, если вы можете, задание Jenkins выдает результаты в XML-схеме JUnit, чтобы вы могли отправлять результаты в качестве PR-проверки через Плагин JUnit.

Обновление проверки на каждом этапе

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

Случай 1. Если вы можете разделить задание на отдельные шаги, вы можете обновить PR-проверки, напрямую взаимодействуя с Плагином API Checks. Под напрямую я имею в виду, что вам не нужно использовать потребительские плагины, такие как Плагин JUnit. В синтаксисе Jenkinsfile:

publishChecks
	name: 'my-cool-check',
	title: 'Integration Test',
	summary: 'Manifest is built. Deploying to a staging pool now.',
	status: 'IN_PROGRESS',
    text: 'Nothing much to see here.',
    detailsURL: 'https://link.to.your/jenkins/build/',
    actions: []

Это полезно, когда каждое действие, выполняемое вашим заданием, можно записать как отдельный вызов исполняемых файлов. Например, у меня может быть задание, которое собирает, тестирует и публикует мой код Rust, вызывая cargo build, cargo test и cargo publish соответственно. Поскольку каждый шаг представляет собой отдельную команду оболочки, я могу легко записать их так:

// Under pipeline > stages > stage('...') > steps.
script {
	publishChecks name: 'my-cool-check', status: 'IN_PROGRESS',
		title: 'Build, Test, and Publish',
		summary: 'Building...'
	sh 'cargo build'
	publishChecks name: 'my-cool-check', status: 'IN_PROGRESS',
		title: 'Build, Test, and Publish',
		summary: 'Testing...'
	sh 'cargo test'
	publishChecks name: 'my-cool-check', status: 'IN_PROGRESS',
		title: 'Build, Test, and Publish',
		summary: 'Publishing...'
	sh 'cargo publish'
}

Обновление проверки за один шаг

Случай 2: Если ваша работа достаточно монолитна, может быть неудобно разбивать автономный скрипт на несколько просто ради того, чтобы разделить их как этапы сборки и вставить между ними проверки публикации. В таких случаях вам придется напрямую взаимодействовать с API GitHub без слоя Jenkins. Что касается модных плагинов, это означает, что вам даже не нужно использовать Плагин Checks API.

Для приложений GitHub процесс авторизации и аутентификации может быть весьма сложным:

  • Когда вы установили приложение GitHub в свой репозиторий, вы предоставили набор разрешений своему приложению. Это называется авторизацией, и то, что именно ваше приложение может делать в вашем репозитории, можно получить из GitHub через «Идентификатор установки».
  • Когда ваш скрипт инициализируется, ему необходимо аутентифицироваться на GitHub как на вашем приложении. Для этого скрипту необходим закрытый ключ приложения. Вы можете сгенерировать закрытый ключ на странице настроек приложения и использовать его навсегда.
  • Если вы имеете дело с необработанным API GitHub, аутентификация весьма сложна: сначала вы генерируете JSON Web Token (JWT) с закрытым ключом. Затем вы вызываете конечную точку GitHub с помощью этого JWT, говоря: Я — это приложение, и я пытаюсь получить доступ к репозиториям, о которых мы договорились ранее. Эта конечная точка предоставляет вам токен доступа к установке (IAT). Ваш сценарий будет использовать этот IAT для вызова других конечных точек точно так же, как вы используете PAT при вызове конечных точек вручную от имени себя.

К счастью, во многих языках есть библиотеки, которые выполняют за вас промежуточную работу. Допустим, ваше Jenkins Job запускает скрипт Python через (sh './run.py'). Мы можем использовать библиотеку PyGitHub для вызова API GitHub на языке Python.

В Python встроенная библиотека регистрации является стандартным способом отправки сообщений. Ваш сценарий создает регистратор и присоединяет к нему различные обработчики, каждый из которых определяет канал вывода ваших сообщений — стандартный вывод, файл на диске, удаленную службу и т. д. Интуитивно мы можем написать обработчик для входа в GitHub Checks.

С помощью ChatGPT я реализовал один здесь. Вы можете видеть, что для инициализации обработчика требуется довольно много параметров. Где вы их берете?

  • Чтобы найти PR и коммит, может помочь Плагин интеграции GitHub. Он может запускать Jenkins Jobs при событиях PR, внедряя эти переменные среды в каждую сборку. Мы будем использовать два: GITHUB_PR_HEAD_SHA и GITHUB_REPO_SSH_URL.
  • Для ссылки из PR-проверки на сборку Jenkins нам также понадобится файл env. вар. установил ванильный Дженкинс: BUILD_URL. Список всех переменных набора Jenkins можно найти здесь.
  • Для аутентификации мы сами. Мы можем загрузить закрытый ключ приложения в хранилище учетных данных Jenkins и обернуть шаг sh './run.py' замыканием sshUserPrivateKey. Что касается идентификатора приложения и идентификатора установки, поскольку они не обязательно являются секретными, мы можем позволить себе указать их с помощью директивы environment. В совокупности ваш Jenkinsfile может выглядеть так:
pipeline {
  environment {
    GITHUB_APP_ID = '123'
    GITHUB_APP_INSTALLATION_ID = '456'
  }
  stages {
    stage('...') {
      steps {
        script {
          sshUserPrivateKey (
            credentialsId: "...", // ID from the Jenkins credentials storage.
            keyFileVariable: 'GITHUB_APP_PRIVATE_KEY_PATH') {
              sh './run.py'

Теперь в вашем скрипте Python вы можете прочитать файл env. var.s во время инициализации:

import logging
from GitHubCheckHandler import GitHubCheckHandler

logger = logging.getLogger('...')

handler = GitHubCheckHandler(
 private_key_path=str(os.getenv("GITHUB_APP_PRIVATE_KEY_PATH")),
 installation_id=int(str(os.getenv("GITHUB_APP_INSTALLATION_ID"))),
 github_app_id=int(str(os.getenv("GITHUB_APP_ID"))),
 owner_repo=str(os.getenv("GITHUB_REPO_SSH_URL"))
  .removeprefix("[email protected]:")
  .removesuffix(".git"),
 commit_sha=str(os.getenv("GITHUB_PR_HEAD_SHA")),
 details_url=str(os.getenv("BUILD_URL")),
)

logger.addHandler(handler)

После этого вы сможете просматривать журналы Python непосредственно в разделах PR-проверок. Обратите внимание, что желтый индикатор рядом с чеком будет продолжать вращаться, пока вы отправляете сообщения. Чтобы сообщить GitHub, что эта проверка завершена, вам следует вызвать метод handler.conclude(...), поэтому вам следует оставить объект-обработчик доступным для вашей основной функции.

Краткое содержание

В этом руководстве мы рассмотрели, как автоматизировать и улучшить контроль качества PR за счет усиления интеграции между Jenkins и GitHub, в частности, за счет более своевременной отправки более полных журналов обратно PR-агентам.

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

Подводя итог, можно сказать, что автоматизация не только повышает эффективность, но и повышает общее качество процесса разработки программного обеспечения. Так что не сомневайтесь — начните автоматизировать обеспечение качества PR уже сегодня и ощутите преимущества более оптимизированного и продуктивного рабочего процесса.

Спасибо, что подписались, и удачного кодирования!