Wrappers and webservice to handle GitHub Webhook requests.


Keywords
git, github, github-webhooks, microservice, php-library, php7, rest-api, restful-api, webservice
Licenses
MIT/GPL-3.0+

Documentation

Webhook

Github Webhook client receiver wrappers and webservice.

Webhook Project Homepage

Description

The Webhook Project facilitates workflow integration of Github Webhook requests. It provides class wrappers for existing projects and an optional end-point installer script for a self-contained solution that is easy to deploy.

Requirements

  • PHP 7.2 or higher

Usage

End-point Installer Script

The command-line script bin/add-endpoint.php creates a webservice end-point that responds to a Github Webhook for the PushEvent on a remote repository by updating a local repository and to a PingEvent by displaying a success message.

The simplest way to prepare the end-point installer is to copy this project somewhere and run Composer:

git clone https://github.com/katmore/webhook.git 
cd webhook
composer update

The installer can be invoked without any arguments; it will prompt for all the required parameters (such as the remote URL, local repo path, webhook secret, etc.):

php bin/add-endpoint.php

The --help switch will provide details on more advanced usage (such as quiet and non-interactive modes).

php bin/add-endpoint.php --help

Wrapper Classes

To use this project's wrapper classes within your existing project, the main topics of focus will be the Webhook\Request class and Payload objects. As a recomended first step, add a dependancy using Composer to your existing project:

composer require katmore/webhook

The Webhook\Request class facilitates interpreting the message body and related HTTP headers of a Github Webhook request. The Webhook\Request class constructor will instantiate and populate a Webhook\Payload child class having a class name that corresponds to the Webhook "Event Type": it searches for the existence of a class having the same "short name" as the GitHub Event Type within the namespace Webhook\Payload. If a thusly named Webhook\Payload child class is not defined for a particular event; the Webhook\Payload\Event class is used by default. For example, a Webhook\Payload\PushEvent object is created and populated for a PushEvent Webhook request.

The Payload object as populated by the Webhook\Request constructor is available using the Webhook\Request::getPayload() method as detailed in the example below:

/*
 * the 'Secret' field corresponding to the expected Webhook request
 */
$hubSecret = "My Secret";

/*
 * obtain the messageBody; in this case, by reading from the php input stream
 */
$messageBody = file_get_contents('php://input');

/*
 * obtain the 'hubSignature'; for example, from the value of the HTTP header 'HTTP_X_HUB_SIGNATURE'
 */
$hubSignature = $_SERVER['HTTP_X_HUB_SIGNATURE'];

/*
 * obtain the 'gitHubEvent'; for example, from the value of the HTTP header 'HTTP_X_GITHUB_EVENT'
 */
$gitHubEvent = $_SERVER['HTTP_X_GITHUB_EVENT'];

/*
 * instiantate a Webhook\Request object...
 */
$request = new \Webhook\Request($messageBody, $hubSignature, $gitHubEvent);

/*
 * validate the request signature
 */
$request->validateSignature($hubSecret);

/*
 * get the payload object...
 * For more info on payloads for the various github events, see:
 *    https://developer.github.com/v3/activity/events/types
 */
$payload = $request->getPayload();

/*
 * The payload object will be an instance of the 
 *    \Webhook\Payload\PushEvent class
 *    if the github event was a 'Push Event'.
 *  
 * The payload object will be an instance of the 
 *    \Webhook\Payload\PingEvent class
 *    if the github event was a 'Ping Event'.
 *
 * The payload object will be an instance of the 
 *    \Webhook\Payload\Event class
 *    for all other events.
 */
var_dump($payload);

Validating a request's "Hub Signature"

At some point in the handling of a Webhook request it is critical that the "Hub Signature" be validated against the shared "Secret" for obvious security reasons. The end-point installer and end-point example both accomplish this by using the Request::validateSignature() method of the Webhook\Request class.

/*
 * the 'Secret' field corresponding to the expected Webhook request
 */
$hubSecret = "My Secret";

/*
 * obtain the messageBody, hubSignature, and gitHubEvent
 */
$messageBody = file_get_contents('php://input');
$hubSignature = $_SERVER['HTTP_X_HUB_SIGNATURE'];
$gitHubEvent = $_SERVER['HTTP_X_GITHUB_EVENT'];

/*
 * instiantate a 'Request' object...
 */
$request = new \Webhook\Request($messageBody, $hubSignature, $gitHubEvent);

try {
  /*
   * validate the request signature
   */
  $request->validateSignature($hubSecret);
} catch(\Webhook\InvalidRequest $e) {
   /*
    * force a 500 HTTP response code upon encountering an 'InvalidRequest' exception,
    */
   http_response_code(500);
   echo "Invalid Request: ".$e->getMessage();
   return;
}

/*
 * continue to do more things...
 */
//$payload = $request->getPayload();

Using the provided end-point example

An end-point example is provided at web/endpoint-example.php which responds to a PushEvent by invoking a 'git pull' or any custom code placed in a callback function, as configured. It also responds to a a PingEvent with a success message.

cp web/endpoint-example.php web/my-repo-endpoint.php
  • edit to specify configuration...

    • change the value of $config['RepoUrl'] to your GitHub repository URL:
    $config['RepoUrl'] = 'https://github.com/my-organization/my-repo';
    • change the value of $config['Secret'] to the "Secret" configured in Github for the webhook:
    $config['Secret'] = 'My Secret';
    • leave the value of $config['RepoPath'] empty to skip the repo update, or change it to the local system path of a repository to perform a git update on every 'push' Webhook event:
    $config['RepoPath'] = '';
    //$config['RepoPath'] = '/path/to/my/repo';
    • place any custom code within the onPushEvent function that should be executed on every 'push' Webhook event
    function onPushEvent(Payload\PushEvent $payload) {
        /*
         *
         * --- place code in this function --
         * --- that should execute when a Github 'push' event occurs --
         *
         */
         //
         // place your custom code here
         //
    }

Unit Tests

To perform unit tests, execute phpunit located in the vendor/bin directory.

vendor/bin/phpunit

The tests.sh wrapper script is provided for convenience.

./tests.sh

Legal

"Webhook" is distributed under the terms of the MIT license or the GPLv3 license.

Copyright (c) 2016-2018, Doug Bird. All rights reserved.