katmore / webhook
Wrappers and webservice to handle GitHub Webhook requests.
Requires
- php: >=7.2.1
- ext-hash: *
- ext-json: *
Requires (Dev)
- php-di/phpdoc-reader: ^2.1
- phpunit/php-code-coverage: ^6.0
- phpunit/phpunit: ^7
README
Github Webhook client receiver wrappers and webservice.
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.
- copy the provided web/endpoint-example.php...
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 agit 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 // }
- change the value of
Unit Tests
coverage.txt
: unit test coverage reportphpunit.xml
: PHPUnit configuration filetests/phpunit
: source code for 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.