# Доставка развертываний

С помощью REST API развертывания можно создавать пользовательские средства для взаимодействия с сервером и сторонним приложением.

Rest API можно использовать для развертывания проектов, размещенных на GitHub сервере, на котором вы владеете. Дополнительные сведения о конечных точках для управления развертываниями и состояниями см. в разделе [Конечные точки REST API для развертываний](/ru/enterprise-server@3.22/rest/deployments). Вы также можете использовать REST API для координации развертываний в момент, когда код приземляется на ветвь по умолчанию. Дополнительные сведения см. в разделе [Создание сервера непрерывной интеграции](/ru/enterprise-server@3.22/rest/guides/building-a-ci-server).

В этом руководстве будет использоваться REST API для демонстрации настройки, которую можно использовать.
В описываемом сценарии мы выполним указанные ниже действия.

* Выполним слияние запроса на вытягивание.
* По завершении непрерывной интеграции мы зададим соответствующее состояние запроса на вытягивание.
* После слияния запроса на вытягивание мы запустим развертывание на нашем сервере.

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

Если вы еще не сделали этого, обязательно `ngrok`[](https://ngrok.com/) и узнайте, как [его](https://ngrok.com/docs/getting-started/) использовать. Мы считаем, что это очень полезное средство для предоставления локальных приложений в Интернете.

Примечание. Полный исходный код для этого проекта можно скачать из [репозитория platform-samples](https://github.com/github/platform-samples/tree/master/api/ruby/delivering-deployments).

## Создание сервера

Мы создадим небольшое приложение Sinatra, чтобы подтвердить работоспособность локальных подключений.
Начнем с этого:

```ruby
require 'sinatra'
require 'json'

post '/event_handler' do
  payload = JSON.parse(params[:payload])
  "Well, it worked!"
end
```

(Если вы не знакомы с тем, как работает Sinatra, рекомендуем [ознакомиться с руководством по Sinatra](http://www.sinatrarb.com/).)

Запустите этот сервер. По умолчанию Sinatra начинается с порта `4567`, поэтому вы также хотите настроить `ngrok` для этого прослушивание.

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

Давайте создадим репозиторий, с которым можно спокойно экспериментировать. Мы предлагаем [репозиторий @octocat Spoon/Knife](https://github.com/octocat/Spoon-Knife).

После этого вы создадите в репозитории новый веб-перехватчик, задав URL-адрес, `ngrok` который дал вам, и выберите `application/x-www-form-urlencoded` тип контента.

Щелкните **Обновить веб-перехватчик**. Вы должны увидеть текст ответа `Well, it worked!`.
Отлично! Установите переключатель в положение **Разрешить мне выбрать отдельные события** и выберите следующее:

* Развертывание
* Состояния развертывания
* Запрос на вытягивание

Это события GitHub , которые будут отправляться на наш сервер всякий раз, когда происходит соответствующее действие. Пока мы настроим сервер так, чтобы он обрабатывал *только* слияния запросов на вытягивание:

```ruby
post '/event_handler' do
  @payload = JSON.parse(params[:payload])

  case request.env['HTTP_X_GITHUB_EVENT']
  when "pull_request"
    if @payload["action"] == "closed" && @payload["pull_request"]["merged"]
      puts "A pull request was merged! A deployment should start now..."
    end
  end
end
```

Почему? Каждое событие, GitHub которое отправляет присоединенный `X-GitHub-Event` заголовок HTTP. Пока нас интересуют только события, связанные с запросами на вытягивание. При слиянии запроса на вытягивание (состояние `closed`, `merged` имеет значение `true`) запускается развертывание.

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

## Работа с развертываниями

Реализовав сервер, проверив код и выполнив слияние запроса на вытягивание, теперь мы хотим развернуть проект.

Сначала мы изменим прослушиватель событий для обработки запросов на вытягивание при их слиянии и приступим к развертыванию:

```ruby
when "pull_request"
  if @payload["action"] == "closed" && @payload["pull_request"]["merged"]
    start_deployment(@payload["pull_request"])
  end
when "deployment"
  process_deployment(@payload)
when "deployment_status"
  update_deployment_status
end
```

На основе сведений из запроса на вытягивание мы начнем заполнять метод `start_deployment`:

```ruby
def start_deployment(pull_request)
  user = pull_request['user']['login']
  payload = JSON.generate(:environment => 'production', :deploy_user => user)
  @client.create_deployment(pull_request['head']['repo']['full_name'], pull_request['head']['sha'], {:payload => payload, :description => "Deploying my sweet branch"})
end
```

Развертывания могут сопровождаться метаданными в виде `payload` и `description`. Хотя эти значения являются необязательными, они полезны для ведения журнала и представления сведений.

При создании нового развертывания активируется отдельное событие. Вот почему нужен новый случай `switch` в обработчике событий `deployment`. Эти сведения можно использовать для получения уведомления об активации развертывания.

Развертывания могут занимать довольно много времени, поэтому необходимо прослушивать различные события, например создание развертывания и изменение его состояния.

Давайте сымитируем развертывание, которое выполняет некоторые действия, и посмотрим, какое влияние оно оказывает на выходные данные. Сначала завершим метод `process_deployment`:

```ruby
def process_deployment
  payload = JSON.parse(@payload['payload'])
  # you can send this information to your chat room, monitor, pager, etc.
  puts "Processing '#{@payload['description']}' for #{payload['deploy_user']} to #{payload['environment']}"
  sleep 2 # simulate work
  @client.create_deployment_status("repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}", 'pending')
  sleep 2 # simulate work
  @client.create_deployment_status("repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}", 'success')
end
```

Наконец, сымитируем сохранение сведений о состоянии путем вывода их в консоли:

```ruby
def update_deployment_status
  puts "Deployment status for #{@payload['id']} is #{@payload['state']}"
end
```

Давайте разберемся, что происходит. Развертывание создается методом `start_deployment`, который активирует событие `deployment`. Далее мы вызываем метод `process_deployment`, который имитирует выполнение некоторых задач. Во время этой обработки мы также вызываем `create_deployment_status`, чтобы получатель мог узнать, что происходит, когда состояние переведено в `pending`.

После завершения развертывания задается состояние `success`.

## Заключение

В GitHub мы уже много лет используем версию `Heaven` для управления нашими развертываниями. Процесс в целом такой же, как и в случае с созданным выше сервером.

* Ожидается ответ о состоянии проверок непрерывной интеграции (успешно или сбой).
* Если необходимые проверки пройдены успешно, выполняется слияние запроса на вытягивание.
* `Heaven` берёт объединённый код и развёртает его на серверы staging и production
* Тем временем `Heaven` мы также уведомляем всех о сборке через [Hubot](https://github.com/github/hubot) , который находится в наших чатах

Вот и все! Чтобы использовать этот пример, не нужно создавать собственную конфигурацию развертывания.
Всегда можно положиться на интеграции [GitHub ](https://github.com/integrations).