├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── LICENSE.md ├── README.md ├── composer.json ├── phpunit.xml.dist └── src ├── GitHook ├── GitHook.php ├── Http │ ├── Controller │ │ └── GitHookController.php │ └── routes.php ├── Payload │ ├── Payload.php │ └── Services │ │ ├── BitbucketService.php │ │ ├── GithubService.php │ │ └── GitlabService.php ├── Providers │ └── LaravelServiceProvider.php └── resources │ └── views │ └── email.blade.php └── config └── git-hook.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | .idea 4 | test.php -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | php: 4 | - hhvm 5 | - 7.0 6 | - 5.6 7 | - 5.5 8 | 9 | matrix: 10 | fast_finish: true 11 | allow_failures: 12 | - php: hhvm 13 | 14 | sudo: false 15 | 16 | install: composer install --no-interaction 17 | 18 | notifications: 19 | email: 20 | on_success: always 21 | on_failure: always 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `webklex/laravel-git-hook` will be documented in this file. 4 | 5 | Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) principles. 6 | 7 | ## [UNRELEASED] 8 | ### Changed 9 | - Maintenance mode example added to config 10 | 11 | ## [0.0.1.6] - 2017-03-05 12 | ### Changed 13 | - Payload management improved 14 | - Configuration description extended 15 | 16 | ### Added 17 | - After pull event commands option added 18 | - Before pull event command option added 19 | 20 | ## [0.0.1.1 - 0.0.1.5] - 2017-03-05 21 | ### Added 22 | - GitHook Service moved into different classes 23 | - Github Payload support added 24 | - Bitbucket Payload support added 25 | 26 | ## 0.0.1 - 2017-03-04 27 | ### Added 28 | - new laravel-git-hook package 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Webklex 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Malte Goldenbaum 4 | 5 | > Permission is hereby granted, free of charge, to any person obtaining a copy 6 | > of this software and associated documentation files (the "Software"), to deal 7 | > in the Software without restriction, including without limitation the rights 8 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | > copies of the Software, and to permit persons to whom the Software is 10 | > furnished to do so, subject to the following conditions: 11 | > 12 | > The above copyright notice and this permission notice shall be included in 13 | > all copies or substantial portions of the Software. 14 | > 15 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Git hook deployment made for Laravel 2 | 3 | [![Latest Version on Packagist][ico-version]][link-packagist] 4 | [![Software License][ico-license]](LICENSE.md) 5 | [![Build Status][ico-travis]][link-travis] 6 | [![Total Downloads][ico-downloads]][link-downloads] 7 | 8 | ## Install 9 | 10 | Via Composer 11 | 12 | ``` bash 13 | $ composer require webklex/laravel-git-hook 14 | ``` 15 | 16 | ## Setup 17 | 18 | Add the service provider to the providers array in `config/app.php`. 19 | 20 | ``` php 21 | 'providers' => [ 22 | Webklex\GitHook\Providers\LaravelServiceProvider::class, 23 | ]; 24 | ``` 25 | 26 | ## Publishing 27 | 28 | You can publish everything at once 29 | 30 | ``` php 31 | php artisan vendor:publish --provider="Webklex\GitHook\Providers\LaravelServiceProvider" 32 | ``` 33 | 34 | ## Usage 35 | 36 | This library is designed to handle the automatic deployment by git hooks 37 | as simple as possible. There isn't much todo to get started: just add the 38 | Provider and edit the `config/git-hook.php` file to make it fit your needs. 39 | 40 | 41 | Custom configuration can be made within the `config/git-hook.php` file: 42 | 43 | | Parameter | Default | Options | Description | 44 | | ------------------ | :-------------------------------: | :---------------------------------------: | --------------------------------------------------------------------------------------------------------: | 45 | | email_recipients | `[]` | `[['name' => '', 'address' => ''], ...]` | Get notified by mail. Just add your credentials | 46 | | email_email_sender | `['address' => '', 'name' => '']` | `['address' => '', 'name' => '']` | Specify a custom email sender address | 47 | | repo_path | `null` | Leave empty to auto detect the vcs root | Perhaps your repository is somehow specially structured, if that's the case, specify your repository path | 48 | | allowed_sources | `[]` | `['192.168.1.1', '192.168.1.2', ...]` | If you want to secure the deployment process a bit more, whitelist the remote repository IPs | 49 | | remote | `origin` | | Your remote branch name | 50 | | git_path | `/usr/bin/git` | | Where is the git binary located | 51 | | logfile | `git-hook` | | Name of the logfile. It will be stored under storage/logs | 52 | | service | `github` | `github`, `gitbucket`, `gitlab` | Define your remote git service. This is required to identify the payload | 53 | | url | `git-hook` | | Define the deployment url. Keep in mind, that the given parameter will be added to your app.url | 54 | | before_pull | `[]` | `['down', ['cmd', ['arg1' => 1]]]` | If you have any commands that have to be called before a pull event, specify them here | 55 | | after_pull | `[]` | `['cmd', ['cmd1', ['arg1' => 1]], 'up']` | If you have any commands that have to be called after a pull event, specify them here | 56 | 57 | 58 | If you are concerned someone could guess it, use a more cryptic url such as: `JHFUjhd67567JHFGhsd78236784wegfJHFghdgf` 59 | 60 | 61 | ## Potential problems: 62 | 63 | Please make sure your `www-data` user can actually perform a git pull on the server without 64 | having to enter a password: 65 | so you might want to take a look at ssh-keys or something similar 66 | 67 | ## Change log 68 | 69 | Please see [CHANGELOG](CHANGELOG.md) for more information what has changed recently. 70 | 71 | ## Testing 72 | 73 | ``` bash 74 | $ composer test 75 | ``` 76 | 77 | ## Security 78 | 79 | If you discover any security related issues, please email github@webklex.com instead of using the issue tracker. 80 | 81 | ## Credits 82 | 83 | - [Webklex][link-author] 84 | - All Contributors 85 | 86 | ## License 87 | 88 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 89 | 90 | [ico-version]: https://img.shields.io/packagist/v/Webklex/laravel-git-hook.svg?style=flat-square 91 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square 92 | [ico-travis]: https://img.shields.io/travis/Webklex/laravel-git-hook/master.svg?style=flat-square 93 | [ico-scrutinizer]: https://img.shields.io/scrutinizer/coverage/g/Webklex/laravel-git-hook.svg?style=flat-square 94 | [ico-code-quality]: https://img.shields.io/scrutinizer/g/Webklex/laravel-git-hook.svg?style=flat-square 95 | [ico-downloads]: https://img.shields.io/packagist/dt/Webklex/laravel-git-hook.svg?style=flat-square 96 | 97 | [link-packagist]: https://packagist.org/packages/Webklex/laravel-git-hook 98 | [link-travis]: https://travis-ci.org/Webklex/laravel-git-hook 99 | [link-scrutinizer]: https://scrutinizer-ci.com/g/Webklex/laravel-git-hook/code-structure 100 | [link-code-quality]: https://scrutinizer-ci.com/g/Webklex/laravel-git-hook 101 | [link-downloads]: https://packagist.org/packages/Webklex/laravel-git-hook 102 | [link-author]: https://github.com/webklex -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webklex/laravel-git-hook", 3 | "type": "library", 4 | "description": "Laravel Git Hook", 5 | "keywords": [ 6 | "webklex", 7 | "laravel", 8 | "laravel deployment", 9 | "auto deployment", 10 | "git", 11 | "deployment", 12 | "git hook", 13 | "hook", 14 | "laravel-git-hook" 15 | ], 16 | "homepage": "https://github.com/webklex/laravel-git-hook", 17 | "license": "MIT", 18 | "authors": [ 19 | { 20 | "name": "Malte Goldenbaum", 21 | "email": "github@webklex.com", 22 | "role": "Developer" 23 | } 24 | ], 25 | "require": { 26 | "php": ">=5.5.9", 27 | "laravel/framework": ">=5.0.0" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "Webklex\\GitHook\\": "src/GitHook" 32 | } 33 | }, 34 | "scripts": { 35 | "test": "phpunit" 36 | }, 37 | "extra": { 38 | "branch-alias": { 39 | "dev-master": "1.0-dev" 40 | } 41 | }, 42 | "minimum-stability": "dev", 43 | "prefer-stable": true 44 | } 45 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/GitHook/GitHook.php: -------------------------------------------------------------------------------- 1 | config = config('git-hook'); 64 | $this->gitPath = !empty($this->config['git_path']) ? $this->config['git_path'] : $this->gitPath; 65 | 66 | $this->initLogger($this->config['logfile']); 67 | } 68 | 69 | /** 70 | * Initialize a new Logger instance 71 | * @param $name 72 | * 73 | * @return $this 74 | */ 75 | public function initLogger($name){ 76 | $this->oLogger = new Logger($name); 77 | $this->oLogger->pushHandler(new StreamHandler(storage_path('logs/'.$name.'.log'), Logger::WARNING)); 78 | 79 | return $this; 80 | } 81 | 82 | /** 83 | * Get the current Logger instance 84 | * 85 | * @return Logger 86 | */ 87 | public function getLogger(){ 88 | return $this->oLogger; 89 | } 90 | 91 | /** 92 | * Parse the raw request content and convert it into a collection 93 | * @param $rawRequest string 94 | * 95 | * @return bool 96 | */ 97 | public function parseRequest($rawRequest){ 98 | $service = 'Webklex\GitHook\Payload\Services\\'.ucfirst($this->config['service']).'Service'; 99 | 100 | /*** 101 | * @var Payload $oPayload 102 | */ 103 | $oPayload = with(new $service())->parsePayload($rawRequest); 104 | 105 | $this->aRequest = $oPayload->getPayload(); 106 | 107 | $this->aRequest->put('commits', collect($this->aRequest->get('commits'))); 108 | 109 | return $this->aRequest->count() > 0; 110 | } 111 | 112 | /** 113 | * Locate the Laravel root directory by trying to locate the .env and .git/config file 114 | */ 115 | public function locateLaravelRoot(){ 116 | if (empty($this->config['repo_path'])) { 117 | $this->config['repo_path'] = __DIR__; 118 | 119 | do { 120 | $this->config['repo_path'] = dirname($this->config['repo_path']); 121 | } while ($this->config['repo_path'] !== '/' && !file_exists($this->config['repo_path'].'/.env')); 122 | } 123 | 124 | if ($this->config['repo_path'] !== '/') { 125 | while ($this->config['repo_path'] !== '/' && !file_exists($this->config['repo_path'].'/.git/config')) { 126 | $this->config['repo_path'] = dirname($this->config['repo_path']); 127 | } 128 | } 129 | } 130 | 131 | /** 132 | * Get the current project branch 133 | * 134 | * @return string 135 | */ 136 | public function getCurrentBranch(){ 137 | if($this->currentBranch == null){ 138 | $this->currentBranch = trim( 139 | shell_exec( 140 | escapeshellcmd($this->gitPath) . ' --git-dir=' . 141 | escapeshellarg($this->config['repo_path'] . '/.git') . ' --work-tree=' . 142 | escapeshellarg($this->config['repo_path']) . ' rev-parse --abbrev-ref HEAD')); 143 | } 144 | 145 | return $this->currentBranch; 146 | } 147 | 148 | /** 149 | * Get the pushed branch name 150 | * 151 | * @return string 152 | */ 153 | public function getPushedBranch(){ 154 | return $this->pushedBranch; 155 | } 156 | 157 | /** 158 | * Check if white listing is enabled and if so if the request comes from a white listed IP 159 | * 160 | * @return bool 161 | */ 162 | public function whiteList(){ 163 | if(!empty($this->config['allowed_sources'])){ 164 | return in_array($_SERVER['REMOTE_ADDR'], $this->config['allowed_sources']); 165 | } 166 | 167 | return true; 168 | } 169 | 170 | /** 171 | * Check if the Repository exists 172 | * 173 | * @return bool 174 | */ 175 | public function checkRepository(){ 176 | if(!empty($this->config['repo_path'])){ 177 | return file_exists($this->config['repo_path'].'/.git/config'); 178 | } 179 | 180 | return true; 181 | } 182 | 183 | /** 184 | * Check if the pushed branch is the one we want to deploy 185 | * 186 | * @return bool 187 | */ 188 | public function checkBranch(){ 189 | $pushedBranch = explode('/', $this->aRequest->get('ref')); 190 | $this->pushedBranch = trim($pushedBranch[2]); 191 | 192 | return $this->getCurrentBranch() == $this->pushedBranch; 193 | } 194 | 195 | /** 196 | * Run an array of given commands 197 | * @param array $commands 198 | */ 199 | protected function runCommands(array $commands){ 200 | foreach($commands as $command){ 201 | 202 | $cmd = $command; 203 | $args = []; 204 | 205 | if(is_array($command)){ 206 | $cmd = $command[0]; 207 | $args = $command[1]; 208 | } 209 | 210 | Artisan::call($cmd, $args); 211 | } 212 | } 213 | 214 | /** 215 | * Perform the deployment task 216 | * 217 | * @return bool 218 | */ 219 | public function gitPull(){ 220 | $git_remote = !empty($this->config['remote']) ? $this->config['remote'] : 'origin'; 221 | 222 | $cmd = escapeshellcmd($this->gitPath) . ' --git-dir=' . 223 | escapeshellarg($this->config['repo_path'] . '/.git') . ' --work-tree=' . 224 | escapeshellarg($this->config['repo_path']) . ' pull ' . 225 | escapeshellarg($git_remote) . ' ' . 226 | escapeshellarg($this->getCurrentBranch()) . ' >> ' . 227 | escapeshellarg($this->config['repo_path'] . '/storage/logs/git-hook.log'); 228 | 229 | $this->runCommands($this->config['before_pull']); 230 | 231 | $pullRequest = shell_exec($cmd); 232 | 233 | $this->runCommands($this->config['after_pull']); 234 | 235 | return $this->notify([ 236 | 'cmd' => $cmd, 237 | 'user' => shell_exec('whoami'), 238 | 'response' => $pullRequest, 239 | ]); 240 | } 241 | 242 | /** 243 | * Notify users if this feature is enabled within the config file 244 | * @param array $result 245 | * 246 | * @return bool 247 | */ 248 | protected function notify(array $result){ 249 | if (!empty($this->config['email_recipients'])) { 250 | 251 | /* Convert the git message into a better readable format 252 | * */ 253 | $aCommit = $this->aRequest->get('commits')->map(function($commit){ 254 | 255 | /* Split message into subject + description 256 | * -Suggested by git: Assumes Git's recommended standard where first line is the main summary 257 | * */ 258 | $commit['human_id'] = substr($commit['id'], 0, 9); 259 | $commit['human_subject'] = strtok($commit['message'], "\n"); 260 | $commit['human_description'] = $commit['message']; 261 | $commit['human_date'] = with(new \DateTime($commit['timestamp']))->format('d.m.Y H:i'); 262 | 263 | return $commit; 264 | }); 265 | $this->aRequest->put('commits', $aCommit->all()); 266 | 267 | /* Set sender address 268 | * */ 269 | $sender = config('mail.from'); 270 | if (!empty($this->config['email_sender']['address'])) { 271 | $sender = $this->config['email_sender']; 272 | } 273 | 274 | /* Set email recipients 275 | * */ 276 | $sender['recipients'] = $this->config['email_recipients']; 277 | 278 | /* Convert the request collection back into an array 279 | * */ 280 | $aRequest = $this->aRequest->all(); 281 | 282 | /* Send the actual information email 283 | * */ 284 | \Mail::send('git-hook::email', [ 'server' => $result, 'git' => $aRequest ], function($message) use ($aRequest, $sender) { 285 | 286 | $message->from($sender['address'], $sender['name']); 287 | 288 | foreach ($sender['recipients'] as $recipient) { 289 | $message->to($recipient['address'], $recipient['name']); 290 | } 291 | 292 | $message->subject('Repo: ' . $aRequest['repository']['name'] . ' updated'); 293 | }); 294 | } 295 | 296 | return true; 297 | } 298 | } -------------------------------------------------------------------------------- /src/GitHook/Http/Controller/GitHookController.php: -------------------------------------------------------------------------------- 1 | oGitHook = new GitHook(); 34 | } 35 | 36 | /** 37 | * @param Request $request 38 | * @return mixed 39 | */ 40 | public function gitHook(Request $request) 41 | { 42 | 43 | /* Check if the request comes from a white listed ip 44 | * Only relevant if you use the white list functionality 45 | * */ 46 | if ($this->oGitHook->whiteList() == false) { 47 | $this->oGitHook->getLogger()->addError('Request must come from an approved IP'); 48 | return response()->json([ 49 | 'success' => false, 50 | 'message' => 'Request must come from an approved IP', 51 | ], 500); 52 | } 53 | 54 | 55 | /* Check if the current request contains any information and isn't empty 56 | * */ 57 | if ($this->oGitHook->parseRequest($request->getContent()) == false) { 58 | $this->oGitHook->getLogger()->addError('Web hook data does not look valid'); 59 | return response()->json([ 60 | 'success' => false, 61 | 'message' => 'Web hook data does not look valid', 62 | ], 500); 63 | } 64 | 65 | $this->oGitHook->locateLaravelRoot(); 66 | 67 | /* Check if the repository actually exists 68 | * */ 69 | if ($this->oGitHook->checkRepository() == false) { 70 | $this->oGitHook->getLogger()->addError('Invalid repo path in config'); 71 | return response()->json([ 72 | 'success' => false, 73 | 'message' => 'Invalid repo path in config', 74 | ], 500); 75 | } 76 | 77 | 78 | /* Check if the pushed branch is the one we one want to deploy 79 | * */ 80 | if ($this->oGitHook->checkBranch() == false){ 81 | $this->oGitHook->getLogger()->addWarning('Pushed refs do not match current branch: ['. 82 | $this->oGitHook->getCurrentBranch().' /=/ '.$this->oGitHook->getPushedBranch().']'); 83 | return response()->json([ 84 | 'success' => false, 85 | 'message' => 'Pushed refs do not match current branch', 86 | ], 500); 87 | } 88 | 89 | 90 | return response()->json($this->oGitHook->gitPull()); 91 | } 92 | } -------------------------------------------------------------------------------- /src/GitHook/Http/routes.php: -------------------------------------------------------------------------------- 1 | app['config']['git-hook']['url'], 'Webklex\GitHook\Http\GitHookController@gitHook'); -------------------------------------------------------------------------------- /src/GitHook/Payload/Payload.php: -------------------------------------------------------------------------------- 1 | [[ 29 | 'id' => null, 30 | 'timestamp' => null, 31 | 'message' => null, 32 | 'url' => null, 33 | 'author' => [ 34 | 'name' => null, 35 | 'email' => null, 36 | ], 37 | ]], 38 | 'repository' => [ 39 | 'name' => null, 40 | 'description' => null, 41 | 'url' => null, 42 | 'homepage' => null, 43 | ], 44 | 'ref' => null, 45 | 'user_name' => null, 46 | 'before' => null, 47 | 'after' => null, 48 | ]; 49 | 50 | /** 51 | * Cast any mapped value to a given schema 52 | * @var array $casts 53 | */ 54 | protected $casts = []; 55 | 56 | /** 57 | * Payload constructor. 58 | */ 59 | public function __construct() {} 60 | 61 | /** 62 | * Parse a payload string into the service 63 | * @param $payload 64 | * 65 | * @return $this 66 | */ 67 | public function parsePayload($payload){ 68 | if($this->isJson($payload)){ 69 | $this->aPayload = $this->validate(json_decode($payload, true), $this->map); 70 | $this->castPayload(); 71 | } 72 | 73 | return $this; 74 | } 75 | 76 | /** 77 | * Check if a given string is valid JSON 78 | * @param $string string 79 | * @return bool 80 | */ 81 | public function isJson($string) { 82 | json_decode($string); 83 | return (json_last_error() == JSON_ERROR_NONE); 84 | } 85 | 86 | /** 87 | * Validate and map a given payload recursive 88 | * @param array $aPayload 89 | * @param array $aMap 90 | * 91 | * @return array 92 | */ 93 | public function validate(array $aPayload, array $aMap){ 94 | $payload = []; 95 | foreach($aMap as $key => $map){ 96 | if($map === ''){ 97 | $payload[$key] = ''; 98 | }elseif(isset($aPayload[$key])){ 99 | if(is_array($aPayload[$key])){ 100 | $payload[$key] = $this->validate($aPayload[$key], $map); 101 | }elseif($map != null){ 102 | $payload[$key] = $this->getPayloadValue($map, $aPayload); 103 | }else{ 104 | $payload[$key] = $aPayload[$key]; 105 | } 106 | }elseif(is_array($map)){ 107 | $payload[$key] = []; 108 | 109 | foreach($map as $k => $m){ 110 | if(is_array($m)){ 111 | $payload[$key][$k] = $this->validate($aPayload, $m); 112 | }elseif($m != null){ 113 | $payload[$key][$k] = $this->getPayloadValue($m, $aPayload); 114 | }elseif($m === ''){ 115 | $payload[$key][$k] = ''; 116 | }else{ 117 | $payload[$key][$k] = $aPayload[$key][$k]; 118 | } 119 | } 120 | }elseif($map != null){ 121 | $payload[$key] = $this->getPayloadValue($map, $aPayload); 122 | } 123 | } 124 | return $payload; 125 | } 126 | 127 | /** 128 | * Get a payload value from a given compact string index 129 | * @param $key 130 | * @param array $payload 131 | * 132 | * @return array|mixed 133 | */ 134 | protected function getPayloadValue($key, array $payload){ 135 | $keys = explode('.', $key); 136 | foreach($keys as $i => $key){ 137 | unset($keys[$i]); 138 | if(isset($payload[$key])){ 139 | $payload = $payload[$key]; 140 | } 141 | } 142 | 143 | return $payload; 144 | } 145 | 146 | /** 147 | * Cast available casts on the current payload 148 | */ 149 | protected function castPayload(){ 150 | foreach($this->casts as $string => $cast){ 151 | $keys = explode('.', $string); 152 | 153 | $key = $keys[0]; 154 | unset($keys[0]); 155 | sort($keys); 156 | 157 | $this->aPayload = $this->replace($this->aPayload, $key, $keys, $cast); 158 | } 159 | } 160 | 161 | /** 162 | * Cast a specific value within the payload 163 | * @param $arr array 164 | * @param $key string 165 | * @param $keys array 166 | * @param $cast string 167 | * 168 | * @return mixed 169 | */ 170 | protected function replace($arr, $key, $keys, $cast){ 171 | if(!empty($keys)){ 172 | $kkey = $keys[0]; 173 | unset($keys[0]); 174 | sort($keys); 175 | $arr[$key] = $this->replace($arr[$key], $kkey, $keys, $cast); 176 | }else{ 177 | $arr[$key] = str_replace('{$1}', $arr[$key], $cast); 178 | } 179 | return $arr; 180 | } 181 | 182 | /** 183 | * Get the current payload 184 | * @return \Illuminate\Support\Collection 185 | */ 186 | public function getPayload(){ 187 | return collect($this->aPayload); 188 | } 189 | } -------------------------------------------------------------------------------- /src/GitHook/Payload/Services/BitbucketService.php: -------------------------------------------------------------------------------- 1 | [[ 23 | 'id' => 'push.changes.0.commits.0.hash', 24 | 'timestamp' => 'push.changes.0.commits.0.date', 25 | 'message' => 'push.changes.0.commits.0.hash', 26 | 'url' => 'push.changes.0.commits.0.links.html.href', 27 | 'author' => [ 28 | 'name' => 'push.changes.0.commits.0.author.user.display_name', 29 | 'email' => '', 30 | ] 31 | ]], 32 | 'repository' => [ 33 | 'name' => 'repository.name', 34 | 'description' => '', 35 | 'url' => 'repository.links.html.href', 36 | 'homepage' => 'repository.website', 37 | ], 38 | 'ref' => 'push.changes.0.new.name', 39 | 'user_name' => 'push.changes.0.commits.0.author.user.display_name', 40 | 'before' => 'push.changes.0.new.target.parents.0.hash', 41 | 'after' => 'push.changes.0.new.target.hash', 42 | ]; 43 | 44 | protected $casts = [ 45 | 'ref' => 'ref/branch/{$1}' 46 | ]; 47 | 48 | } -------------------------------------------------------------------------------- /src/GitHook/Payload/Services/GithubService.php: -------------------------------------------------------------------------------- 1 | [[ 24 | 'id' => null, 25 | 'timestamp' => null, 26 | 'message' => null, 27 | 'url' => null, 28 | 'author' => [ 29 | 'name' => null, 30 | 'email' => null, 31 | ], 32 | ]], 33 | 'repository' => [ 34 | 'name' => null, 35 | 'description' => null, 36 | 'url' => null, 37 | 'homepage' => null, 38 | ], 39 | 'ref' => null, 40 | 'user_name' => null, 41 | 'before' => null, 42 | 'after' => null, 43 | ]; 44 | 45 | protected $casts = []; 46 | 47 | } -------------------------------------------------------------------------------- /src/GitHook/Payload/Services/GitlabService.php: -------------------------------------------------------------------------------- 1 | [[ 20 | 'id' => null, 21 | 'timestamp' => null, 22 | 'message' => null, 23 | 'url' => null, 24 | 'author' => [ 25 | 'name' => null, 26 | 'email' => null, 27 | ], 28 | ]], 29 | 'repository' => [ 30 | 'name' => null, 31 | 'description' => null, 32 | 'url' => null, 33 | 'homepage' => null, 34 | ], 35 | 'ref' => null, 36 | 'user_name' => null, 37 | 'before' => null, 38 | 'after' => null, 39 | ]; 40 | 41 | protected $casts = []; 42 | 43 | } -------------------------------------------------------------------------------- /src/GitHook/Providers/LaravelServiceProvider.php: -------------------------------------------------------------------------------- 1 | publishes([ 27 | __DIR__.'/../../config/git-hook.php' => config_path('git-hook.php'), 28 | ]); 29 | 30 | $this->mergeConfigFrom(realpath(__DIR__.'/../../config/git-hook.php'), 'git-hook'); 31 | $this->loadViewsFrom(__DIR__.'/../resources/views', 'git-hook'); 32 | 33 | require __DIR__.'/../Http/routes.php'; 34 | } 35 | 36 | /** 37 | * Register any package services. 38 | * 39 | * @return void 40 | */ 41 | public function register() 42 | { 43 | $this->app->bind('git-hook', function ($app) { 44 | return new GitHookApp; 45 | }); 46 | 47 | } 48 | } -------------------------------------------------------------------------------- /src/GitHook/resources/views/email.blade.php: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 |

{{ $git['repository']['name'] }} updated

17 |

The Git repository has just been updated by {{ $git['user_name'] }} via the web hook.

18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
System user{{ $server['user'] }}
Command{{ $server['cmd'] }}
Response{{ $server['response'] }}
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 |
Repository{{ $git['repository']['name'] }}
{{ $git['repository']['description'] }}
Ref{{ $git['ref'] }}
Before{{ $git['before'] }}
After{{ $git['after'] }}
URL{{ $git['repository']['url'] }}
Homepage{{ $git['repository']['homepage'] }}
58 |

Commit log

59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | @foreach($git['commits'] as $commit) 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | @endforeach 78 |
DateSubjectAuthorCommit
{{ $commit['human_date'] }}{{ $commit['human_subject'] }}{{ $commit['author']['name'] }}{{ $commit['human_id'] }}
79 |
80 | -------------------------------------------------------------------------------- /src/config/git-hook.php: -------------------------------------------------------------------------------- 1 | 'Admin', 'address' => 'email@example.com'], 26 | | ... 27 | | ] 28 | | 29 | */ 30 | 'email_recipients' => [], 31 | 32 | 33 | /* 34 | |-------------------------------------------------------------------------- 35 | | Email sender 36 | |-------------------------------------------------------------------------- 37 | | 38 | | The email address and name that notification emails will be sent from. 39 | | This will default to the sender in config(mail.from) if left null. 40 | | 41 | */ 42 | 'email_sender' => ['address' => null, 'name' => null], 43 | 44 | 45 | /* 46 | |-------------------------------------------------------------------------- 47 | | Repository path 48 | |-------------------------------------------------------------------------- 49 | | 50 | | This the root path of the Git repository that will be pulled. If this 51 | | is left empty the script will try to determine the directory itself 52 | | but looking for the project's .env file it's nearby .git directory. 53 | | 54 | | No trailing slash 55 | | 56 | */ 57 | 'repo_path' => '', 58 | 59 | 60 | /* 61 | |-------------------------------------------------------------------------- 62 | | Allowed sources 63 | |-------------------------------------------------------------------------- 64 | | 65 | | A request will be ignored unless it comes from an IP listed in this 66 | | array. Leave the array empty to allow all sources. 67 | | 68 | | This is useful for a little extra security if you run your own Git 69 | | repo server. 70 | | 71 | */ 72 | 'allowed_sources' => [], 73 | 74 | 75 | /* 76 | |-------------------------------------------------------------------------- 77 | | Remote name 78 | |-------------------------------------------------------------------------- 79 | | 80 | | The name of the remote repository to pull the changes from 81 | | 82 | */ 83 | 'remote' => 'origin', 84 | 85 | 86 | /* 87 | |-------------------------------------------------------------------------- 88 | | Git binary path 89 | |-------------------------------------------------------------------------- 90 | | 91 | | The full path to the system git binary. e.g. /usr/bin/git 92 | | 93 | | Leave blank to let the system detect using the current PATH variable 94 | | 95 | */ 96 | 'git_path' => '/usr/bin/git', 97 | 98 | 99 | /* 100 | |-------------------------------------------------------------------------- 101 | | Logfile name 102 | |-------------------------------------------------------------------------- 103 | | 104 | | The filename of the logfile which will be used to store deployment 105 | | information. 106 | | 107 | | By default it will use: git-hook 108 | | 109 | | The log file will be placed within the storage/log/ directory. 110 | | 111 | */ 112 | 'logfile' => 'git-hook', 113 | 114 | 115 | /* 116 | |-------------------------------------------------------------------------- 117 | | Remote git service 118 | |-------------------------------------------------------------------------- 119 | | 120 | | Please select a service. This is required in order to parse the delivered 121 | | payload. 122 | | 123 | | Available services: 124 | | github [default] 125 | | bitbucket 126 | | 127 | */ 128 | 'service' => 'github', 129 | 130 | 131 | /* 132 | |-------------------------------------------------------------------------- 133 | | Url parameter 134 | |-------------------------------------------------------------------------- 135 | | 136 | | Please specify a url parameter. The router will adapt to it automatically. 137 | | 138 | | Example: if you enter 'another-git-hook' 139 | | It will be transformed into: https://your-domain.tld/another-git-hook 140 | | 141 | */ 142 | 'url' => 'git-hook', 143 | 144 | 145 | /* 146 | |-------------------------------------------------------------------------- 147 | | Before pull event 148 | |-------------------------------------------------------------------------- 149 | | 150 | | If you have any commands that have to be called before a pull event, specify 151 | | them below. 152 | | 153 | | ['down','route:clear', ['some:command', ['arg1' => 1]]] 154 | | 155 | */ 156 | 'before_pull' => [], 157 | 158 | 159 | /* 160 | |-------------------------------------------------------------------------- 161 | | After pull event 162 | |-------------------------------------------------------------------------- 163 | | 164 | | If you have any commands that have to be called after a pull event, specify 165 | | them below. 166 | | 167 | | ['route:clear', ['some:command', ['arg1' => 1]], 'up'] 168 | | 169 | */ 170 | 'after_pull' => [] 171 | 172 | 173 | ]; --------------------------------------------------------------------------------