Download the PHP package namelesscoder/gizzle without Composer
On this page you can find all versions of the php package namelesscoder/gizzle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package gizzle
Gizzle
A tiny GitHub Webhook Listener written in PHP which can be easily extended with plugins.
Installing Gizzle
Run:
Assuming your project uses the composer class loader you then have access to using Gizzle's classes in your project.
Virtual Host
If you wish to use the default receiving script, symlink the web
folder from Gizzle:
And add a (publicly accessible!) virtual host for your favorite HTTP server pointing to ./web/
inside this folder or simply include the package in your own composer application.
Finally, configure your GitHub repository and add the URL to your Gizzle project pointing to the file /github-webhook.php
as requested through HTTP (file web/github-webhook.php
in filesystem) or configure your virtual host to serve the github-webhook.php
filename as default file.
You can of course create your own receiving script instead of github-webhook.php
if you need additional features.
Note! the default receiving script only supports the features described in this README file.
Security (secret file and personal token)
Gizzle uses the secret
(a token) which you enter in GitHub while setting up the web hook. Use the same secret token when initializing your Payload class. The secret token is required and must match, or the Payload throws a RuntimeException.
When using the shipped public file in ./web/github-webhook.php
your secret token will be read from the file ./.secret
(note: dot-file, placed outside the public web root). When first installing this package or your own package which uses this package, create the file whichever way you prefer and make sure it contains your "secret" key from GitHub. For example, using the shell:
Gizzle can also use a personal access token to "talk back" to GitHub. You can utilize this to do things like comment on the commit and update it with a status (which is displayed when the commit is part of a pull request) - and even automatically merge branches. The default implementation for every plugin will automatically update the commit status with pending, success or error as the Payload execution progresses, but other plugins can access the GitHub API from Gizzle plugins. All you have to do is make sure a special .token
file exists and contains your personal access token - which, like the .secret
file - is sensitive information you should never share. The .token
file is created the exact same way as the .secret
file and you can obtain a new access token to use just with Gizzle. Read more about how in the section about the GitHub API integration.
Running Gizzle
The ./web/github-webhook.php
file which is shipped with this repository can be used as URL of your web hook when configuring it in GitHub - or you can manually process the payload from within your own application and use its URL instead:
Configuring plugins
Not all plugins support configuration but those that do can be configured by placing a Settings.yml
file in the root of your project or in any folder leading up to the file which creates the Payload instance. The first file found in a reverse search gets used and should contain all the configuration required by your plugins.
The format of the configuration file is:
It is possible for any Package to return Plugins from other packages as well - which is ideal when constructing Packages which include and provide configuration for many plugins. Such a package might return plugin names which don't belong to that Package, and because the class name is used as key in the configuration, it is possible to configure the same Plugin differently when it is provided by Package A and Package B. This is done to increase reusability of smaller plugins which can serve many purposes - a good example would be a Git plugin which pushes when used in one package and pulls when used in another, because each package can provide a different configuration for the same plugin.
Word of caution, though: the plugin can only be configured inside the scope of the Package that returned it. This means that every mandatory option should be present in each place the Plugin is used (in other words: you cannot configure global defaults).
Plugin events
Gizzle provides you with a way to configure for each plugin, a list of other plugins which should be executed with the same payload on special events. Currently supported is the onStart
, onSuccess
and onError
events. There are two ways of configuring events; one is from inside your custom plugin classes by providing a default value for the corresponding setting (you can do so in the initialize()
or getSetting
methods whichever you prefer) - the other way is defining these settings in your configuration file. If providing these as a default setting value from your custom plugin simply return the exact same array that is expressed for each event in this example.
About the example: the illustration configures the self-update plugin which runs git pull
and composer install
in the root folder of the package that implemented it in its configuration. The sub-plugin that's also used is a ficticious plugin which sends an email to the entity pushing the commit described in the payload with cc: or bcc: defined by settings for that plugin and another also ficticious plugin which is able to send a message to syslog
with a custom severity. It also demonstrates how you can use this in as many nested levels as you wish, catching events at each level; in this case causing the "CRITICAL!" syslog message if self-update failed and either the pusher or the master dev-op could not be notified of the failure directly.
Gizzle's own few settings can be configured in each settings file by under the NamelessCoder\Gizzle
scope.
Note that the events are defined on the same level as any other setting a plugin uses which means the names are reserved and cannot be used as names for your own plugin settings. Defining an incorrect value (something not an array) may cause exceptions. However: contrary to how "root" plugins report errors, exceptions which are raised by a plugin that is used as event listener will simply add a friendly error message to the response, allowing the next plugin to continue, and so on. Whereas a "root" plugin would cause the entire Payload to stop processing.
Multiple configurations
You can create any number of alternative configuration files. The name or path of the settings file can be provided manually when instanciating the Payload:
You can then place any number of such additional settings files as ./Settings/*.yml
and reference them by changing the third parameter.
The default implementation that ships with Gizzle - the github-webhook.php
file inside ./web/
- uses the $_GET['settings']
argument as third parameter after validating that it contains the only allowed characters: a-z
, A-Z
, 0-9
and /
. The latter is allowed in order to let you divide configuration files into any number of sub directories and reference them by path. Additionally, the file name itself must end with .yml
, must not be a hidden file (dot-file) and finally must be a relative path (e.g. not starting with /
).
To select which configuration gets used simply set the expected GET parameter when adding the URL as webhook in GitHub's repository settings:
And you can specify multiple settings files which must all be processed by specifying arguments as an array:
Which causes first ./Settings/SpecialSettings.yml
and then ./Settings/OtherSettings.yml
to be processed in the same execution. Any error caused by one will cause the job to exit, throwing only the first error which occurred.
You can also use this to version your settings. If for example your design practices change and you require support for more than one repository design pattern, you can easily store the legacy configuration as a different settings file and by modifying the web hook URL in each repository, support both of your repositories' patterns simultaneously. A good example of when such versioning might become necessary is when switching to/from the "git flow" pattern or in multiple production branch scenarios where new production branches are continuously added and removed.
Usage of API from plugins
The API can be accessed via the $payload
argument that is provided to the essential methods on plugins. To access the API:
Every resource type in Github has its own URL and some resource types support different parameters depending on the context - for example, a Github "comment" can be created in several different contexts and each has requirements for required properties. Study the Github API v3 developer documentation for the details about each resource.
Note the additional decoding step which is required when you need to read data from the response. An stdClass
is returned with public properties allowing you to read response data - see the official GitHub v3 API reference for available data for each action. If you need another data type, manually use json_decode($response->getContent());
and pass any special JSON_*
options you require.
Tip: If the API returns data which contains for example commits, repositories, entities etc. and the data type is
array
, Gizzle's domain model supports mapping such data (recursively) by simply passing the array of properties into the constructor:$commit = new Commit($commitDataAsArray);
.
Updating commit status
If you wish to make Gizzle update the HEAD commit of the Payload as it gets processed (one status per settings file that processes the Payload), Gizzle supports a GitHub personal access token which, like the .secret
file, is placed in the project root folder and is named .token
. A token is a 32-character string with randomized letters and numbers.
To obtain a personal access token:
- In GitHub, under account settings, generate yourself a new Access Token.
- Make sure you associate this token with at least permissions to access status and access (public) repositories. If any plugins require additional permissions, each should document which - and you should add permissions as required.
- Copy the token and insert it in the file
.token
in the project root folder. - Do not commit the token file! Instead, add it to git ignore either locally or for your project. The token is sensitive information and should never be shared.
When present, the token is read from this file and used to initialize the GitHub API used by Gizzle. The API can then be used by Gizzle and Gizzle plugins to perform any action which you permit.
Additional resources for using the Github API:
- Access Token generation in GitHub account
- Documentation for every possible action you can perform through this GitHub API
- Documentation for how to use the PHP
Api
class to access the GitHub API
Adding comments to Commits or Pull Requests (or files therein)
Gizzle includes a simple Message object which can be used to send comments to GitHub and attach them to a commit or a pull request, or a line in a specific file in a commit or pull request. Contrary to manually dispatching such messages using the GitHub API (which is still possible!), using the Message object allows you to control the flow of messages and avoids duplicate comments. For example, Messages become spooled and you can set a maximum allowed number of Messages in the spool in order to prevent flooding. If the limit is exceeded (for the current Payload and regardless of how many settings files are being processed), Gizzle will replace all spooled Messages with a single Message informing that there were too many items to report, along with a summary of the Messages that were generated.
Although these Messages allow an almost instant feedback to the user who for example creates a pull request, it is not intended to be a replacement for a continuous integration solution. The ideal type of feedback to return using Messages is formalities checking: commit message style, pull request composition (e.g. warning to squash all commits) and brief code style checking. Note that access to the actual contents of files requires reading the contents of each file either from a manually checked out repository or using GitHub's raw data URLs (which you manually download to a temporary file if the utility you use does not support URL targets for checking).
You can utilise this simple way to provide almost instant feedback on formalities for users who submit pull requests through GitHub's web interface. However: you should consider fitting your repository with actual git hooks which perform these checks before the user pushes to remotes or even creates a commit. Such hooks are not processed when pull requests are created through GitHub's web interface - but it does provide excellent and instant feedback to any user making changes locally in a cloned repository.
For an example of how such hooks can be implemented, see https://github.com/FluidTYPO3/fluidtypo3-development which contains examples for validating the commit message, running unit tests and style checks and contains a script which maintains the hooks (instead of requiring the user to copy each one when they're updated).
To create and dispatch a new Message that ends up as a comment on GitHub, all you need is access to the Payload
instance:
And to create a more general comment that gets added to the "Discussion" tab in GitHub's pull request view:
And finally, if both a pull request and commit are specified, Gizzle assumes your intention is to create an inline review comment for that commit in the pull request:
The spooled Message instances are then processed last, after all plugins have finished executing.
Creating plugins
To create a plugin for Gizzle you need one mandatory class and optionally a lister class (which gets shared by all plugins in your package):
- The class
MyVendor\MyPackage\GizzlePlugins\MyAwesomePlugin
(or another class name or namespace location - your choice) which implementsNamelessCoder\Gizzle\PluginInterface
and the methods it specifies. - Optionally also the class
MyVendor\MyPackage\GizzlePlugins\PluginList
which must implement interfaceNamelessCoder\Gizzle\PluginListInterface
and contain agetPluginClassNames
method which returns an array of any number of string class names of your plugins. This class gets used when users reference your plugin collection using your package name - it is not required if your plugins are solely intended to be used from aSettings.yml
context (as described above).
When users load your plugins by package name, your PluginList class is asked to return the class names of plugins and it is here you have your option to change which class names are returned for example depending on configuration. However, when users implement your plugin directly in Settings.yml
or by manually instanciating it, your PluginList class does not get used. This means that if your plugin only should be used directly from settings or manually inside other plugins, you do not need the PluginList class - which is why it is marked optional.