Created
November 11, 2015 16:20
-
-
Save jlis/4a89eb510e6c7eb6118c to your computer and use it in GitHub Desktop.
PHP commit-msg git hook which checks your syntax, the code style, runs unit tests and checks for a ticket nummer inside of the commit message.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env php | |
| <?php | |
| /** | |
| * The commit-msg hook script. | |
| * Just create a symlink in your .git/hooks/ folder to this file. | |
| * | |
| * @author Julius Ehrlich <julius.ehrlich@lovoo.com> | |
| */ | |
| class PreCommitHook | |
| { | |
| /** | |
| * @var array | |
| */ | |
| private $changedFiles = []; | |
| /** | |
| * @var string | |
| */ | |
| private $commitMessage; | |
| public function __construct() | |
| { | |
| $this->changedFiles = $this->getChangedFiles(); | |
| } | |
| public function run() | |
| { | |
| $this->checkCommitMessage(); | |
| $this->checkSyntax(); | |
| $this->checkStyle(); | |
| $this->checkTests(); | |
| } | |
| /** | |
| * @param array $argv | |
| */ | |
| public function setCommitMessage(array $argv) | |
| { | |
| if (count($argv) < 2) { | |
| return; | |
| } | |
| $commitFile = $argv[1]; | |
| if (file_exists($commitFile)) { | |
| $this->commitMessage = file_get_contents($commitFile); | |
| } | |
| } | |
| /** | |
| * Checks the codestyle of the altered files with PHPCS. | |
| * | |
| * @throws \RuntimeException | |
| */ | |
| private function checkStyle() | |
| { | |
| if ($this->checkIsDisabled('style')) { | |
| return; | |
| } | |
| $options = '--extensions=php --error-severity=1 --standard=PSR2'; | |
| foreach ($this->changedFiles as $file) { | |
| if (false === strpos($file, '.php')) { | |
| continue; | |
| } | |
| exec(sprintf('vendor/bin/phpcs %s %s', $options, $file), $output, $returnCode); | |
| if ($returnCode !== 0) { | |
| printf('PHP style check failed for file: %s %s', $file, PHP_EOL); | |
| printf('%s %s', implode(PHP_EOL, $output), PHP_EOL); | |
| throw new \RuntimeException(); | |
| } | |
| } | |
| } | |
| /** | |
| * Runs a lint/sytax check against the altered files. | |
| * | |
| * @throws \RuntimeException | |
| */ | |
| private function checkSyntax() | |
| { | |
| if ($this->checkIsDisabled('syntax')) { | |
| return; | |
| } | |
| foreach ($this->changedFiles as $file) { | |
| exec('php -l '.$file, $output, $returnCode); | |
| if ($returnCode !== 0) { | |
| printf('PHP syntax check failed for file: %s %s', $file, PHP_EOL); | |
| printf('%s %s', $output, PHP_EOL); | |
| throw new \RuntimeException(); | |
| } | |
| } | |
| } | |
| /** | |
| * Runs the PHPUnit tests to ensure no tests are broken. | |
| * | |
| * @throws \RuntimeException | |
| */ | |
| private function checkTests() | |
| { | |
| if ($this->checkIsDisabled('tests')) { | |
| return; | |
| } | |
| $projectName = basename(getcwd()); | |
| exec('vendor/bin/phpunit -c phpunit.xml', $output, $returnCode); | |
| if ($returnCode !== 0) { | |
| $summary = array_pop($output); | |
| printf('Test suite for %s failed: ', $projectName); | |
| printf('( %s ) %s', $summary, PHP_EOL); | |
| throw new \RuntimeException(); | |
| } | |
| } | |
| /** | |
| * Checks if the commit message includes a ticket number | |
| * | |
| * @throws \RuntimeException | |
| */ | |
| private function checkCommitMessage() | |
| { | |
| if (null === $this->commitMessage || $this->checkIsDisabled('commitmessage')) { | |
| return; | |
| } | |
| $pattern = '(ISSUE-|BUG-)'; | |
| if (1 !== preg_match($pattern, $this->commitMessage)) { | |
| printf('Failed to find a matching ticket pattern: %s %s', $pattern, PHP_EOL); | |
| throw new \RuntimeException(); | |
| } | |
| } | |
| /** | |
| * @return array | |
| */ | |
| private function getChangedFiles() | |
| { | |
| exec('git diff --cached --name-only', $output); | |
| return is_array($output) ? $output : []; | |
| } | |
| /** | |
| * @param $check | |
| * | |
| * @return bool | |
| */ | |
| private function checkIsDisabled($check) | |
| { | |
| $skipTag = '#no'.$check; | |
| if (false !== strpos($this->commitMessage, $skipTag)) { | |
| printf('Found %s, skipping the "%s" tests %s', $skipTag, $check, PHP_EOL); | |
| return true; | |
| } | |
| return false; | |
| } | |
| } | |
| try { | |
| $hook = new PreCommitHook(); | |
| if (is_array($argv)) { | |
| $hook->setCommitMessage($argv); | |
| } | |
| $hook->run(); | |
| exit(0); | |
| } catch (\RuntimeException $e) { | |
| exit(1); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment