# CI 서버 빌드

상태 API를 사용하여 고유한 CI 시스템을 빌드합니다.

REST API를 사용하여 커밋을 테스트 서비스와 연결할 수 있으므로 모든 푸시를 테스트하고 끌어오기 요청으로 GitHub 나타낼 수 있습니다. 관련 엔드포인트에 대한 자세한 내용은 [커밋 상태에 대한 REST API 엔드포인트](/ko/enterprise-server@3.22/rest/commits/statuses)을(를) 참조하세요.

이 가이드에서는 해당 API로 사용할 수 있는 설정을 보여 줍니다.
이 시나리오에서는 다음을 수행합니다.

* 끌어오기 요청이 열릴 때 CI 제품군을 실행합니다(CI 상태를 보류 중으로 설정).
* CI가 완료되면 그에 따라 끌어오기 요청의 상태를 설정합니다.

CI 시스템 및 호스트 서버는 상상한 그대로가 됩니다. Travis, Jenkins 또는 완전히 다른 무언가가 될 수 있습니다. 이 가이드의 핵심은 통신을 관리하는 서버를 설정하고 구성하는 것입니다.

아직 다운로드하지 않은 경우 [`ngrok`을 다운로드](https://ngrok.com/)하고 [사용](https://ngrok.com/docs/getting-started/) 방법을 알아보세요. 로컬 애플리케이션을 인터넷에 노출하는 데 매우 유용한 도구라고 합니다.

참고: [플랫폼 샘플 리포지토리에서](https://github.com/github/platform-samples/tree/master/api/ruby/building-a-ci-server) 이 프로젝트에 대한 전체 소스 코드를 다운로드할 수 있습니다.

## 서버 작성

로컬 연결이 잘 작동하는지 확인하기 위해 간단한 Sinatra 앱을 작성합니다.
먼저 다음을 살펴보겠습니다.

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

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

(Sinatra의 작동 방식에 익숙하지 않은 경우 [ 가이드를 읽는 것이](http://www.sinatrarb.com/) 좋습니다.)

이 서버를 시작합니다. 기본적으로 Sinatra는 포트 `4567`에서 시작되므로 `ngrok`도 수신 대기를 시작하도록 구성할 수 있습니다.

이 서버가 작동하려면 웹후크를 사용하여 리포지토리를 설정해야 합니다. 끌어오기 요청을 만들거나 병합할 때마다 webhook가 실행되도록 구성해야 합니다.

편하게 실험할 수 있는 리포지토리를 만들어 보세요.
[
@octocat의 스푼/나이프 리포지토리](https://github.com/octocat/Spoon-Knife)를 제안해보겠습니다.

그런 다음, 리포지토리에 새 webhook를 만들고, `ngrok`에서 제공한 URL을 피드하고, 콘텐츠 형식으로 `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"] == "opened"
      process_pull_request(@payload["pull_request"])
    end
  end
end

helpers do
  def process_pull_request(pull_request)
    puts "It's #{pull_request['title']}"
  end
end
```

무슨 일입니까?
GitHub가 전송하는 모든 이벤트에는 `X-GitHub-Event` HTTP 헤더가 첨부됩니다. 지금은 PR 이벤트만 신경을 써야 합니다. 여기에서 정보의 페이로드를 가져와서 제목 필드를 반환합니다. 이상적인 시나리오에서는 풀 리퀘스트가 열릴 때뿐만 아니라 업데이트될 때마다 서버가 관심을 가질 것입니다. 이렇게 하면 모든 새 푸시가 CI 테스트를 통과하도록 보장합니다.
하지만 이 데모에서는 언제 여는지에 대해서만 걱정합니다.

이 개념 증명을 테스트하려면 테스트 리포지토리의 브랜치에서 몇 가지 변경을 하고, 끌어오기 요청을 생성합니다. 서버가 그에 따라 응답해야 합니다!

## 상태 작업

서버를 준비하면 CI 상태를 설정(및 업데이트)하는 첫 번째 요구 사항을 시작할 준비가 된 것입니다. 서버를 업데이트할 때마다 **Redeliver**를 클릭하여 동일한 페이로드를 보낼 수 있습니다. 변경할 때마다 새 끌어오기 요청을 수행할 필요가 없습니다.

API와 GitHub 상호 작용하므로 [Octokit.rb](https://github.com/octokit/octokit.rb) 를 사용하여 상호 작용을 관리합니다. 해당 클라이언트를 [a personal access token](/ko/enterprise-server@3.22/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens)로 구성하겠습니다:

```ruby
# !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
# Instead, set and test environment variables, like below
ACCESS_TOKEN = ENV['MY_PERSONAL_TOKEN']

before do
  @client ||= Octokit::Client.new(:access_token => ACCESS_TOKEN)
end
```

그다음에는 GitHub의 풀 리퀘스트를 업데이트해서 CI에서 처리되고 있다는 점을 분명히 하면 됩니다.

```ruby
def process_pull_request(pull_request)
  puts "Processing pull request..."
  @client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'pending')
end
```

여기서는 세 가지 매우 기본적인 작업을 수행합니다.

* 리포지토리의 전체 이름을 찾습니다.
* 끌어오기 요청의 마지막 SHA를 찾습니다.
* 상태를 “보류 중”으로 설정합니다.

정말 간단하죠. 여기에서 테스트 제품군을 실행하기 위해 필요한 모든 프로세스를 실행할 수 있습니다. 코드를 Jenkins에 전달하거나 [Travis](https://api.travis-ci.com/docs/)와 같은 API를 통해 다른 웹 서비스를 호출할 수 있습니다. 그 후에는 상태를 다시 한 번 업데이트해야 합니다. 이 예제에서는 `"success"`로 설정하기만 하면 됩니다.

```ruby
def process_pull_request(pull_request)
  @client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'pending')
  sleep 2 # do busy work...
  @client.create_status(pull_request['base']['repo']['full_name'], pull_request['head']['sha'], 'success')
  puts "Pull request processed!"
end
```

## 결론

GitHub 몇 년 동안 CI를 관리하기 위해 [Janky](https://github.com/github/janky) 버전을 사용했습니다.
기본 흐름은 기본적으로 위에서 빌드한 서버와 동일합니다.
GitHub에서는, 우리는:

* 끌어오기 요청이 생성되거나 업데이트될 때 Janky를 통해 Jenkins에 트리거합니다.
* CI 상태에 대한 응답을 기다립니다.
* 코드가 녹색이면 끌어오기 요청을 병합합니다.

이 모든 통신은 채팅방으로 다시 유입됩니다. 이 예제를 사용하기 위해 고유한 CI 설정을 빌드할 필요가 없습니다.
항상 [GitHub 통합](https://github.com/integrations) 사용할 수 있습니다.