1
1
require 'sinatra'
2
2
require 'octokit'
3
- require 'dotenv/load' # Manages environment variables
4
3
require 'json'
5
- require 'openssl' # Verifies the webhook signature
6
- require 'jwt' # Authenticates a GitHub App
7
- require 'time' # Gets ISO 8601 representation of a Time object
8
- require 'logger' # Logs debug statements
4
+ require 'openssl' # Used to verify the webhook signature
5
+ require 'jwt' # Used to authenticate a GitHub App
6
+ require 'time' # Used to get ISO 8601 representation of a Time object
7
+ require 'logger'
9
8
10
9
set :port , 3000
11
- set :bind , '0.0.0.0'
12
10
13
11
14
12
# This is template code to create a GitHub App server.
15
13
# You can read more about GitHub Apps here: # https://developer.github.com/apps/
16
14
#
17
15
# On its own, this app does absolutely nothing, except that it can be installed.
18
- # It's up to you to add functionality!
16
+ # It's up to you to add fun functionality!
19
17
# You can check out one example in advanced_server.rb.
20
18
#
21
19
# This code is a Sinatra app, for two reasons:
22
20
# 1. Because the app will require a landing page for installation.
23
21
# 2. To easily handle webhook events.
24
22
#
23
+ #
25
24
# Of course, not all apps need to receive and process events!
26
25
# Feel free to rip out the event handling code if you don't need it.
27
26
#
30
29
31
30
class GHAapp < Sinatra ::Application
32
31
33
- # Expects that the private key in PEM format. Converts the newlines
32
+ # !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
33
+ # Instead, set and read app tokens or other secrets in your code
34
+ # in a runtime source, like an environment variable like below
35
+
36
+ # Expects that the private key has been set as an environment variable in
37
+ # PEM format using the following command to replace newlines with the
38
+ # literal `\n`:
39
+ # export GITHUB_PRIVATE_KEY=`awk '{printf "%s\\n", $0}' private-key.pem`
40
+ #
41
+ # Converts the newlines
34
42
PRIVATE_KEY = OpenSSL ::PKey ::RSA . new ( ENV [ 'GITHUB_PRIVATE_KEY' ] . gsub ( '\n' , "\n " ) )
35
43
36
44
# Your registered app must have a secret set. The secret is used to verify
@@ -51,26 +59,32 @@ class GHAapp < Sinatra::Application
51
59
get_payload_request ( request )
52
60
verify_webhook_signature
53
61
authenticate_app
54
- # Authenticate the app installation in order to run API operations
62
+ # Authenticate each installation of the app in order to run API operations
55
63
authenticate_installation ( @payload )
56
64
end
57
65
58
66
59
67
post '/event_handler' do
60
68
61
- # # # # # # # # # # # #
62
- # ADD YOUR CODE HERE #
63
- # # # # # # # # # # # #
69
+ case request . env [ 'HTTP_X_GITHUB_EVENT' ]
70
+ when 'issues'
71
+ if @payload [ 'action' ] === 'opened'
72
+ handle_issue_opened_event ( @payload )
73
+ end
74
+ end
64
75
65
- 200 # success status
76
+ 'ok' # You have to return _something_. ;)
66
77
end
67
78
68
79
69
80
helpers do
70
81
71
- # # # # # # # # # # # # # # # # #
72
- # ADD YOUR HELPER METHODS HERE #
73
- # # # # # # # # # # # # # # # # #
82
+ # When an issue is opened, add a label
83
+ def handle_issue_opened_event ( payload )
84
+ repo = payload [ 'repository' ] [ 'full_name' ]
85
+ issue_number = payload [ 'issue' ] [ 'number' ]
86
+ @installation_client . add_labels_to_an_issue ( repo , issue_number , [ 'needs-response' ] )
87
+ end
74
88
75
89
# Saves the raw payload and converts the payload to JSON format
76
90
def get_payload_request ( request )
@@ -87,7 +101,7 @@ def get_payload_request(request)
87
101
end
88
102
89
103
# Instantiate an Octokit client authenticated as a GitHub App.
90
- # GitHub App authentication requires that you construct a
104
+ # GitHub App authentication equires that we construct a
91
105
# JWT (https://jwt.io/introduction/) signed with the app's private key,
92
106
# so GitHub can be sure that it came from the app an not altererd by
93
107
# a malicious third party.
@@ -103,15 +117,15 @@ def authenticate_app
103
117
iss : APP_IDENTIFIER
104
118
}
105
119
106
- # Cryptographically sign the JWT.
120
+ # Cryptographically sign the JWT
107
121
jwt = JWT . encode ( payload , PRIVATE_KEY , 'RS256' )
108
122
109
123
# Create the Octokit client, using the JWT as the auth token.
110
124
@app_client ||= Octokit ::Client . new ( bearer_token : jwt )
111
125
end
112
126
113
- # Instantiate an Octokit client, authenticated as an installation of a
114
- # GitHub App, to run API operations.
127
+ # Instantiate an Octokit client authenticated as an installation of a
128
+ # GitHub App to run API operations.
115
129
def authenticate_installation ( payload )
116
130
installation_id = payload [ 'installation' ] [ 'id' ]
117
131
installation_token = @app_client . create_app_installation_access_token ( installation_id ) [ :token ]
@@ -121,14 +135,14 @@ def authenticate_installation(payload)
121
135
# Check X-Hub-Signature to confirm that this webhook was generated by
122
136
# GitHub, and not a malicious third party.
123
137
#
124
- # GitHub uses the WEBHOOK_SECRET, registered to the GitHub App, to
125
- # create the hash signature sent in the `X-HUB-Signature` header of each
126
- # webhook . This code computes the expected hash signature and compares it to
127
- # the signature sent in the `X-HUB-Signature` header. If they don't match,
128
- # this request is an attack, and you should reject it. GitHub uses the HMAC
129
- # hexdigest to compute the signature. The `X-HUB-Signature` looks something
130
- # like this: "sha1=123456".
131
- # See https://developer.github.com/webhooks/securing/ for details.
138
+ # GitHub will the WEBHOOK_SECRET, registered
139
+ # to the GitHub App, to create a hash signature sent in each webhook payload
140
+ # in the `X-HUB-Signature` header . This code computes the expected hash
141
+ # signature and compares it to the signature sent in the `X-HUB-Signature`
142
+ # header. If they don't match, this request is an attack, and we should
143
+ # reject it. GitHub uses the HMAC hexdigest to compute the signature. The
144
+ # `X-HUB-Signature` looks something like this: "sha1=123456"
145
+ # See https://developer.github.com/webhooks/securing/ for details
132
146
def verify_webhook_signature
133
147
their_signature_header = request . env [ 'HTTP_X_HUB_SIGNATURE' ] || 'sha1='
134
148
method , their_digest = their_signature_header . split ( '=' )
@@ -143,8 +157,9 @@ def verify_webhook_signature
143
157
144
158
end
145
159
160
+
146
161
# Finally some logic to let us run this server directly from the commandline, or with Rack
147
- # Don't worry too much about this code. But, for the curious:
162
+ # Don't worry too much about this code ;) But, for the curious:
148
163
# $0 is the executed file
149
164
# __FILE__ is the current file
150
165
# If they are the same—that is, we are running this file directly, call the Sinatra run method
0 commit comments