11require 'sinatra'
22require 'octokit'
3- require 'dotenv/load' # Manages environment variables
43require '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'
98
109set :port , 3000
11- set :bind , '0.0.0.0'
1210
1311
1412# This is template code to create a GitHub App server.
1513# You can read more about GitHub Apps here: # https://developer.github.com/apps/
1614#
1715# 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!
1917# You can check out one example in advanced_server.rb.
2018#
2119# This code is a Sinatra app, for two reasons:
2220# 1. Because the app will require a landing page for installation.
2321# 2. To easily handle webhook events.
2422#
23+ #
2524# Of course, not all apps need to receive and process events!
2625# Feel free to rip out the event handling code if you don't need it.
2726#
3029
3130class GHAapp < Sinatra ::Application
3231
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
3442 PRIVATE_KEY = OpenSSL ::PKey ::RSA . new ( ENV [ 'GITHUB_PRIVATE_KEY' ] . gsub ( '\n' , "\n " ) )
3543
3644 # Your registered app must have a secret set. The secret is used to verify
@@ -51,26 +59,32 @@ class GHAapp < Sinatra::Application
5159 get_payload_request ( request )
5260 verify_webhook_signature
5361 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
5563 authenticate_installation ( @payload )
5664 end
5765
5866
5967 post '/event_handler' do
6068
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
6475
65- 200 # success status
76+ 'ok' # You have to return _something_. ;)
6677 end
6778
6879
6980 helpers do
7081
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
7488
7589 # Saves the raw payload and converts the payload to JSON format
7690 def get_payload_request ( request )
@@ -87,7 +101,7 @@ def get_payload_request(request)
87101 end
88102
89103 # 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
91105 # JWT (https://jwt.io/introduction/) signed with the app's private key,
92106 # so GitHub can be sure that it came from the app an not altererd by
93107 # a malicious third party.
@@ -103,15 +117,15 @@ def authenticate_app
103117 iss : APP_IDENTIFIER
104118 }
105119
106- # Cryptographically sign the JWT.
120+ # Cryptographically sign the JWT
107121 jwt = JWT . encode ( payload , PRIVATE_KEY , 'RS256' )
108122
109123 # Create the Octokit client, using the JWT as the auth token.
110124 @app_client ||= Octokit ::Client . new ( bearer_token : jwt )
111125 end
112126
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.
115129 def authenticate_installation ( payload )
116130 installation_id = payload [ 'installation' ] [ 'id' ]
117131 installation_token = @app_client . create_app_installation_access_token ( installation_id ) [ :token ]
@@ -121,14 +135,14 @@ def authenticate_installation(payload)
121135 # Check X-Hub-Signature to confirm that this webhook was generated by
122136 # GitHub, and not a malicious third party.
123137 #
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
132146 def verify_webhook_signature
133147 their_signature_header = request . env [ 'HTTP_X_HUB_SIGNATURE' ] || 'sha1='
134148 method , their_digest = their_signature_header . split ( '=' )
@@ -143,8 +157,9 @@ def verify_webhook_signature
143157
144158 end
145159
160+
146161 # 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:
148163 # $0 is the executed file
149164 # __FILE__ is the current file
150165 # If they are the same—that is, we are running this file directly, call the Sinatra run method
0 commit comments