Как обработать полезную нагрузку github webhook в Jenkins?

В настоящее время я запускаю свои сборки Jenkins через веб-крючок GitHub. Как бы я проанализировал полезную нагрузку JSON? Если я пытаюсь параметризовать мою сборку и использую переменную $payload, веб-крючок GitHub завершается с ошибкой:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
<title>Error 400 This page expects a form submission</title>
</head>
<body><h2>HTTP ERROR 400</h2>
<p>Problem accessing /job/Jumph-CycleTest/build. Reason:
<pre>    This page expects a form submission</pre></p><hr /><i><small>Powered by Jetty://</small></i><br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                
<br/>                                                

</body>
</html>

Как я могу заставить мой GitHub webhook работать с параметризованной сборкой Jenkins, и как я могу затем проанализировать полезную нагрузку webhook, чтобы использовать определенные строки, такие как имя пользователя коммиттера, в качестве условий в сборке?

2 ответа

Решение

Есть несколько уловок, чтобы заставить это работать, и я нашел (теперь несуществующий) chloky.com сообщение в блоге, чтобы быть полезным для большей части этого. Похоже, что веб-крюк по крайней мере связался с вашим экземпляром Jenkins, я пока пропущу эти шаги. Но, если вы хотите больше подробностей, просто прокрутите конец моего ответа, чтобы увидеть содержание, от которого я смог спасти chloky.com - Я не знаю оригинального автора, и информация может быть устаревшей, но я нашел ее полезной.

Таким образом, чтобы подвести итог, вы можете сделать следующее, чтобы иметь дело с полезной нагрузкой:

  1. Установите строковый параметр под названием "полезная нагрузка" в вашем задании Jenkins. Если вы планируете запускать сборку вручную, было бы неплохо в какой-то момент дать ему документ JSON по умолчанию, но он вам сейчас не нужен. Это имя параметра чувствительно к регистру (я использую Linux, так что это не удивительно...)
  2. Настройте webhook в github для использования конечной точки buildWithParameters вместо конечной точки сборки, т.е. http://<<yourserver>>/job/<<yourjob>>/buildWithParameters?token=<<yourtoken>>

  3. Сконфигурируйте ваш webhook для использования application/x-www-form-encoded вместо application/json. В первом подходе данные JSON упаковываются в переменную формы, называемую "полезной нагрузкой", что, по-видимому, и позволяет Jenkins назначить их переменной среды. Подход application/json просто POSTs raw JSON, который, кажется, ни к чему не применим (я не мог заставить его работать). Вы можете увидеть разницу, указав вашему веб-крюку что-то вроде requestbin и проверив результаты.

  4. На этом этапе вы должны получить переменную $ payload при запуске сборки. Чтобы проанализировать JSON, я настоятельно рекомендую установить jq на ваш сервер Jenkins и попробовать здесь синтаксический анализ. JQ особенно хорош, потому что он кроссплатформенный.
  5. Отсюда просто разберите то, что вам нужно из JSON, в другие переменные окружения. В сочетании с этапами условной сборки это может дать вам большую гибкость.

Надеюсь это поможет!


РЕДАКТИРОВАТЬ вот что я мог бы взять из оригинальных сообщений в блоге на http://chloky.com/tag/jenkins/, который был мертв на некоторое время. Надеюсь, этот контент тоже кому-нибудь пригодится.


Пост № 1 - июль 2012

Github предоставляет хороший способ отправлять уведомления системе CI, например jenkins, всякий раз, когда совершается фиксация для репозитория. Это действительно полезно для запуска заданий сборки в jenkins для проверки коммитов, которые были только что сделаны в репо. Вам просто нужно перейти в раздел администрирования хранилища, щелкнуть сервисные хуки слева, нажать "URL-адреса веб-крючка" в верхней части списка, а затем ввести URL-адрес веб-крючка, которого ожидает Дженкинс (посмотрите на этого Дженкинса). плагин для настройки jenkins для получения этих хуков от github).

Настроить Дженкинс для веб-крючка

Недавно, однако, я искал способ запустить webhook, когда к репо делается пул-запрос, а не когда в репо делается коммит. Это сделано для того, чтобы jenkins мог выполнить кучу тестов по запросу pull, прежде чем решить, следует ли объединять запрос pull - полезно, когда у вас много разработчиков, работающих над своими собственными форками, и регулярно отправляющих запросы pull на главный сервер. Сделки рЕПО.

Оказывается, это не так очевидно, как можно было бы надеяться, и требует немного возиться с API github.

По умолчанию, когда вы настраиваете github webhook, он настроен на запуск только тогда, когда сделана фиксация против репо. Нет простого способа увидеть или изменить это в веб-интерфейсе github при настройке webhook. Для того, чтобы каким-либо образом манипулировать webhook, вам нужно использовать API.

Чтобы внести изменения в репо через github API, нам нужно авторизоваться. Мы будем использовать curl, поэтому, если бы захотели, мы могли бы каждый раз передавать имя пользователя и пароль, например:

# curl https://api.github.com/users/mancdaz --user 'mancdaz'
Enter host password for user 'mancdaz':

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

curl https://api.github.com/authorizations --user "mancdaz" \
--data '{"scopes":["repo"]}' -X POST

Вам будет возвращено что-то вроде следующего:

{
   "app":{
      "name":"GitHub API",
      "url":"http://developer.github.com/v3/oauth/#oauth-authorizations-api"
   },
   "token":"b2067d190ab94698a592878075d59bb13e4f5e96",
   "scopes":[
      "repo"
   ],
   "created_at":"2012-07-12T12:55:26Z",
   "updated_at":"2012-07-12T12:55:26Z",
   "note_url":null,
   "note":null,
   "id":498182,
   "url":"https://api.github.com/authorizations/498182"
}

Теперь мы можем использовать этот токен в последующих запросах для управления нашей учетной записью github через API. Итак, давайте запросим наш репозиторий и найдем веб-крючок, который мы настроили в веб-интерфейсе ранее:

# curl  https://api.github.com/repos/mancdaz/mygithubrepo/hooks?access_token=b2067d190ab94698592878075d59bb13e4f5e96
[
  {
    "created_at": "2012-07-12T11:18:16Z",
    "updated_at": "2012-07-12T11:18:16Z",
    "events": [
      "push"
    ],
    "last_response": {
      "status": "unused",
      "message": null,
      "code": null
    },
    "name": "web",
    "config": {
      "insecure_ssl": "1",
      "content_type": "form",
      "url": "http://jenkins-server.chloky.com/post-hook"
    },
    "id": 341673,
    "active": true,
    "url": "https://api.github.com/repos/mancdaz/mygithubrepo/hooks/341673"
  }
]

Обратите внимание на важный бит из этого вывода json:

"events": [
      "push"
    ]

По сути, это говорит о том, что этот веб-крючок будет срабатывать только тогда, когда в репо будет сделан коммит (push). Документация по github API описывает множество различных типов событий, которые можно добавить в этот список - для наших целей мы хотим добавить pull_request, и вот как мы это делаем (обратите внимание, что мы получаем идентификатор webhook из вывода json выше. Если у вас определено несколько хуков, ваш вывод будет содержать все эти хуки, поэтому убедитесь, что вы получили правильный ID):

# curl  https://api.github.com/repos/mancdaz/mygithubrepo/hooks/341673?access_token=b2067d190ab94698592878075d59bb13e4f5e96 -X PATCH --data '{"events": ["push", "pull_request"]}'
{
  "created_at": "2012-07-12T11:18:16Z",
  "updated_at": "2012-07-12T16:03:21Z",
  "last_response": {
    "status": "unused",
    "message": null,
    "code": null
  },
  "events": [
    "push",
    "pull_request"
  ],
  "name": "web",
  "config": {
    "insecure_ssl": "1",
    "content_type": "form",
    "url": "http://jenkins-server.chloky.com/post-hook"
  },
  "id": 341673,
  "active": true,
  "url": "https://api.github.com/repos/mancdaz/mygithubrepo/hooks/341673"
}

Увидеть!

"events": [
    "push",
    "pull_request"
  ],

Этот webhook теперь будет запускаться всякий раз, когда к нашему репо поступает либо коммит, либо запрос на извлечение. Именно то, что вы делаете в своих джинсах / с этим веб-крючком, зависит от вас. Мы используем его, чтобы запустить несколько интеграционных тестов в jenkins, чтобы протестировать предложенный патч, а затем фактически объединить и закрыть (снова используя API) запрос на извлечение автоматически. Довольно мило.

Пост № 2 - сентябрь 2012

В предыдущем посте я говорил о настройке github webhook для запуска по запросу, а не просто по фиксации. Как уже упоминалось, в репозитории github происходит много событий, и, согласно документации по github, многие из них могут быть использованы для запуска webhook.

Независимо от того, какое событие вы решили активировать, когда веб-крюк запускается из github, он по существу создает POST для URL-адреса, настроенного в веб-крюке, включая полезную нагрузку json в теле. Полезная нагрузка json содержит различные сведения о событии, которое вызвало срабатывание webhook. Пример полезной нагрузки, которая запускается при простом коммите, можно увидеть здесь:

payload
{
   "after":"c04a2b2af96a5331bbee0f11fe12965902f5f571",
   "before":"78d414a69db29cdd790659924eb9b27baac67f60",
   "commits":[
      {
         "added":[
            "afile"
         ],
         "author":{
            "email":"myemailaddress@mydomain.com",
            "name":"Darren Birkett",
            "username":"mancdaz"
         },
         "committer":{
            "email":"myemailaddress@mydomain.com",
            "name":"Darren Birkett",
            "username":"mancdaz"
         },
         "distinct":true,
         "id":"c04a2b2af96a5331bbee0f11fe12965902f5f571",
         "message":"adding afile",
         "modified":[

         ],
         "removed":[

         ],
         "timestamp":"2012-09-03T02:35:59-07:00",
         "url":"https://github.com/mancdaz/mygithubrepo/commit/c04a2b2af96a5331bbee0f11fe12965902f5f571"
      }
   ],
   "compare":"https://github.com/mancdaz/mygithubrepo/compare/78d414a69db2...c04a2b2af96a",
   "created":false,
   "deleted":false,
   "forced":false,
   "head_commit":{
      "added":[
         "afile"
      ],
      "author":{
         "email":"myemailaddress@mydomain.com",
         "name":"Darren Birkett",
         "username":"mancdaz"
      },
      "committer":{
         "email":"myemailaddress@mydomain.com",
         "name":"Darren Birkett",
         "username":"mancdaz"
      },
      "distinct":true,
      "id":"c04a2b2af96a5331bbee0f11fe12965902f5f571",
      "message":"adding afile",
      "modified":[

      ],
      "removed":[

      ],
      "timestamp":"2012-09-03T02:35:59-07:00",
      "url":"https://github.com/mancdaz/mygithubrepo/commit/c04a2b2af96a5331bbee0f11fe12965902f5f571"
   },
   "pusher":{
      "email":"myemailaddress@mydomain.com",
      "name":"mancdaz"
   },
   "ref":"refs/heads/master",
   "repository":{
      "created_at":"2012-07-12T04:17:51-07:00",
      "description":"",
      "fork":false,
      "forks":1,
      "has_downloads":true,
      "has_issues":true,
      "has_wiki":true,
      "name":"mygithubrepo",
      "open_issues":0,
      "owner":{
         "email":"myemailaddress@mydomain.com",
         "name":"mancdaz"
      },
      "private":false,
      "pushed_at":"2012-09-03T02:36:06-07:00",
      "size":124,
      "stargazers":1,
      "url":"https://github.com/mancdaz/mygithubrepo",
      "watchers":1
   }
}

Вся эта полезная нагрузка передается в запросах POST как один параметр с оригинальным заголовком payload, Он содержит тонну информации о только что произошедшем событии, все или любой из которых может использоваться jenkins, когда мы создаем задания после триггера. Чтобы использовать эту полезную нагрузку в Jenkins, у нас есть несколько вариантов. Я обсуждаю один ниже.

Получение полезной нагрузки

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

Получение полезной нагрузки

Передача параметров в работу сборки jenkins

Далее в конфигурации задания мы можем указать, что мы хотим иметь возможность запускать сборку удаленно (то есть, что мы хотим разрешить github запускать сборку, публикуя наш URL с полезной нагрузкой):

Передача параметров

Затем, когда мы настраиваем webhook в нашем репозитории github (как описано в первом посте), мы даем ему URL-адрес, на который нам говорит jenkins:

Настройка URL в Github

Вы не можете видеть все это на экране, но URL, который я указал для webhook, был тем, на который мне сказал Дженкинс:

http://jenkins-server.chloky.com:8080/job/mytestbuild//buildWithParameters?token=asecuretoken Теперь, когда я построил свою новую работу в jenkins, для целей этого теста я просто сказал ему выводить содержимое параметра 'payload' (который доступен в параметризованных сборках как переменная оболочки с тем же именем), используя простой скрипт:

#!/bin/bash

echo "the build worked! The payload is $payload"

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

mancdaz@chloky$ (git::master)$ touch myfile

mancdaz@chloky$ (git::master) git add myfile

mancdaz@chloky$ (git::master) git commit -m 'added my file'
[master 4810490] added my file
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 myfile

mancdaz@chloky$ (git::master) git push
Counting objects: 3, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 232 bytes, done.
Total 2 (delta 1), reused 0 (delta 0)
To git@github.com:mancdaz/mygithubrepo.git
 c7ecafa..4810490 master -> master

И на нашем сервере jenkins мы можем посмотреть на консольный вывод задания, которое было запущено, и вот, наш "полезный груз" содержится в переменной $ payload и доступен для использования:

Так здорово, вся информация о нашем мероприятии на github здесь. и полностью доступны в нашей работе Дженкинс! Правда, он в большом json-объекте, но с небольшим хитрым ударом ты должен быть в порядке.

Конечно, в этом примере использовался простой коммит, чтобы продемонстрировать принципы получения полезной нагрузки внутри jenkins. Как мы уже говорили в предыдущем посте, фиксация - это одно из многих событий в репо, которое может вызвать веб-крючок. То, что вы делаете внутри jenkins после запуска, зависит от вас, но самое интересное приходит, когда вы начинаете взаимодействовать с github, чтобы выполнять дальнейшие действия в репо (публиковать комментарии, запросы на слияние, отклонять коммиты и т. Д.) На основе результатов ваши рабочие задания сборки, которые были вызваны начальным событием.

Посмотрите на следующий пост, где я свяжу все это вместе и покажу вам, как обрабатывать, выполнять тесты и, наконец, объединять запрос на извлечение в случае успеха - все автоматически внутри jenkins. Автоматизация это весело!

Существует плагин Generic Webhook Trigger, который может добавлять значения из содержимого публикации в сборку.

Если содержание публикации:

{
   "app":{
      "name":"GitHub API",
      "url":"http://developer.github.com/v3/oauth/#oauth-authorizations-api"
   }
}

Вы можете настроить его так:

И при запуске с некоторым содержимым сообщения:

curl -v -H "Content-Type: application/json" -X POST -d '{ "app":{ "name":"GitHub API", "url":"http://developer.github.com/v3/oauth/" }}' http://localhost:8080/jenkins/generic-webhook-trigger/invoke?token=sometoken

Он разрешит переменные и сделает их доступными в работе по сборке.

{  
   "status":"ok",
   "data":{  
      "triggerResults":{  
         "free":{  
            "id":2,
            "regexpFilterExpression":"",
            "regexpFilterText":"",
            "resolvedVariables":{  
               "app_name":"GitHub API",
               "everything_app_url":"http://developer.github.com/v3/oauth/",
               "everything":"{\"app\":{\"name\":\"GitHub API\",\"url\":\"http://developer.github.com/v3/oauth/\"}}",
               "everything_app_name":"GitHub API"
            },
            "searchName":"",
            "searchUrl":"",
            "triggered":true,
            "url":"queue/item/2/"
         }
      }
   }
}
Другие вопросы по тегам