Skip to content
This repository was archived by the owner on Jun 8, 2023. It is now read-only.

GitHub temp #16

Open
wants to merge 19 commits into
base: testing-push
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env-example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
GITHUB_PRIVATE_KEY=""
GITHUB_APP_IDENTIFIER=
GITHUB_WEBHOOK_SECRET=
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
.env
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ source 'http://rubygems.org'
gem 'sinatra', '~> 2.0'
gem 'jwt', '~> 2.1'
gem 'octokit', '~> 4.0'
gem 'dotenv'
20 changes: 11 additions & 9 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,36 @@ GEM
specs:
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
faraday (0.15.2)
dotenv (2.5.0)
faraday (0.15.3)
multipart-post (>= 1.2, < 3)
jwt (2.1.0)
multipart-post (2.0.0)
mustermann (1.0.2)
octokit (4.9.0)
mustermann (1.0.3)
octokit (4.13.0)
sawyer (~> 0.8.0, >= 0.5.3)
public_suffix (3.0.2)
rack (2.0.5)
rack-protection (2.0.3)
public_suffix (3.0.3)
rack (2.0.6)
rack-protection (2.0.4)
rack
sawyer (0.8.1)
addressable (>= 2.3.5, < 2.6)
faraday (~> 0.8, < 1.0)
sinatra (2.0.3)
sinatra (2.0.4)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.3)
rack-protection (= 2.0.4)
tilt (~> 2.0)
tilt (2.0.8)

PLATFORMS
ruby

DEPENDENCIES
dotenv
jwt (~> 2.1)
octokit (~> 4.0)
sinatra (~> 2.0)

BUNDLED WITH
1.14.6
1.17.1
20 changes: 14 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
This is the sample project built by following the "[Building Your First GitHub App](https://developer.github.com/apps/building-your-first-github-app)" Quickstart guide on developer.github.com.
This is an example GitHub App that adds a label to all new issues opened in a repository. You can follow the "[Using the GitHub API in your app](https://developer.github.com/apps/quickstart-guides/using-the-github-api-in-your-app/)" quickstart guide on developer.github.com to learn how to build the app code in `server.rb`.

It consists of two different servers: `server.rb` (boilerplate) and `advanced_server.rb` (completed project).
This project listens for webhook events and uses the Octokit.rb library to make REST API calls. This example project consists of two different servers:
* `template_server.rb` (GitHub App template code)
* `server.rb` (completed project)

## Install and run
To learn how to set up a template GitHub App, follow the "[Setting up your development environment](https://developer.github.com/apps/quickstart-guides/setting-up-your-development-environment/)" quickstart guide on developer.github.com.

## Install

To run the code, make sure you have [Bundler](http://gembundler.com/) installed; then enter `bundle install` on the command line.

* For the boilerplate project, enter `ruby server.rb` on the command line.
## Set environment variables

1. Create a copy of the `.env-example` file called `.env`.
2. Add your GitHub App's private key, app ID, and webhook secret to the `.env` file.

* For the completed project, enter `ruby advanced_server.rb` on the command line.
## Run the server

Both commands will run the server at `localhost:3000`.
1. Run `ruby template_server.rb` or `ruby server.rb` on the command line.
1. View the default Sinatra app at `localhost:3000`.
107 changes: 44 additions & 63 deletions server.rb
Original file line number Diff line number Diff line change
@@ -1,44 +1,19 @@
require 'sinatra'
require 'octokit'
require 'dotenv/load' # Manages environment variables
require 'json'
require 'openssl' # Used to verify the webhook signature
require 'jwt' # Used to authenticate a GitHub App
require 'time' # Used to get ISO 8601 representation of a Time object
require 'logger'
require 'openssl' # Verifies the webhook signature
require 'jwt' # Authenticates a GitHub App
require 'time' # Gets ISO 8601 representation of a Time object
require 'logger' # Logs debug statements

set :port, 3000


# This is template code to create a GitHub App server.
# You can read more about GitHub Apps here: # https://developer.github.com/apps/
#
# On its own, this app does absolutely nothing, except that it can be installed.
# It's up to you to add fun functionality!
# You can check out one example in advanced_server.rb.
#
# This code is a Sinatra app, for two reasons:
# 1. Because the app will require a landing page for installation.
# 2. To easily handle webhook events.
#
#
# Of course, not all apps need to receive and process events!
# Feel free to rip out the event handling code if you don't need it.
#
# Have fun!
#
set :bind, '0.0.0.0'

class GHAapp < Sinatra::Application

# !!! DO NOT EVER USE HARD-CODED VALUES IN A REAL APP !!!
# Instead, set and read app tokens or other secrets in your code
# in a runtime source, like an environment variable like below

# Expects that the private key has been set as an environment variable in
# PEM format using the following command to replace newlines with the
# literal `\n`:
# export GITHUB_PRIVATE_KEY=`awk '{printf "%s\\n", $0}' private-key.pem`
#
# Converts the newlines
# Converts the newlines. Expects that the private key has been set as an
# environment variable in PEM format.
PRIVATE_KEY = OpenSSL::PKey::RSA.new(ENV['GITHUB_PRIVATE_KEY'].gsub('\n', "\n"))

# Your registered app must have a secret set. The secret is used to verify
Expand All @@ -54,31 +29,37 @@ class GHAapp < Sinatra::Application
end


# Before each request to the `/event_handler` route
# Executed before each request to the `/event_handler` route
before '/event_handler' do
get_payload_request(request)
verify_webhook_signature
authenticate_app
# Authenticate each installation of the app in order to run API operations
# Authenticate the app installation in order to run API operations
authenticate_installation(@payload)
end


post '/event_handler' do

# # # # # # # # # # # # # # # # # # #
# ADD YOUR CODE HERE #
# # # # # # # # # # # # # # # # # # #
case request.env['HTTP_X_GITHUB_EVENT']
when 'issues'
if @payload['action'] === 'opened'
handle_issue_opened_event(@payload)
end
end

'ok' # We've got to return _something_. ;)
200 # success status
end


helpers do

# # # # # # # # # # # # # # # # # # #
# ADD YOUR HELPERS METHODS HERE #
# # # # # # # # # # # # # # # # # # #
# When an issue is opened, add a label
def handle_issue_opened_event(payload)
repo = payload['repository']['full_name']
issue_number = payload['issue']['number']
@installation_client.add_labels_to_an_issue(repo, issue_number, ['needs-response'])
end

# Saves the raw payload and converts the payload to JSON format
def get_payload_request(request)
Expand All @@ -95,9 +76,9 @@ def get_payload_request(request)
end

# Instantiate an Octokit client authenticated as a GitHub App.
# GitHub App authentication equires that we construct a
# GitHub App authentication requires that you construct a
# JWT (https://jwt.io/introduction/) signed with the app's private key,
# so GitHub can be sure that it came from the app an not altererd by
# so GitHub can be sure that it came from the app and was not altered by
# a malicious third party.
def authenticate_app
payload = {
Expand All @@ -111,32 +92,32 @@ def authenticate_app
iss: APP_IDENTIFIER
}

# Cryptographically sign the JWT
# Cryptographically sign the JWT.
jwt = JWT.encode(payload, PRIVATE_KEY, 'RS256')

# Create the Octokit client, using the JWT as the auth token.
@app_client ||= Octokit::Client.new(bearer_token: jwt)
end

# Instantiate an Octokit client authenticated as an installation of a
# GitHub App to run API operations.
# Instantiate an Octokit client, authenticated as an installation of a
# GitHub App, to run API operations.
def authenticate_installation(payload)
installation_id = payload['installation']['id']
installation_token = @app_client.create_app_installation_access_token(installation_id)[:token]
@installation_client = Octokit::Client.new(bearer_token: installation_token)
@installation_id = payload['installation']['id']
@installation_token = @app_client.create_app_installation_access_token(@installation_id)[:token]
@installation_client = Octokit::Client.new(bearer_token: @installation_token)
end

# Check X-Hub-Signature to confirm that this webhook was generated by
# GitHub, and not a malicious third party.
#
# GitHub will the WEBHOOK_SECRET, registered
# to the GitHub App, to create a hash signature sent in each webhook payload
# in the `X-HUB-Signature` header. This code computes the expected hash
# signature and compares it to the signature sent in the `X-HUB-Signature`
# header. If they don't match, this request is an attack, and we should
# reject it. GitHub uses the HMAC hexdigest to compute the signature. The
# `X-HUB-Signature` looks something like this: "sha1=123456"
# See https://developer.github.com/webhooks/securing/ for details
# GitHub uses the WEBHOOK_SECRET, registered to the GitHub App, to
# create the hash signature sent in the `X-HUB-Signature` header of each
# webhook. This code computes the expected hash signature and compares it to
# the signature sent in the `X-HUB-Signature` header. If they don't match,
# this request is an attack, and you should reject it. GitHub uses the HMAC
# hexdigest to compute the signature. The `X-HUB-Signature` looks something
# like this: "sha1=123456".
# See https://developer.github.com/webhooks/securing/ for details.
def verify_webhook_signature
their_signature_header = request.env['HTTP_X_HUB_SIGNATURE'] || 'sha1='
method, their_digest = their_signature_header.split('=')
Expand All @@ -145,17 +126,17 @@ def verify_webhook_signature

# The X-GITHUB-EVENT header provides the name of the event.
# The action value indicates the which action triggered the event.
logger.debug "---- recevied event #{request.env['HTTP_X_GITHUB_EVENT']}"
logger.debug "---- received event #{request.env['HTTP_X_GITHUB_EVENT']}"
logger.debug "---- action #{@payload['action']}" unless @payload['action'].nil?
end

end


# Finally some logic to let us run this server directly from the commandline, or with Rack
# Don't worry too much about this code ;) But, for the curious:
# Finally some logic to let us run this server directly from the command line,
# or with Rack. Don't worry too much about this code. But, for the curious:
# $0 is the executed file
# __FILE__ is the current file
# If they are the same—that is, we are running this file directly, call the Sinatra run method
# If they are the same—that is, we are running this file directly, call the
# Sinatra run method
run! if __FILE__ == $0
end
Loading