|
| 1 | +require 'sinatra' |
| 2 | +require 'jwt' |
| 3 | +require 'json' |
| 4 | +require 'active_support/all' |
| 5 | +require 'octokit' |
| 6 | + |
| 7 | +begin |
| 8 | + GITHUB_APP_ID = ENV.fetch("GITHUB_APP_ID") |
| 9 | + GITHUB_PRIVATE_KEY = ENV.fetch("GITHUB_APP_PRIVATE_KEY") |
| 10 | +rescue KeyError |
| 11 | + $stderr.puts "To run this script, please set the following environment variables:" |
| 12 | + $stderr.puts "- GITHUB_APP_ID: GitHub App ID" |
| 13 | + $stderr.puts "- GITHUB_APP_PRIVATE_KEY: GitHub App Private Key" |
| 14 | + exit 1 |
| 15 | +end |
| 16 | +@client = nil |
| 17 | + |
| 18 | +# Webhook listener |
| 19 | +post '/payload' do |
| 20 | + github_event = request.env['HTTP_X_GITHUB_EVENT'] |
| 21 | + if github_event == "installation" |
| 22 | + parse_installation_payload(request.body.read) |
| 23 | + else |
| 24 | + puts "New event #{github_event}" |
| 25 | + end |
| 26 | +end |
| 27 | + |
| 28 | +# To authenticate as a GitHub App, generate a private key. Use this key to sign |
| 29 | +# a JSON Web Token (JWT), and encode using the RS256 algorithm. GitHub checks |
| 30 | +# that the request is authenticated by verifying the token with the |
| 31 | +# integration's stored public key. https://git.io/vQOLW |
| 32 | +def get_jwt_token |
| 33 | + private_key = OpenSSL::PKey::RSA.new(GITHUB_PRIVATE_KEY) |
| 34 | + |
| 35 | + payload = { |
| 36 | + # issued at time |
| 37 | + iat: Time.now.to_i, |
| 38 | + # JWT expiration time (10 minute maximum) |
| 39 | + exp: 5.minutes.from_now.to_i, |
| 40 | + # GitHub App's identifier |
| 41 | + iss: GITHUB_APP_ID |
| 42 | + } |
| 43 | + |
| 44 | + JWT.encode(payload, private_key, "RS256") |
| 45 | +end |
| 46 | + |
| 47 | +# A GitHub App is installed by a user on one or more repositories. |
| 48 | +# The installation ID is passed in the webhook event. This returns all |
| 49 | +# repositories this installation has access to. |
| 50 | +def get_app_repositories |
| 51 | + json_response = @client.list_installation_repos |
| 52 | + |
| 53 | + repository_list = [] |
| 54 | + if json_response.count > 0 |
| 55 | + json_response["repositories"].each do |repo| |
| 56 | + repository_list.push(repo["full_name"]) |
| 57 | + end |
| 58 | + else |
| 59 | + puts json_response |
| 60 | + end |
| 61 | + |
| 62 | + repository_list |
| 63 | +end |
| 64 | + |
| 65 | +# For each repository that has Issues enabled, create an issue stating that a |
| 66 | +# GitHub App was installed |
| 67 | +def create_issues(repositories, sender_username) |
| 68 | + repositories.each do |repo| |
| 69 | + begin |
| 70 | + @client.create_issue(repo, "#{sender_username} added new app!", "Added GitHub App") |
| 71 | + rescue |
| 72 | + puts "Issues is disabled for this repository" |
| 73 | + end |
| 74 | + end |
| 75 | +end |
| 76 | + |
| 77 | +# When an App is added by a user, it will generate a webhook event. Parse an |
| 78 | +# `installation` webhook event, list all repositories this App has access to, |
| 79 | +# and create an issue. |
| 80 | +def parse_installation_payload(json_body) |
| 81 | + webhook_data = JSON.parse(json_body) |
| 82 | + if webhook_data["action"] == "created" || webhook_data["action"] == "added" |
| 83 | + installation_id = webhook_data["installation"]["id"] |
| 84 | + |
| 85 | + # Get JWT for App and get access token for an installation |
| 86 | + jwt_client = Octokit::Client.new(:bearer_token => get_jwt_token) |
| 87 | + jwt_client.default_media_type = "application/vnd.github.machine-man-preview+json" |
| 88 | + app_token = jwt_client.create_installation_access_token(installation_id) |
| 89 | + |
| 90 | + # Create octokit client that has access to installation resources |
| 91 | + @client = Octokit::Client.new(access_token: app_token[:token] ) |
| 92 | + @client.default_media_type = "application/vnd.github.machine-man-preview+json" |
| 93 | + |
| 94 | + # List all repositories this installation has access to |
| 95 | + repository_list = [] |
| 96 | + if webhook_data["installation"].key?("repositories_added") |
| 97 | + webhook_data["installation"]["repositories_added"].each do |repo| |
| 98 | + repository_list.push(repo["full_name"]) |
| 99 | + end |
| 100 | + else |
| 101 | + # Get repositories by query |
| 102 | + repository_list = get_app_repositories |
| 103 | + end |
| 104 | + |
| 105 | + # Create an issue in each repository stating an App has been given added |
| 106 | + create_issues(repository_list, webhook_data["sender"]["login"]) |
| 107 | + end |
| 108 | +end |
0 commit comments