class GithubSnapBuilder::Application
Constants
- APP_IDENTIFIER
The GitHub App's identifier (type integer) set when registering an app.
- PRIVATE_KEY
Converts the newlines. Expects that the private key has been set as an environment variable in PEM format.
- WEBHOOK_SECRET
Your registered app must have a secret set. The secret is used to verify that webhooks are sent by GitHub.
Public Instance Methods
Instantiate an Octokit client authenticated as a GitHub App. GitHub App authentication requires that you construct a JWT (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 a malicious third party.
# File lib/github_snap_builder/server.rb, line 160 def authenticate_app payload = { # The time that this JWT was issued, _i.e._ now. iat: Time.now.to_i, # JWT expiration time (10 minute maximum) exp: Time.now.to_i + (10 * 60), # Your GitHub App's identifier number iss: APP_IDENTIFIER } # 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.
# File lib/github_snap_builder/server.rb, line 181 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) end
Saves the raw payload and converts the payload to JSON format
# File lib/github_snap_builder/server.rb, line 142 def get_payload_request(request) # request.body is an IO or StringIO object # Rewind in case someone already read it request.body.rewind # The raw text of the body is required for webhook signature verification @payload_raw = request.body.read begin @payload = JSON.parse @payload_raw rescue => e fail "Invalid JSON (#{e}): #{@payload_raw}" end end
# File lib/github_snap_builder/server.rb, line 83 def handle_pull_request_updated_event(payload) pull_request = payload['pull_request'] repo = pull_request['base']['repo']['full_name'] head_url = pull_request['head']['repo']['html_url'] base_url = pull_request['base']['repo']['html_url'] commit_sha = pull_request['head']['sha'] pr_number = pull_request['number'] repo_config = CONFIG.repo(repo) || raise("Config missing repo definition for '#{repo}'") channel = repo_config.channel token = repo_config.token || raise("'#{repo}' config missing token") relative_logfile_path = File.join repo, "#{commit_sha}.log" logfile_path = File.join(logdir, relative_logfile_path) FileUtils.mkpath File.dirname(logfile_path) FileUtils.chmod_R 0755, File.dirname(logfile_path) build_logger = Logger.new(logfile_path) log_url = request.url.gsub 'event_handler', "logs/#{relative_logfile_path}" status_reporter = GithubStatusReporter.new(@installation_client, repo, commit_sha, log_url) begin begin status_reporter.pending("Currently building a snap...") builder = SnapBuilder.new(build_logger, base_url, head_url, commit_sha, CONFIG.build_type) logger.info "Building snap for '#{repo}'" snap_path = builder.build rescue Error => e logger.error "Failed to build snap: #{e.message}" status_reporter.error("Snap failed to build.") return end begin status_reporter.pending("Currently uploading/releasing snap...") full_channel = "#{channel}/pr-#{pr_number}" logger.info "Pushing and releasing snap into '#{full_channel}'" builder.release(snap_path, token, full_channel) rescue Error => e logger.error "Failed to push/release snap: #{e.message}" status_reporter.error("Snap failed to upload/release.") return end logger.info 'Built and released snap, all done' status_reporter.success("Snap built and released to '#{full_channel}'") rescue => e logger.error "Unknown error: #{e.message}" status_reporter.error("Encountered an error.") raise ensure if !snap_path.nil? and File.file? snap_path File.delete snap_path end end end
# File lib/github_snap_builder/server.rb, line 210 def logdir ENV.fetch("SNAP_BUILDER_LOG_DIR", "log") end
# File lib/github_snap_builder/server.rb, line 214 def logfile_path(repo, commit_sha) end
Check X-Hub-Signature to confirm that this webhook was generated by GitHub, and not a malicious third party.
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 developer.github.com/webhooks/securing/ for details.
# File lib/github_snap_builder/server.rb, line 198 def verify_webhook_signature their_signature_header = request.env['HTTP_X_HUB_SIGNATURE'] || 'sha1=' method, their_digest = their_signature_header.split('=') our_digest = OpenSSL::HMAC.hexdigest(method, WEBHOOK_SECRET, @payload_raw) halt 401 unless their_digest == our_digest # The X-GITHUB-EVENT header provides the name of the event. # The action value indicates the which action triggered the event. logger.debug "---- received event #{request.env['HTTP_X_GITHUB_EVENT']}" logger.debug "---- action #{@payload['action']}" unless @payload['action'].nil? end