Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Nov 1, 2017. It is now read-only.

Commit54dcedd

Browse files
committed
Merge pull request#473 from github/update-1396645501
Sync changes from upstream repository
2 parents08295e1 +a8703c6 commit54dcedd

File tree

7 files changed

+368
-6
lines changed

7 files changed

+368
-6
lines changed
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
---
2+
title:Building a CI server | GitHub API
3+
---
4+
5+
#Building a CI server
6+
7+
* TOC
8+
{:toc}
9+
10+
The[Status API][status API] is responsible for tying together commits with
11+
a testing service, so that every push you make can be tested and represented
12+
in a GitHub pull request.
13+
14+
This guide will use that API to demonstrate a setup that you can use.
15+
In our scenario, we will:
16+
17+
* Run our CI suite when a Pull Request is opened (we'll set the CI status to pending).
18+
* When the CI is finished, we'll set the Pull Request's status accordingly.
19+
20+
Our CI system and host server will be figments of our imagination. They could be
21+
Travis, Jenkins, or something else entirely. The crux of this guide will be setting up
22+
and configuring the server managing the communication.
23+
24+
If you haven't already, be sure to[download ngrok][ngrok], and learn how
25+
to[use it][using ngrok]. We find it to be a very useful tool for exposing local
26+
connections.
27+
28+
Note: you can download the complete source code for this project
29+
[from the platform-samples repo][platform samples].
30+
31+
##Writing your server
32+
33+
We'll write a quick Sinatra app to prove that our local connections are working.
34+
Let's start with this:
35+
36+
#!ruby
37+
require 'sinatra'
38+
require 'json'
39+
40+
post '/event_handler' do
41+
payload = JSON.parse(params[:payload])
42+
"Well, it worked!"
43+
end
44+
45+
46+
(If you're unfamiliar with how Sinatra works, we recommend[reading the Sinatra guide][Sinatra].)
47+
48+
Start this server up. By default, Sinatra starts on port`9393`, so you'll want
49+
to configure ngrok to start listening for that, too.
50+
51+
In order for this server to work, we'll need to set a repository up with a webhook.
52+
The webhook should be configured to fire whenever a Pull Request is created, or merged.
53+
Go ahead and create a repository you're comfortable playing around in. Might we
54+
suggest[@octocat's Spoon/Knife repository](https://github.com/octocat/Spoon-Knife)?
55+
After that, you'll create a new webhook in your repository, feeding it the URL
56+
that ngrok gave you:
57+
58+
![A new ngrok URL](/images/webhook_sample_url.png)
59+
60+
Click**Update webhook**. You should see a body response of`Well, it worked!`.
61+
Great! Click on**Let me select individual events.**, and select the following:
62+
63+
* Status
64+
* Pull Request
65+
66+
These are the events GitHub will send to our server whenever the relevant action
67+
occurs. Let's update our server to*just* handle the Pull Request scenario right now:
68+
69+
#!ruby
70+
post '/event_handler' do
71+
@payload = JSON.parse(params[:payload])
72+
73+
case request.env['HTTP_X_GITHUB_EVENT']
74+
when "pull_request"
75+
if @payload["action"] == "opened"
76+
process_pull_request(@payload["pull_request"])
77+
end
78+
end
79+
end
80+
81+
helpers do
82+
def process_pull_request(pull_request)
83+
puts "It's #{pull_request['title']}"
84+
end
85+
end
86+
87+
What's going on? Every event that GitHub sends out attached a`X-GitHub-Event`
88+
HTTP header. We'll only care about the PR events for now. From there, we'll
89+
take the payload of information, and return the title field. In an ideal scenario,
90+
our server would be concerned with every time a pull request is updated, not just
91+
when it's updated. That would make sure that every new push passes the CI tests.
92+
But for this demo, we'll just worry about when it's opened.
93+
94+
To test out this proof-of-concept, make some changes in a branch in your test
95+
repository, and open a pull request. Your server should respond accordingly!
96+
97+
##Working with statuses
98+
99+
With our server in place, we're ready to start our first requirement, which is
100+
setting (and updating) CI statuses. Note that at any time you update your server,
101+
you can click**Redeliver** to send the same payload. There's no need to make a
102+
new pull request every time you make a change!
103+
104+
Since we're interacting with the GitHub API, we'll use[Octokit.rb][octokit.rb]
105+
to manage our interactions. We'll configure that client with
106+
[a personal access token][access token]:
107+
108+
#!ruby
109+
# !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
110+
# Instead, set and test environment variables, like below
111+
ACCESS_TOKEN = ENV['MY_PERSONAL_TOKEN']
112+
113+
before do
114+
@client ||= Octokit::Client.new(:access_token => ACCESS_TOKEN)
115+
end
116+
117+
After that, we'll just need to update the pull request on GitHub to make clear
118+
that we're processing on the CI:
119+
120+
#!ruby
121+
def process_pull_request(pull_request)
122+
puts "Processing pull request..."
123+
@client.create_status(pull_request['head']['repo']['full_name'], pull_request['head']['sha'], 'pending')
124+
end
125+
126+
We're doing three very basic things here:
127+
128+
* we're looking up the full name of the repository
129+
* we're looking up the last SHA of the pull request
130+
* we're setting the status to "pending"
131+
132+
That's it! From here, you can run whatever process you need to in order to execute
133+
your test suite. Maybe you're going to pass off your code to Jenkins, or call
134+
on another web service via its API, like[Travis][travis api]. After that, you'd
135+
be sure to update the status once more. In our example, we'll just set it to`"success"`:
136+
137+
#!ruby
138+
def process_pull_request(pull_request)
139+
@client.create_status(pull_request['head']['repo']['full_name'], pull_request['head']['sha'], 'pending')
140+
sleep 2 # do busy work...
141+
@client.create_status(pull_request['head']['repo']['full_name'], pull_request['head']['sha'], 'success')
142+
puts "Pull request processed!"
143+
end
144+
145+
##Conclusion
146+
147+
At GitHub, we've used a version of[Janky][janky] to manage our CI for years.
148+
The basic flow is essentially the exact same as the server we've built above.
149+
At GitHub, we:
150+
151+
* Fire to Jenkins when a pull request is created or updated (via Janky)
152+
* Wait for a response on the state of the CI
153+
* If the code is green, we merge the pull request
154+
155+
All of this communication is funneled back to our chat rooms. You don't need to
156+
build your own CI or deployment setup to use this example.
157+
You can always rely on[third-party services][integrations].
158+
159+
[deploy API]:/v3/repos/deployments/
160+
[status API]:/v3/repos/statuses/
161+
[ngrok]:https://ngrok.com/
162+
[using ngrok]:/webhooks/configuring/#using-ngrok
163+
[platform samples]:https://github.com/github/platform-samples/tree/master/api/ruby/building-a-ci-server
164+
[Sinatra]:http://www.sinatrarb.com/
165+
[webhook]:/webhooks/
166+
[octokit.rb]:https://github.com/octokit/octokit.rb
167+
[access token]:https://help.github.com/articles/creating-an-access-token-for-command-line-use
168+
[travis api]:https://api.travis-ci.org/docs/
169+
[janky]:https://github.com/github/janky
170+
[heaven]:https://github.com/atmos/heaven
171+
[hubot]:https://github.com/github/hubot
172+
[integrations]:https://github.com/integrations
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
---
2+
title:Delivering deployments | GitHub API
3+
---
4+
5+
#Delivering deployments
6+
7+
* TOC
8+
{:toc}
9+
10+
The[Deployments API][deploy API] provides your projects hosted on GitHub with
11+
the capability to launch them on a production server that you own. Combined with
12+
[the Status API][status API], you'll be able to coordinate your deployments
13+
the moment your code lands on`master`.
14+
15+
This guide will use that API to demonstrate a setup that you can use.
16+
In our scenario, we will:
17+
18+
* Merge a Pull Request
19+
* When the CI is finished, we'll set the Pull Request's status accordingly.
20+
* When the Pull Request is merged, we'll run our deployment to our server.
21+
22+
Our CI system and host server will be figments of our imagination. They could be
23+
Heroku, Amazon, or something else entirely. The crux of this guide will be setting up
24+
and configuring the server managing the communication.
25+
26+
If you haven't already, be sure to[download ngrok][ngrok], and learn how
27+
to[use it][using ngrok]. We find it to be a very useful tool for exposing local
28+
connections.
29+
30+
Note: you can download the complete source code for this project
31+
[from the platform-samples repo][platform samples].
32+
33+
##Writing your server
34+
35+
We'll write a quick Sinatra app to prove that our local connections are working.
36+
Let's start with this:
37+
38+
#!ruby
39+
require 'sinatra'
40+
require 'json'
41+
42+
post '/event_handler' do
43+
payload = JSON.parse(params[:payload])
44+
"Well, it worked!"
45+
end
46+
47+
48+
(If you're unfamiliar with how Sinatra works, we recommend[reading the Sinatra guide][Sinatra].)
49+
50+
Start this server up. By default, Sinatra starts on port`9393`, so you'll want
51+
to configure ngrok to start listening for that, too.
52+
53+
In order for this server to work, we'll need to set a repository up with a webhook.
54+
The webhook should be configured to fire whenever a Pull Request is created, or merged.
55+
Go ahead and create a repository you're comfortable playing around in. Might we
56+
suggest[@octocat's Spoon/Knife repository](https://github.com/octocat/Spoon-Knife)?
57+
After that, you'll create a new webhook in your repository, feeding it the URL
58+
that ngrok gave you:
59+
60+
![A new ngrok URL](/images/webhook_sample_url.png)
61+
62+
Click**Update webhook**. You should see a body response of`Well, it worked!`.
63+
Great! Click on**Let me select individual events.**, and select the following:
64+
65+
* Deployment
66+
* Deployment status
67+
* Pull Request
68+
69+
These are the events GitHub will send to our server whenever the relevant action
70+
occurs. We'll configure our server to*just* handle when Pull Requests are merged
71+
right now:
72+
73+
#!ruby
74+
post '/event_handler' do
75+
@payload = JSON.parse(params[:payload])
76+
77+
case request.env['HTTP_X_GITHUB_EVENT']
78+
when "pull_request"
79+
if @payload["action"] == "closed" && @payload["pull_request"]["merged"]
80+
puts "A pull request was merged! A deployment should start now..."
81+
end
82+
end
83+
end
84+
85+
What's going on? Every event that GitHub sends out attached a`X-GitHub-Event`
86+
HTTP header. We'll only care about the PR events for now. When a pull request is
87+
merged (its state is`closed`, and`merged` is`true`), we'll kick off a deployment.
88+
89+
To test out this proof-of-concept, make some changes in a branch in your test
90+
repository, open a pull request, and merge it. Your server should respond accordingly!
91+
92+
##Working with deployments
93+
94+
With our server in place, the code being reviewed, and our pull request
95+
is merged, we want our project to be deployed to the production server.
96+
97+
We'll start by modifying our event listener to process pull requests when they're
98+
merged, and start paying attention to deployments:
99+
100+
#!ruby
101+
when "pull_request"
102+
if @payload["action"] == "closed" && @payload["pull_request"]["merged"]
103+
start_deployment(@payload["pull_request"])
104+
end
105+
when "deployment"
106+
process_deployment(@payload)
107+
when "deployment_status"
108+
update_deployment_status
109+
end
110+
111+
Based on the information from the pull request, we'll start by filling out the
112+
`start_deployment` method:
113+
114+
#!ruby
115+
def start_deployment(pull_request)
116+
user = pull_request['user']['login']
117+
payload = JSON.generate(:environment => 'production', :deploy_user => user)
118+
@client.create_deployment(pull_request['head']['repo']['full_name'], pull_request['head']['sha'], {:payload => payload, :description => "Deploying my sweet branch"})
119+
end
120+
121+
Deployments can have some metadata attached to them, in the form of a`payload`
122+
and a`description`. Although these values are optional, it's helpful to use
123+
for logging and representing information.
124+
125+
When a new deployment is created, a completely separate event is trigged. That's
126+
why we have a new`switch` case in the event handler for`deployment`. You can
127+
use this information to be notified when a deployment has been triggered.
128+
129+
Deployments can take a rather long time, so we'll want to listen for various events,
130+
such as when the deployment was created, and what state it's in.
131+
132+
Let's simulate a deployment that does some work, and notice the effect it has on
133+
the output. First, let's complete our`process_deployment` method:
134+
135+
#!ruby
136+
def process_deployment
137+
payload = JSON.parse(@payload['payload'])
138+
# you can send this information to your chat room, monitor, pager, e.t.c.
139+
puts "Processing '#{@payload['description']}' for #{payload['deploy_user']} to #{payload['environment']}"
140+
sleep 2 # simulate work
141+
@client.create_deployment_status("repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}", 'pending')
142+
sleep 2 # simulate work
143+
@client.create_deployment_status("repos/#{@payload['repository']['full_name']}/deployments/#{@payload['id']}", 'success')
144+
end
145+
146+
Finally, we'll simulate storing the status information as console output:
147+
148+
#!ruby
149+
def update_deployment_status
150+
puts "Deployment status for #{@payload['id']} is #{@payload['state']}"
151+
end
152+
153+
Let's break down what's going on. A new deployment is created by`start_deployment`,
154+
which triggers the`deployment` event. From there, we call`process_deployment`
155+
to simulate work that's going on. During that processing, we also make a call to
156+
`create_deployment_status`, which lets a receiver know what's going on, as we
157+
switch the status to`pending`.
158+
159+
After the deployment is finished, we set the status to`success`. You'll notice
160+
that this pattern is the exact same as when we you your CI statuses.
161+
162+
##Conclusion
163+
164+
At GitHub, we've used a version of[Heaven][heaven] to manage
165+
our deployments for years. The basic flow is essentially the exact same as the
166+
server we've built above. At GitHub, we:
167+
168+
* Wait for a response on the state of the CI
169+
* If the code is green, we merge the pull request
170+
* Heaven takes the merged code, and deploys it to our production servers
171+
* In the meantime, Heaven also notifies everyone about the build, via[Hubot][hubot] sitting in our chat rooms
172+
173+
That's it! You don't need to build your own CI or deployment setup to use this example.
174+
You can always rely on[third-party services][integrations].
175+
176+
[deploy API]:/v3/repos/deployments/
177+
[status API]:/guides/building-a-ci-server
178+
[ngrok]:https://ngrok.com/
179+
[using ngrok]:/webhooks/configuring/#using-ngrok
180+
[platform samples]:https://github.com/github/platform-samples/tree/master/api/ruby/delivering-deployments
181+
[Sinatra]:http://www.sinatrarb.com/
182+
[webhook]:/webhooks/
183+
[octokit.rb]:https://github.com/octokit/octokit.rb
184+
[access token]:https://help.github.com/articles/creating-an-access-token-for-command-line-use
185+
[travis api]:https://api.travis-ci.org/docs/
186+
[janky]:https://github.com/github/janky
187+
[heaven]:https://github.com/atmos/heaven
188+
[hubot]:https://github.com/github/hubot
189+
[integrations]:https://github.com/integrations

‎content/guides/index.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ stored and documented in our public
1515

1616
Feel free to fork, clone, and improve these guides.
1717

18-
![The Professorcat](/images/professorcat.png)
18+
![The Professorcat](/images/electrocat.png)

‎content/webhooks/creating/index.md‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,3 @@ it's time to set up our local server to test the webhook. Head on over to
5858

5959
[webhook-api]:/v3/repos/hooks/
6060
[hooks-api]:/webhooks/#events
61-

‎layouts/guides.html‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ <h2><a href="/v3/">API</a></h2>
2424
<ul>
2525
<li><h3><ahref="/guides/">Overview</a></h3></li>
2626
<li><h3><ahref="/guides/getting-started/">Getting Started</a></h3></li>
27-
<li><h3><ahref="/guides/basics-of-authentication/">Basics of Authentication</a></h3></li>
28-
<li><h3><ahref="/guides/rendering-data-as-graphs/">Rendering Data as Graphs</a></h3></li>
29-
<li><h3><ahref="/guides/working-with-comments/">Working with Comments</a></h3></li>
30-
<li><h3><ahref="/guides/traversing-with-pagination/">Traversing with Pagination</a></h3></li>
27+
<li><h3><ahref="/guides/basics-of-authentication/">Basics of authentication</a></h3></li>
28+
<li><h3><ahref="/guides/rendering-data-as-graphs/">Rendering data as graphs</a></h3></li>
29+
<li><h3><ahref="/guides/working-with-comments/">Working with comments</a></h3></li>
30+
<li><h3><ahref="/guides/traversing-with-pagination/">Traversing with pagination</a></h3></li>
31+
<li><h3><ahref="/guides/building-a-ci-server/">Building a CI server</a></h3></li>
32+
<li><h3><ahref="/guides/delivering-deployments/">Delivering deployments</a></h3></li>
3133
</ul>
3234
</div>
3335

‎static/images/electrocat.png‎

25.4 KB
Loading
61.9 KB
Loading

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp