Skip to main content

My preferred CaptainHook setup (for Magento)

This blog post might be outdated!
This blog post was published more than one year ago and might be outdated!
· 3 min read
Stephan Hochdörfer
Head of IT Business Operations

Back at #mageuc19 I gave a talk about various PHP tools that help you to manage the quality of your (Magento) project. One of the tools I mentioned is CaptainHook, a tool written in PHP to manage your git hooks. Whilst this is not a QA tool per se, it still made sense to showcase it as most QA tools do run via CaptainHook in our projects. That means tools like PHPUnit, Codesniffer, Security Checker or tools like PHPStan or Psalm.

The main reason I like CaptainHook is that it offers you a lot of flexibility on how to configure which tools run when in your git workflow. CaptainHook comes with support for the following Git Hooks, all of them being configurable in a captainhook.json file that resides in your Git repository:

{
"commit-msg": {
"enabled": false,
"actions": []
},
"pre-push": {
"enabled": false,
"actions": []
},
"pre-commit": {
"enabled": false,
"actions": []
},
"prepare-commit-msg": {
"enabled": false,
"actions": []
},
"post-commit": {
"enabled": false,
"actions": []
},
"post-merge": {
"enabled": false,
"actions": []
},
"post-checkout": {
"enabled": false,
"actions": []
}
}

Usually, I like to differ between pre-commit and pre-push hooks. As pre-commit hooks, I run tools like a linter or PHPUnit, all tools that run fast and give instant feedback. When pushing to a remote, I usually run tasks that potentially take a longer time to run, e.g. CodeSniffer or PHPStan. That way I can quickly add my commits without too much interruption locally and I "only" have a wait a bit longer when pushing (which might take some more time anyway). So a typical captaionhook.json configuration looks like this:

{
"commit-msg": {
"enabled": false,
"actions": []
},
"pre-push": {
"enabled": true,
"actions": [{
"action": "./vendor/bin/phing sniff",
"options": []
},{
"action": "./vendor/bin/phing analyze",
"options": []
},{
"action": "./vendor/bin/phing security:check",
"options": []
}]
},
"pre-commit": {
"enabled": true,
"actions": [{
"action": "\\CaptainHook\\App\\Hook\\PHP\\Action\\Linting",
"options": []
},{
"action": "./vendor/bin/phing unit",
"options": []
}]
},
"prepare-commit-msg": {
"enabled": false,
"actions": []
},
"post-commit": {
"enabled": false,
"actions": []
},
"post-merge": {
"enabled": false,
"actions": []
},
"post-checkout": {
"enabled": false,
"actions": []
}
}

The Phing tasks run the respective CLI commands since you use Phing also in the build pipelines, it made sense to call the same targets during the Git hook run. If you don't use Phing, you can call Composer scripts or the respective CLI commands directly.

The post-merge hook might also come in handy for things like a "composer install" or running "bin/magento setup:upgrade" if needed. For quite some time now, CaptainHook comes with support for conditions to make sure commands are only run when some requirements are met. For example, in case of composer, the command should only run when either the composer.json or composer.lock file was changed. You can do that easily with the following configuration:

{
"post-merge": {
"enabled": true,
"actions": [{
"action": "composer.phar install",
"options": [],
"conditions": [{
"exec": "\\CaptainHook\\App\Hook\\Condition\\FileChange\\Any",
"args": [ ["composer.json", "composer.lock"] ]
}]
}]
}
}