Hooker is a lightweight PHP web application that can be used to trigger remote workflows on your Linux or UNIX based servers. It is fully self-contained by design (a single script), it doesn't have any external dependencies and does not require any package managers such as Composer making it perfect for inclusion into legacy projects too.
It has specifically been designed to simplify and automate application deployments using Git or Docker containers when you don't want or need the complexity of a full CI/CD setup, but you can easily use it for a ton of other really useful tasks.
- A web server (the installation guide uses Nginx)
- PHP 5.4+
- The
shell_exec()
function is required (Some shared hosting environments disable this!)
This script is released under the GPLv2 license. Feel free to use it, fork it, improve it and contribute by open a pull-request!
It is recommended to set-up a new virtual host configuration on your web server of which then acts as a web-hook endpoint for multiple configured projects.
You should create separate site configurations that then get triggered by specifying the site/application configuration
with the app
parameter eg. https://deploy.mysite.com/hooker.php?app=website1
.
If you have set up your server using Conductor you can automatically install Hooker by running this simple command:
bash -c "$(curl -fsSL https://raw.githubusercontent.com/allebb/hooker/stable/utils/auto-install-conductor.sh)"
Hooker is fully self-contained in a single script (it doesn't have any external dependency and therefore doesn't't require the need to pull dependencies from Composer or
include
a ton of external scripts) so if you wish to include this single script into an existing project simple copy thehooker.php
file and update the default values within this file to your needs.
In this example, we'll create a new Nginx vhost configuration on an Ubuntu Linux server and use the default www-data
user (for simplicity and demonstration purposes).
First we need to create a hosting directory to host our hooker.php
file:
sudo -u www-data mkdir /var/www/hooker
We will now create a cache directory for Composer, this will speed up package installs (if you intend to use it):
sudo mkdir /var/www/.cache
sudo chown -R www-data:www-data /var/www/.cache
We'll use Git to download the latest (stable) version (we'll also be able to use sudo -u www-data git pull
in future
to apply updates):
cd /var/www/hooker
sudo -u www-data git clone https://github.com/allebb/hooker.git .
sudo -u www-data git checkout stable
We'll now copy the example configuration file and use that to configure our individual sites:
sudo -u www-data cp hooker.conf.example.php hooker.conf.php
At this point you should edit this file and configure your sites, for example it may look like this:
/var/www/hooker/hooker.conf.php
/**
* Hooker Configuration File
*/
return [
// Enable output of debugging information.
'debug' => true,
// Need to customise the default Git path?
// 'git_bin' => '/usr/bin/git',
// Need to customise the default PHP version used for running Composer and other PHP specific tasks?
// ** This path can also be overridden by individual sites/applications as required in the sites array below. **
//'php_bin' => '/usr/bin/php7.4',
// Need to customise the default Composer path?
// ** This path can also be overridden by individual sites/applications as required in the sites array below. **
//'composer_bin' => '/usr/bin/composer',
// By default we'll allow any server(s) to "hit" these deployment hooks but you can add specific IP addresses
// to the whitelist if you wish...
'ip_whitelist' => [],
'sites' => [
// An example basic HTML website (Webhook example: http://deploy.mysite.com/hooker.php?app=my_basic_website&key=SomeRandomWordThatMustBePresentInTheKeyParam).
'my_example_website' => [
'debug' => false, // Override and disable the output of debug info for this specific deployment workflow?
'key' => 'SomeRandomStringThatMustBePresentInTheKeyParam',
'local_repo' => '/var/www/html-website', // Use current directory
'is_github' => true, // Using GitHub webhooks to trigger this workflow.
'branch' => 'deploy-prod', // As long as the GitHub webhook request relates to changes on this git branch, we'll run the deployment workflow!
//'pre_commands' => [
// // Uses the default (inherited deployment commands)
//],
//'deploy_commands' => [
// // Uses the default (inherited deployment commands eg. cd {{local-repo}} && {{git-bin}} reset --hard HEAD && {{git-ssh-key}}{{git-bin}} pull)
//],
//'post_commands' => [
// // Uses the default (inherited deployment commands)
//],
],
// An example Laravel Deployment Configuration (Webhook example: http://deploy.mysite.com/hooker.php?app=my_other_website&key=32c9f55eea8526374731acca13c81aca)
'my_other_website' => [
'key' => '32c9f55eea8526374731acca13c81aca',
'local_repo' => '@conductor', // This will auto-resolve to /var/conductor/applications/my_other_website
'git_ssh_key_path' => '@conductor', // Optional - This will auto-resolve and use the Conductor generated private (deployment) key at /var/www/.ssh/my_other_website.deploykey
'user' => false,
'php_bin' => '/usr/bin/php8.0',
// Override the "default" PHP version used for this deployment/running Composer, this application needs PHP 8.0!
//'composer_bin' => '/usr/bin/composer', // Need to override with a different Composer version?
'pre_commands' => [
'{{php-bin}} {{local-repo}}/artisan down', // Example of a pre-command to set our Laravel application into "maintenance mode".
'{{php-bin}} {{local-repo}}/artisan config:clear', // We'll also clear the configuration cache before we pull the latest code from Git..
],
//'deploy_commands' => [
// // Uses the default (inherited deployment command eg. cd {{local-repo}} && {{git-bin}} reset --hard HEAD && {{git-bin}} pull)
// // You can of course run other tasks here too, shell scripts, npm, nodejs etc. etc.
//],
'post_commands' => [
'cd {{local-repo}} && {{php-bin}} {{composer-bin}} install --no-dev --no-progress --prefer-dist --optimize-autoloader',
'chmod 755 {{local-repo}}/storage',
'{{php-bin}} {{local-repo}}/artisan migrate --force',
'{{php-bin}} {{local-repo}}/artisan config:cache',
'{{php-bin}} {{local-repo}}/artisan cache:clear',
'{{php-bin}} {{local-repo}}/artisan route:cache',
'{{php-bin}} {{local-repo}}/artisan up',
//'{{php-bin}} {{local-repo}}/artisan queue:restart', // Using a job queue? Restart it so it uses the latest version of your code!
//'{{php-bin}} {{local-repo}}/artisan horizon:terminate', // Using Horizon for your queues instead??
],
],
// An example Laravel Deployment Configuration using a local ".hooker.json" repository configuration. (Webhook example: http://deploy.mysite.com/hooker.php?app=another_application&key=VgUjbEIPbOCpiRQa2UHjqiXcmbE8eIht)
'another_application' => [
'key' => 'VgUjbEIPbOCpiRQa2UHjqiXcmbE8eIht',
'local_repo' => '/var/www/another_application',
'git_ssh_key_path' => '/var/www/.ssh/id_rsa', // Optional - We can set a private (deployment key) that will be used when git makes requests.
'use_json' => 'true', // This will read the configuration from a .hooker.json file stored in your git repo. eg. /var/www/another_application/.hooker.json
],
],
];
This section is optional and only required if you need to pull from private repositories and don't wish to generate deployment keys on an application by application basis (using conductor genkey {app_name}
or manually).
Let's change into the www-data
user's home directory:
cd /var/www
We will now create a new .ssh
directory (profile) for the 'www-data' user (as it is this user account that will be,
behind the scenes connecting to Git) and set the required permissions.
mkdir .ssh
chown www-data:www-data -R .ssh
chmod 0700 .ssh
Now we'll generate a new SSH key-pair for the www-data
user of which will be used to authenticate with your Git
hosting service.
In order to enable headless operation ensure that you use the default options (just keep pressing the ENTER key at the prompts) and when asked to enter a passphrase ensure that you leave it empty otherwise Hooker will not work correctly!
sudo -u www-data ssh-keygen -t rsa -b 4096
The contents of the public key (/var/www/.ssh/id_rsa.pub
) now needs to be copied and added to your Git hosting
provider's SSH keys section:
cat /var/www/.ssh/id_rsa.pub
Now, we need to set the correct ownership and permissions for this new site:
chown www-data:www-data -R /var/www/hooker
This example Nginx virtualhost configuration can be added to your server - assuming you're using Nginx and PHP7.0-FPM ( just make adjustments as required):
/etc/nginx/sites-available/hooker.conf
server {
listen 80;
# Wish to secure and host your deployment web service over HTTPS using a LetsEncrypt SSL certificate?
#listen 443 ssl;
#ssl_certificate /etc/letsencrypt/live/{your_certificcate_name}/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/live/{your_certificcate_name}/privkey.pem;
#ssl_trusted_certificate /etc/letsencrypt/live/{your_certificcate_name}/chain.pem;
# Recommendations from https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html
#ssl_protocols TLSv1.2 TLSv1.3;
#ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384';
#ssl_prefer_server_ciphers on;
#ssl_session_cache shared:SSL:10m;
root /var/www/hooker;
server_name {your_domain};
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri /index.php =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
Once created, you will need to symlink it to the /etc/nginx/sites-enabled
directory like so:
cd /etc/nginx/sites-enabled
ln -s ../sites-available/hooker.conf .
Now restart Nginx for the new virtualhost configuration to take effect:
sudo service nginx restart
If all goes well, you should be able to access the 'ping' test page at: http://deploy.mysite.com/hooker.php?ping
, a
successful installation should return the word 'PONG'.
To update the version of Hooker simple run the following command(s):
cd /var/www/hooker
sudo -u www-data git pull
Remember to check and update your server's hooker.conf.php
and if you are using them in your code repositories, your local .hooker.json
files with any new configuration
options, an overview of the "Configuration options" can be found in the next section.
A full list and explanation of the configuration items and workflow "placeholders" tags can be found in the Configuration Items file.
Instead of having to edit and update the hooker.conf.php
on the server each time you wish to make a change to the deployment
workflow, a .hooker.json
file can be committed to your Git repository and will be used to define the workflow steps,
the syntax is as follows:
This example demonstrates the deployment of a Laravel web application
{
"debug": true,
"php_bin": "/usr/bin/php8.0",
"composer_bin": "/usr/bin/composer",
"pre_commands": [
"{{php-bin}} {{local-repo}}/artisan down",
"{{php-bin}} {{local-repo}}/artisan config:clear"
],
"#deploy_commands": [],
"post_commands": [
"cd {{local-repo}} && {{php-bin}} {{composer-bin}} install --no-dev --no-progress --prefer-dist --optimize-autoloader",
"chmod 755 {{local-repo}}/storage",
"{{php-bin}} {{local-repo}}/artisan migrate --force",
"{{php-bin}} {{local-repo}}/artisan config:cache",
"{{php-bin}} {{local-repo}}/artisan cache:clear",
"{{php-bin}} {{local-repo}}/artisan route:cache",
"{{php-bin}} {{local-repo}}/artisan view:cache",
"{{php-bin}} {{local-repo}}/artisan up"
]
}
Notice in the above JSON file example that the deploy_commands
JSON key has been commented out (with a hash), this
is important as an empty array here will override the default git pull
commands, only uncomment this if you need to
do custom tasks/customise the git pull command here.
Keep in mind that the Hooker webservice will first check that a local .hooker.json
file exists and then uses the
workflow steps within it, so you would have to effectively "hit" this endpoint twice for any .hooker.json
changes to
take effect as the first time it's run, it will load the local file which in turn would then pull the latest changes
from your repository and only then, on the next execution will it use the latest workflow instructions.
For this to work, your Hooker configuration file MUST specify the local_repo
and key
properties
in addition, the use_json
property must also be set to true
.
For security reasons, when using a .hooker.json
file some overrides are not available and will need to be set in the
main Hooker web service configuration file (hooker.conf.php
). These settings are: remote_repo
, branch
, local_repo
, key
, disable_init
and user
.
Generally you would simply use the webhook URL in your CI/CD environment which will then make a request to the endpoint and resulting in the deployment of your application/website on your server but for smaller projects where you don't need the complexity or overhead of full CI/CD environments you can instead use some of these services below to quickly and easily set up a fully automated deployment environment.
The following examples shows how to set up webhooks to trigger deployments from a couple of the most used Git hosting services.
Getting your sites and web applications to deploy using GitHub web hooks is super easy - You can very easily (and quickly) have your code automatically deployed to your server (or group of servers) by simply adding a GitHub web hook.
When I've needed setup quick and simple automated deployments, I'll create a separate Git branch called "deploy-prod" ( or "deploy-test") and then set up a GitHub webhook to trigger these deployments using Hooker.
If you wish to use this simple method for having your sites or applications automatically deploy ensure that you set up the GitHub webhook as follows:
In your hooker.conf.php
file make sure that you have these (is_github
and branch
) settings:
'sites' => [
'my-example-webapp' => [
...
'key' => 'MyRandomDeploymentKey',
'remote_repo' => '[email protected]:allebb/example.git',
'is_github' => true, // Using GitHub webhooks to trigger this workflow.
'branch' => 'deploy-prod', // As long as the GitHub webhook request relates to changes on this git branch, we'll run the deployment workflow!
...
],
]
You should then configure your GitHub web hook to send the payload to your hooker deployment URL, in our example this
would be http://deploy.mysite.com/hooker.php?app=my-example-webapp&key=MyRandomDeploymentKey
and ensure that you
specify the Content type as application/json
as shown here:
When you push to the configured branch, GitHub will trigger a deployment to our server using this web hook URL. You can
view the output of the deployment process from this screen too as long as you have the debug
option set
to true
in your Hooker configuration you will be able to see the output (result) of each of your workflow/deployment
steps.
As per the GitHub section above (see for context), you can also enable your configuration to receive standard webhooks from BitBucket.
You should configure your BitBucket webhook like so:
Enabling BitBucket support is as simple as setting your site configuration to match this example:
'sites' => [
'my-example-webapp' => [
...
'key' => 'MyRandomDeploymentKey',
'remote_repo' => '[email protected]:allebb/example.git',
'is_bitbucket' => true, // Using BitBucket webhooks to trigger this workflow.
'branch' => 'deploy-prod', // As long as the BitBucket webhook request relates to changes on this git branch, we'll run the deployment workflow!
...
],
]
As per the GitHub section above (see for context), you can also enable your configuration to receive standard webhooks from GitLab.
You should configure your GitLab webhook like so:
Enabling GitLab support is as simple as setting your site configuration to match this example:
'sites' => [
'my-example-webapp' => [
...
'key' => 'MyRandomDeploymentKey',
'remote_repo' => '[email protected]:allebb/example.git',
'is_gitlab' => true, // Using GitLab webhooks to trigger this workflow.
'branch' => 'deploy-prod', // As long as the GitLab webhook request relates to changes on this git branch, we'll run the deployment workflow!
...
],
]
Please report any bugs on the Issue Tracker, please ensure that bug reports are clear and contain as much information as possible.
Bug reports will be looked at and resolved as soon as possible!