11require 'sinatra'
22require 'octokit'
3+ require 'dotenv/load' # Manages environment variables
4+ require 'git'
35require 'json'
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'
6+ require 'openssl' # Verifies the webhook signature
7+ require 'jwt' # Authenticates a GitHub App
8+ require 'time' # Gets ISO 8601 representation of a Time object
9+ require 'logger' # Logs debug statements
810
911set :port , 3000
12+ set :bind , '0.0.0.0'
1013
1114
1215# This is template code to create a GitHub App server.
1316# You can read more about GitHub Apps here: # https://developer.github.com/apps/
1417#
1518# On its own, this app does absolutely nothing, except that it can be installed.
16- # It's up to you to add fun functionality!
19+ # It's up to you to add functionality!
1720# You can check out one example in advanced_server.rb.
1821#
1922# This code is a Sinatra app, for two reasons:
2023# 1. Because the app will require a landing page for installation.
2124# 2. To easily handle webhook events.
2225#
23- #
2426# Of course, not all apps need to receive and process events!
2527# Feel free to rip out the event handling code if you don't need it.
2628#
2931
3032class GHAapp < Sinatra ::Application
3133
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+ # Expects that the private key in PEM format. Converts the newlines
4235 PRIVATE_KEY = OpenSSL ::PKey ::RSA . new ( ENV [ 'GITHUB_PRIVATE_KEY' ] . gsub ( '\n' , "\n " ) )
4336
4437 # Your registered app must have a secret set. The secret is used to verify
@@ -59,26 +52,25 @@ class GHAapp < Sinatra::Application
5952 get_payload_request ( request )
6053 verify_webhook_signature
6154 authenticate_app
62- # Authenticate each installation of the app in order to run API operations
55+ # Authenticate the app installation in order to run API operations
6356 authenticate_installation ( @payload )
6457 end
6558
6659
6760 post '/event_handler' do
6861
69- # # # # # # # # # # # # # # # # # # #
70- # ADD YOUR CODE HERE #
71- # # # # # # # # # # # # # # # # # # #
62+ # # # # # # # # # # # #
63+ # ADD YOUR CODE HERE #
64+ # # # # # # # # # # # #
7265
73- 'ok' # We've got to return _something_. ;)
7466 end
7567
7668
7769 helpers do
7870
79- # # # # # # # # # # # # # # # # # # #
80- # ADD YOUR HELPERS METHODS HERE #
81- # # # # # # # # # # # # # # # # # # #
71+ # # # # # # # # # # # # # # # # #
72+ # ADD YOUR HELPER METHODS HERE #
73+ # # # # # # # # # # # # # # # # #
8274
8375 # Saves the raw payload and converts the payload to JSON format
8476 def get_payload_request ( request )
@@ -95,7 +87,7 @@ def get_payload_request(request)
9587 end
9688
9789 # Instantiate an Octokit client authenticated as a GitHub App.
98- # GitHub App authentication equires that we construct a
90+ # GitHub App authentication requires that you construct a
9991 # JWT (https://jwt.io/introduction/) signed with the app's private key,
10092 # so GitHub can be sure that it came from the app an not altererd by
10193 # a malicious third party.
@@ -111,15 +103,15 @@ def authenticate_app
111103 iss : APP_IDENTIFIER
112104 }
113105
114- # Cryptographically sign the JWT
106+ # Cryptographically sign the JWT.
115107 jwt = JWT . encode ( payload , PRIVATE_KEY , 'RS256' )
116108
117109 # Create the Octokit client, using the JWT as the auth token.
118110 @app_client ||= Octokit ::Client . new ( bearer_token : jwt )
119111 end
120112
121- # Instantiate an Octokit client authenticated as an installation of a
122- # GitHub App to run API operations.
113+ # Instantiate an Octokit client, authenticated as an installation of a
114+ # GitHub App, to run API operations.
123115 def authenticate_installation ( payload )
124116 installation_id = payload [ 'installation' ] [ 'id' ]
125117 installation_token = @app_client . create_app_installation_access_token ( installation_id ) [ :token ]
@@ -129,14 +121,14 @@ def authenticate_installation(payload)
129121 # Check X-Hub-Signature to confirm that this webhook was generated by
130122 # GitHub, and not a malicious third party.
131123 #
132- # GitHub will the WEBHOOK_SECRET, registered
133- # to the GitHub App, to create a hash signature sent in each webhook payload
134- # in the `X-HUB-Signature` header . This code computes the expected hash
135- # signature and compares it to the signature sent in the `X-HUB-Signature`
136- # header. If they don't match, this request is an attack, and we should
137- # reject it. GitHub uses the HMAC hexdigest to compute the signature. The
138- # `X-HUB-Signature` looks something like this: "sha1=123456"
139- # See https://developer.github.com/webhooks/securing/ for details
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.
140132 def verify_webhook_signature
141133 their_signature_header = request . env [ 'HTTP_X_HUB_SIGNATURE' ] || 'sha1='
142134 method , their_digest = their_signature_header . split ( '=' )
@@ -150,12 +142,5 @@ def verify_webhook_signature
150142 end
151143
152144 end
153-
154-
155- # Finally some logic to let us run this server directly from the commandline, or with Rack
156- # Don't worry too much about this code ;) But, for the curious:
157- # $0 is the executed file
158- # __FILE__ is the current file
159- # If they are the same—that is, we are running this file directly, call the Sinatra run method
160145 run! if __FILE__ == $0
161146end
0 commit comments