mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 15:34:12 +01:00
Compare commits
3 Commits
feature/pr
...
feature/87
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
da2ce04fce | ||
|
|
f86fc16d3f | ||
|
|
f83015d208 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -58,6 +58,10 @@ tests/*/vendor/*
|
||||
/tests/php-unit-tests/phpunit.xml
|
||||
/tests/php-unit-tests/postbuild_integration.xml
|
||||
|
||||
# CaptainHook: For Git hooks management
|
||||
# - Never version local config file
|
||||
/captainhook.config.json
|
||||
|
||||
|
||||
# Jetbrains
|
||||
/.idea/**
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
# Git hooks for iTop
|
||||
|
||||
> [!WARNING]
|
||||
> This read me and the `install.php` / `pre-commit.php` files are outdated. If we were to keep using what it is supposed to do, we should migrate it to the proper CaptainHook Git hooks manager.\
|
||||
|
||||
## ❓ Goal
|
||||
|
||||
Those [git hooks](https://git-scm.com/docs/githooks) aims to ease developing on [iTop](https://github.com/Combodo/iTop).
|
||||
~~Those [git hooks](https://git-scm.com/docs/githooks) aims to ease developing on [iTop](https://github.com/Combodo/iTop).~~
|
||||
|
||||
## ☑ Available hooks
|
||||
|
||||
* pre-commit : rejects commit if you have at least one SCSS file staged, and no CSS file
|
||||
* ~~pre-commit : rejects commit if you have at least one SCSS file staged, and no CSS file~~
|
||||
|
||||
## ⚙ Install
|
||||
|
||||
Just run install.php !
|
||||
~~Just run install.php !~~
|
||||
|
||||
87
.make/git-hooks/pre-commit-php-code-style-fixer.php
Normal file
87
.make/git-hooks/pre-commit-php-code-style-fixer.php
Normal file
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Git pre-commit hook to run PHP-CS-Fixer on staged PHP files which are not in third-party libs.
|
||||
*/
|
||||
|
||||
$sRootFolderAbsPath = dirname(dirname(__DIR__));
|
||||
$sPhpCsFixerBinaryAbsPath = $sRootFolderAbsPath.'/tests/php-code-style/vendor/bin/php-cs-fixer';
|
||||
$sPhpCsFixerConfigFileAbsPath = $sRootFolderAbsPath.'/tests/php-code-style/.php-cs-fixer.dist.php';
|
||||
|
||||
// Retrieve list of staged (`--cached`) files (ACMR: Added, Changed, Modified, Renamed only)
|
||||
$sStagedFilesCmd = 'git diff --cached --name-only --diff-filter=ACMR';
|
||||
exec($sStagedFilesCmd, $aStagedFiles);
|
||||
|
||||
// Ignore non-PHP files and third-party folders
|
||||
$aStagedFiles = array_filter($aStagedFiles, function ($sStagedFile) {
|
||||
// Ignore non-PHP files
|
||||
if (false === preg_match('/\.php$/i', $sStagedFile)) {
|
||||
return false;
|
||||
}
|
||||
// Ignore files in third-party folders
|
||||
$sNormalizedStagedFile = str_replace('\\', '/', $sStagedFile);
|
||||
if (
|
||||
strpos($sNormalizedStagedFile, 'lib/') !== false // Our root Composer libs folder
|
||||
|| strpos($sNormalizedStagedFile, 'vendor/') !== false // Any sub-folder Composer libs folder
|
||||
|| strpos($sNormalizedStagedFile, 'node_modules/') !== false // OUr root NPM libs folder
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
$iStagedFilesCount = count($aStagedFiles);
|
||||
if ($iStagedFilesCount === 0) {
|
||||
echo "No file to check for PHP code style.\n";
|
||||
exit(0);
|
||||
}
|
||||
echo "{$iStagedFilesCount} file(s) to check for PHP code style.\n";
|
||||
|
||||
// Prepare batch of files to process (limit command line length as it could fail on some systems)
|
||||
$aChunks = array_chunk(array_values($aStagedFiles), 50);
|
||||
$iExitCode = 0;
|
||||
|
||||
// Execute chunks
|
||||
$iNbChunks = count($aChunks);
|
||||
foreach ($aChunks as $iIdx => $aChunk) {
|
||||
$iChunkNumber = $iIdx + 1;
|
||||
|
||||
$sChunkFilesAsArgs = implode(' ', array_map('escapeshellarg', $aChunk));
|
||||
$sPhpCsFixerCmd = escapeshellcmd(PHP_BINARY)
|
||||
.' '.escapeshellarg($sPhpCsFixerBinaryAbsPath)
|
||||
.' fix --using-cache=no --config='.escapeshellarg($sPhpCsFixerConfigFileAbsPath)
|
||||
.' --verbose '.$sChunkFilesAsArgs;
|
||||
|
||||
echo "Executing chunk {$iChunkNumber}/{$iNbChunks} : {$sPhpCsFixerCmd}\n\n";
|
||||
passthru($sPhpCsFixerCmd, $iExitCode);
|
||||
if ($iExitCode !== 0) {
|
||||
echo "Failed to fix chunk #{$iChunkNumber} Aborting.\n";
|
||||
exit($iExitCode);
|
||||
}
|
||||
}
|
||||
|
||||
// Find which files have been fixed and re-stage them
|
||||
$sFixedFilesCmd = 'git diff --name-only --diff-filter=M';
|
||||
exec($sFixedFilesCmd, $aFixedFiles);
|
||||
$aFixedFilesToRestage = array_intersect($aFixedFiles, $aStagedFiles);
|
||||
|
||||
// Re-stage fixed files to include them in the commit
|
||||
if (count($aFixedFilesToRestage) === 0) {
|
||||
echo "No file needed PHP code style fixing, it was already ok.\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
echo "Re-staging fixed files:\n";
|
||||
foreach ($aFixedFilesToRestage as $sFixedFileToRestage) {
|
||||
$sGitAddCmd = 'git add '.escapeshellarg($sFixedFileToRestage);
|
||||
|
||||
echo " - {$sFixedFileToRestage}\n";
|
||||
passthru($sGitAddCmd, $iRetCode);
|
||||
if ($iRetCode !== 0) {
|
||||
echo " Failed to re-stage fixed file '{$sFixedFileToRestage}'. Continuing anyway.\n";
|
||||
}
|
||||
}
|
||||
|
||||
echo "All done, file(s) PHP code style fixed and added to commit.\n";
|
||||
exit($iExitCode);
|
||||
9
captainhook.config.json.dist
Normal file
9
captainhook.config.json.dist
Normal file
@@ -0,0 +1,9 @@
|
||||
// * Duplicate file into `captainhook.config.json`
|
||||
// * Remove all comments (`// ...`)
|
||||
// * Keep only 1 `php-path` line and adjust it to your environment
|
||||
{
|
||||
// Example of Windows path to PHP binary
|
||||
"php-path": "C:/wamp64/bin/php/php8.2.26/php.exe"
|
||||
// Example of Unix path tp PHP binary
|
||||
"php-path": "/usr/bin/php"
|
||||
}
|
||||
21
captainhook.json
Normal file
21
captainhook.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"config" : {
|
||||
"bootstrap" : "lib/autoload.php"
|
||||
},
|
||||
"commit-msg" : {
|
||||
"enabled" : true,
|
||||
"actions" : []
|
||||
},
|
||||
"pre-commit" : {
|
||||
"enabled" : true,
|
||||
"actions" : [
|
||||
{
|
||||
"action" : "{$CONFIG|value-of:php-path} .make/git-hooks/pre-commit-php-code-style-fixer.php",
|
||||
"config" : {
|
||||
"label" : "PHP code style fixer",
|
||||
"allow-failure": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@
|
||||
"soundasleep/html2text": "~2.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"captainhook/captainhook": "^5.25",
|
||||
"symfony/debug-bundle": "~6.4.0",
|
||||
"symfony/stopwatch": "~6.4.0",
|
||||
"symfony/web-profiler-bundle": "~6.4.0"
|
||||
@@ -98,8 +99,9 @@
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": ["@rmUnnecessaryFolders", "@tcpdfCustomFonts"],
|
||||
"post-update-cmd": ["@rmUnnecessaryFolders", "@tcpdfCustomFonts"],
|
||||
"post-install-cmd": ["@installGitHooks","@rmUnnecessaryFolders", "@tcpdfCustomFonts"],
|
||||
"post-update-cmd": ["@installGitHooks","@rmUnnecessaryFolders", "@tcpdfCustomFonts"],
|
||||
"installGitHooks": "@php lib/bin/captainhook install --force",
|
||||
"rmUnnecessaryFolders": "@php .make/dependencies/rmUnnecessaryFolders.php --manager composer",
|
||||
"tcpdfCustomFonts": "@php .make/dependencies/composer/tcpdf/tcpdfUpdateFonts.php"
|
||||
}
|
||||
|
||||
383
composer.lock
generated
383
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "0da5da3b165955f386268e6dd8db2a8d",
|
||||
"content-hash": "9de096942ebd728704bc74b51221a5df",
|
||||
"packages": [
|
||||
{
|
||||
"name": "apereo/phpcas",
|
||||
@@ -4880,6 +4880,322 @@
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "captainhook/captainhook",
|
||||
"version": "5.25.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/captainhook-git/captainhook.git",
|
||||
"reference": "f2278edde4b45af353861aae413fc3840515bb80"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/captainhook-git/captainhook/zipball/f2278edde4b45af353861aae413fc3840515bb80",
|
||||
"reference": "f2278edde4b45af353861aae413fc3840515bb80",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"captainhook/secrets": "^0.9.4",
|
||||
"ext-json": "*",
|
||||
"ext-spl": "*",
|
||||
"ext-xml": "*",
|
||||
"php": ">=8.0",
|
||||
"sebastianfeldmann/camino": "^0.9.2",
|
||||
"sebastianfeldmann/cli": "^3.3",
|
||||
"sebastianfeldmann/git": "^3.14",
|
||||
"symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0",
|
||||
"symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0",
|
||||
"symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
|
||||
},
|
||||
"replace": {
|
||||
"sebastianfeldmann/captainhook": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "~1 || ^2.0",
|
||||
"mikey179/vfsstream": "~1"
|
||||
},
|
||||
"bin": [
|
||||
"bin/captainhook"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"captainhook": {
|
||||
"config": "captainhook.json"
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "6.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"CaptainHook\\App\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Feldmann",
|
||||
"email": "sf@sebastian-feldmann.info"
|
||||
}
|
||||
],
|
||||
"description": "PHP git hook manager",
|
||||
"homepage": "http://php.captainhook.info/",
|
||||
"keywords": [
|
||||
"commit-msg",
|
||||
"git",
|
||||
"hooks",
|
||||
"post-merge",
|
||||
"pre-commit",
|
||||
"pre-push",
|
||||
"prepare-commit-msg"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/captainhook-git/captainhook/issues",
|
||||
"source": "https://github.com/captainhook-git/captainhook/tree/5.25.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sponsors/sebastianfeldmann",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-12T12:14:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "captainhook/secrets",
|
||||
"version": "0.9.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/captainhook-git/secrets.git",
|
||||
"reference": "d62c97f75f81ac98e22f1c282482bd35fa82f631"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/captainhook-git/secrets/zipball/d62c97f75f81ac98e22f1c282482bd35fa82f631",
|
||||
"reference": "d62c97f75f81ac98e22f1c282482bd35fa82f631",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"CaptainHook\\Secrets\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Feldmann",
|
||||
"email": "sf@sebastian-feldmann.info"
|
||||
}
|
||||
],
|
||||
"description": "Utility classes to detect secrets",
|
||||
"keywords": [
|
||||
"commit-msg",
|
||||
"keys",
|
||||
"passwords",
|
||||
"post-merge",
|
||||
"prepare-commit-msg",
|
||||
"secrets",
|
||||
"tokens"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/captainhook-git/secrets/issues",
|
||||
"source": "https://github.com/captainhook-git/secrets/tree/0.9.7"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sponsors/sebastianfeldmann",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-04-08T07:10:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastianfeldmann/camino",
|
||||
"version": "0.9.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianfeldmann/camino.git",
|
||||
"reference": "bf2e4c8b2a029e9eade43666132b61331e3e8184"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianfeldmann/camino/zipball/bf2e4c8b2a029e9eade43666132b61331e3e8184",
|
||||
"reference": "bf2e4c8b2a029e9eade43666132b61331e3e8184",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SebastianFeldmann\\Camino\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Feldmann",
|
||||
"email": "sf@sebastian-feldmann.info"
|
||||
}
|
||||
],
|
||||
"description": "Path management the OO way",
|
||||
"homepage": "https://github.com/sebastianfeldmann/camino",
|
||||
"keywords": [
|
||||
"file system",
|
||||
"path"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianfeldmann/camino/issues",
|
||||
"source": "https://github.com/sebastianfeldmann/camino/tree/0.9.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sebastianfeldmann",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2022-01-03T13:15:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastianfeldmann/cli",
|
||||
"version": "3.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianfeldmann/cli.git",
|
||||
"reference": "6fa122afd528dae7d7ec988a604aa6c600f5d9b5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianfeldmann/cli/zipball/6fa122afd528dae7d7ec988a604aa6c600f5d9b5",
|
||||
"reference": "6fa122afd528dae7d7ec988a604aa6c600f5d9b5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/process": "^4.3 | ^5.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SebastianFeldmann\\Cli\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Feldmann",
|
||||
"email": "sf@sebastian-feldmann.info"
|
||||
}
|
||||
],
|
||||
"description": "PHP cli helper classes",
|
||||
"homepage": "https://github.com/sebastianfeldmann/cli",
|
||||
"keywords": [
|
||||
"cli"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianfeldmann/cli/issues",
|
||||
"source": "https://github.com/sebastianfeldmann/cli/tree/3.4.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sebastianfeldmann",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-26T10:19:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastianfeldmann/git",
|
||||
"version": "3.15.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianfeldmann/git.git",
|
||||
"reference": "90cb5a32f54dbb0d7dcd87d02e664ec2b50c0c96"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianfeldmann/git/zipball/90cb5a32f54dbb0d7dcd87d02e664ec2b50c0c96",
|
||||
"reference": "90cb5a32f54dbb0d7dcd87d02e664ec2b50c0c96",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-libxml": "*",
|
||||
"ext-simplexml": "*",
|
||||
"php": ">=8.0",
|
||||
"sebastianfeldmann/cli": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikey179/vfsstream": "^1.6"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SebastianFeldmann\\Git\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Feldmann",
|
||||
"email": "sf@sebastian-feldmann.info"
|
||||
}
|
||||
],
|
||||
"description": "PHP git wrapper",
|
||||
"homepage": "https://github.com/sebastianfeldmann/git",
|
||||
"keywords": [
|
||||
"git"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianfeldmann/git/issues",
|
||||
"source": "https://github.com/sebastianfeldmann/git/tree/3.15.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sebastianfeldmann",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-05T08:07:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug-bundle",
|
||||
"version": "v6.4.0",
|
||||
@@ -4954,6 +5270,71 @@
|
||||
],
|
||||
"time": "2023-11-01T12:07:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v6.4.26",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "48bad913268c8cafabbf7034b39c8bb24fbc5ab8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/48bad913268c8cafabbf7034b39c8bb24fbc5ab8",
|
||||
"reference": "48bad913268c8cafabbf7034b39c8bb24fbc5ab8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Process\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v6.4.26"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-09-11T09:57:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/stopwatch",
|
||||
"version": "v6.4.0",
|
||||
|
||||
@@ -3,20 +3,23 @@
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
if (PHP_VERSION_ID < 50600) {
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException($err);
|
||||
if (!headers_sent()) {
|
||||
header('HTTP/1.1 500 Internal Server Error');
|
||||
}
|
||||
$err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
|
||||
if (!ini_get('display_errors')) {
|
||||
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
|
||||
fwrite(STDERR, $err);
|
||||
} elseif (!headers_sent()) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
require_once __DIR__.'/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit7f81b4a2a468a061c306af5e447a9a9f::getLoader();
|
||||
|
||||
119
lib/bin/captainhook
Normal file
119
lib/bin/captainhook
Normal file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Proxy PHP file generated by Composer
|
||||
*
|
||||
* This file includes the referenced bin path (../captainhook/captainhook/bin/captainhook)
|
||||
* using a stream wrapper to prevent the shebang from being output on PHP<8
|
||||
*
|
||||
* @generated
|
||||
*/
|
||||
|
||||
namespace Composer;
|
||||
|
||||
$GLOBALS['_composer_bin_dir'] = __DIR__;
|
||||
$GLOBALS['_composer_autoload_path'] = __DIR__ . '/..'.'/autoload.php';
|
||||
|
||||
if (PHP_VERSION_ID < 80000) {
|
||||
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class BinProxyWrapper
|
||||
{
|
||||
private $handle;
|
||||
private $position;
|
||||
private $realpath;
|
||||
|
||||
public function stream_open($path, $mode, $options, &$opened_path)
|
||||
{
|
||||
// get rid of phpvfscomposer:// prefix for __FILE__ & __DIR__ resolution
|
||||
$opened_path = substr($path, 17);
|
||||
$this->realpath = realpath($opened_path) ?: $opened_path;
|
||||
$opened_path = $this->realpath;
|
||||
$this->handle = fopen($this->realpath, $mode);
|
||||
$this->position = 0;
|
||||
|
||||
return (bool) $this->handle;
|
||||
}
|
||||
|
||||
public function stream_read($count)
|
||||
{
|
||||
$data = fread($this->handle, $count);
|
||||
|
||||
if ($this->position === 0) {
|
||||
$data = preg_replace('{^#!.*\r?\n}', '', $data);
|
||||
}
|
||||
|
||||
$this->position += strlen($data);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function stream_cast($castAs)
|
||||
{
|
||||
return $this->handle;
|
||||
}
|
||||
|
||||
public function stream_close()
|
||||
{
|
||||
fclose($this->handle);
|
||||
}
|
||||
|
||||
public function stream_lock($operation)
|
||||
{
|
||||
return $operation ? flock($this->handle, $operation) : true;
|
||||
}
|
||||
|
||||
public function stream_seek($offset, $whence)
|
||||
{
|
||||
if (0 === fseek($this->handle, $offset, $whence)) {
|
||||
$this->position = ftell($this->handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stream_tell()
|
||||
{
|
||||
return $this->position;
|
||||
}
|
||||
|
||||
public function stream_eof()
|
||||
{
|
||||
return feof($this->handle);
|
||||
}
|
||||
|
||||
public function stream_stat()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
public function stream_set_option($option, $arg1, $arg2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function url_stat($path, $flags)
|
||||
{
|
||||
$path = substr($path, 17);
|
||||
if (file_exists($path)) {
|
||||
return stat($path);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(function_exists('stream_get_wrappers') && in_array('phpvfscomposer', stream_get_wrappers(), true))
|
||||
|| (function_exists('stream_wrapper_register') && stream_wrapper_register('phpvfscomposer', 'Composer\BinProxyWrapper'))
|
||||
) {
|
||||
return include("phpvfscomposer://" . __DIR__ . '/..'.'/captainhook/captainhook/bin/captainhook');
|
||||
}
|
||||
}
|
||||
|
||||
return include __DIR__ . '/..'.'/captainhook/captainhook/bin/captainhook';
|
||||
5
lib/bin/captainhook.bat
Normal file
5
lib/bin/captainhook.bat
Normal file
@@ -0,0 +1,5 @@
|
||||
@ECHO OFF
|
||||
setlocal DISABLEDELAYEDEXPANSION
|
||||
SET BIN_TARGET=%~dp0/captainhook
|
||||
SET COMPOSER_RUNTIME_BIN_DIR=%~dp0
|
||||
php "%BIN_TARGET%" %*
|
||||
21
lib/captainhook/captainhook/LICENSE
Normal file
21
lib/captainhook/captainhook/LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016 Sebastian Feldmann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
88
lib/captainhook/captainhook/bin/captainhook
Normal file
88
lib/captainhook/captainhook/bin/captainhook
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/**
|
||||
* CaptainHook
|
||||
*
|
||||
* Copyright (c) 2016, Sebastian Feldmann <sf@sebnastian-feldmann.info>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
|
||||
use CaptainHook\App\Console\Application as CaptainHook;
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
|
||||
(static function($argv)
|
||||
{
|
||||
define('__CAPTAINHOOK_RUNNING__', true);
|
||||
|
||||
// check installation type [composer bin dir, git clone / phar, composer package dir]
|
||||
$composerAutoloadLocations = [
|
||||
__DIR__ . '/../autoload.php',
|
||||
__DIR__ . '/../vendor/autoload.php',
|
||||
__DIR__ . '/../../../autoload.php'
|
||||
];
|
||||
|
||||
foreach ($composerAutoloadLocations as $file) {
|
||||
if (file_exists($file)) {
|
||||
define('CAPTAINHOOK_COMPOSER_AUTOLOAD', $file);
|
||||
break;
|
||||
}
|
||||
}
|
||||
unset($file);
|
||||
|
||||
if (!defined('CAPTAINHOOK_COMPOSER_AUTOLOAD')) {
|
||||
fwrite(STDERR,
|
||||
'Autoloader could not be found:' . PHP_EOL .
|
||||
' Please run `composer install` to generate the autoloader' . PHP_EOL
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$GLOBALS['__composer_autoload_files'] = [];
|
||||
|
||||
try {
|
||||
require CAPTAINHOOK_COMPOSER_AUTOLOAD;
|
||||
} catch (Throwable $exception) {
|
||||
fwrite(STDERR,
|
||||
'Composer autoloader crashed:' . PHP_EOL .
|
||||
' Please update your autoloader by running `composer install`' . PHP_EOL .
|
||||
' You can re-run the hook by executing `' . implode(' ', $argv) . '`' . PHP_EOL
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
$captainHook = new CaptainHook($argv[0]);
|
||||
$captainHook->run(new ArgvInput($argv));
|
||||
}
|
||||
)($argv);
|
||||
|
||||
|
||||
77
lib/captainhook/captainhook/composer.json
Normal file
77
lib/captainhook/captainhook/composer.json
Normal file
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "captainhook/captainhook",
|
||||
"type": "library",
|
||||
"description": "PHP git hook manager",
|
||||
"keywords": ["git", "hooks", "pre-commit", "pre-push", "commit-msg", "prepare-commit-msg", "post-merge"],
|
||||
"homepage": "http://php.captainhook.info/",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Feldmann",
|
||||
"email": "sf@sebastian-feldmann.info"
|
||||
}
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/captainhook-git/captainhook/issues"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/sebastianfeldmann"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"CaptainHook\\App\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"CaptainHook\\App\\": "tests/unit/",
|
||||
"CaptainHook\\App\\Integration\\": "tests/integration/"
|
||||
}
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.0",
|
||||
"ext-json": "*",
|
||||
"ext-spl": "*",
|
||||
"ext-xml": "*",
|
||||
"captainhook/secrets": "^0.9.4",
|
||||
"sebastianfeldmann/camino": "^0.9.2",
|
||||
"sebastianfeldmann/cli": "^3.3",
|
||||
"sebastianfeldmann/git": "^3.14",
|
||||
"symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0",
|
||||
"symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0",
|
||||
"symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "~1 || ^2.0",
|
||||
"mikey179/vfsstream": "~1"
|
||||
},
|
||||
"bin": [
|
||||
"bin/captainhook"
|
||||
],
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "6.0.x-dev"
|
||||
},
|
||||
"captainhook": {
|
||||
"config": "captainhook.json"
|
||||
}
|
||||
},
|
||||
"replace" : {
|
||||
"sebastianfeldmann/captainhook": "*"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": "tools/phive install --force-accept-unsigned",
|
||||
"tools": "tools/phive install --force-accept-unsigned",
|
||||
"compile": "tools/box compile",
|
||||
"test": "tools/phpunit --testsuite UnitTests",
|
||||
"test:integration": "tools/phpunit --testsuite IntegrationTests --no-coverage",
|
||||
"static": "tools/phpstan analyse",
|
||||
"style": "tools/phpcs --standard=psr12 src tests"
|
||||
}
|
||||
}
|
||||
7
lib/captainhook/captainhook/phive.xml
Normal file
7
lib/captainhook/captainhook/phive.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phive xmlns="https://phar.io/phive">
|
||||
<phar name="phpunit" version="^9.4" installed="9.6.23" location="./tools/phpunit" copy="true"/>
|
||||
<phar name="humbug/box" version="^4.0" installed="4.6.6" location="./tools/box" copy="true"/>
|
||||
<phar name="phpcs" version="^3.5" installed="3.7.2" location="./tools/phpcs" copy="true"/>
|
||||
<phar name="phpstan" version="^1.0" installed="1.12.28" location="./tools/phpstan" copy="true"/>
|
||||
</phive>
|
||||
10
lib/captainhook/captainhook/phpcs.xml
Normal file
10
lib/captainhook/captainhook/phpcs.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<ruleset>
|
||||
<arg name="cache" value=".cs-check.json"/>
|
||||
<arg name="parallel" value="8" />
|
||||
|
||||
<rule ref="PSR12"/>
|
||||
|
||||
<file>src</file>
|
||||
<file>tests</file>
|
||||
</ruleset>
|
||||
45
lib/captainhook/captainhook/src/CH.php
Normal file
45
lib/captainhook/captainhook/src/CH.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CaptainHook\App;
|
||||
|
||||
/**
|
||||
* Class CH
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
final class CH
|
||||
{
|
||||
/**
|
||||
* Current CaptainHook version
|
||||
*/
|
||||
public const VERSION = '5.25.11';
|
||||
|
||||
/**
|
||||
* Release date of the current version
|
||||
*/
|
||||
public const RELEASE_DATE = '2025-08-12';
|
||||
|
||||
/**
|
||||
* Default configuration file
|
||||
*/
|
||||
public const CONFIG = 'captainhook.json';
|
||||
|
||||
/**
|
||||
* Minimal required version for the installer
|
||||
*/
|
||||
public const MIN_REQ_INSTALLER = '5.22.0';
|
||||
}
|
||||
449
lib/captainhook/captainhook/src/Config.php
Normal file
449
lib/captainhook/captainhook/src/Config.php
Normal file
@@ -0,0 +1,449 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App;
|
||||
|
||||
use CaptainHook\App\Config\Run;
|
||||
use InvalidArgumentException;
|
||||
use SebastianFeldmann\Camino\Check;
|
||||
|
||||
/**
|
||||
* Class Config
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
* @internal
|
||||
*/
|
||||
class Config
|
||||
{
|
||||
public const SETTING_ALLOW_FAILURE = 'allow-failure';
|
||||
public const SETTING_BOOTSTRAP = 'bootstrap';
|
||||
public const SETTING_COLORS = 'ansi-colors';
|
||||
public const SETTING_CUSTOM = 'custom';
|
||||
public const SETTING_GIT_DIR = 'git-directory';
|
||||
public const SETTING_INCLUDES = 'includes';
|
||||
public const SETTING_INCLUDES_LEVEL = 'includes-level';
|
||||
public const SETTING_LABEL = 'label';
|
||||
public const SETTING_RUN_EXEC = 'run-exec';
|
||||
public const SETTING_RUN_MODE = 'run-mode';
|
||||
public const SETTING_RUN_PATH = 'run-path';
|
||||
public const SETTING_RUN_GIT = 'run-git';
|
||||
public const SETTING_PHP_PATH = 'php-path';
|
||||
public const SETTING_VERBOSITY = 'verbosity';
|
||||
public const SETTING_FAIL_ON_FIRST_ERROR = 'fail-on-first-error';
|
||||
|
||||
/**
|
||||
* Path to the config file
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $path;
|
||||
|
||||
/**
|
||||
* Does the config file exist
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $fileExists;
|
||||
|
||||
/**
|
||||
* CaptainHook settings
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private array $settings;
|
||||
|
||||
/**
|
||||
* All options related to running CaptainHook
|
||||
*
|
||||
* @var \CaptainHook\App\Config\Run
|
||||
*/
|
||||
private Run $runConfig;
|
||||
|
||||
/**
|
||||
* List of users custom settings
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private array $custom = [];
|
||||
|
||||
/**
|
||||
* List of plugins
|
||||
*
|
||||
* @var array<string, \CaptainHook\App\Config\Plugin>
|
||||
*/
|
||||
private array $plugins = [];
|
||||
|
||||
/**
|
||||
* List of hook configs
|
||||
*
|
||||
* @var array<string, \CaptainHook\App\Config\Hook>
|
||||
*/
|
||||
private array $hooks = [];
|
||||
|
||||
/**
|
||||
* Config constructor
|
||||
*
|
||||
* @param string $path
|
||||
* @param bool $fileExists
|
||||
* @param array<string, mixed> $settings
|
||||
*/
|
||||
public function __construct(string $path, bool $fileExists = false, array $settings = [])
|
||||
{
|
||||
$settings = $this->setupPlugins($settings);
|
||||
$settings = $this->setupCustom($settings);
|
||||
$settings = $this->setupRunConfig($settings);
|
||||
|
||||
|
||||
$this->path = $path;
|
||||
$this->fileExists = $fileExists;
|
||||
$this->settings = $settings;
|
||||
|
||||
foreach (Hooks::getValidHooks() as $hook => $value) {
|
||||
$this->hooks[$hook] = new Config\Hook($hook);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract custom settings from Captain Hook ones
|
||||
*
|
||||
* @param array<string, mixed> $settings
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function setupCustom(array $settings): array
|
||||
{
|
||||
/* @var array<string, mixed> $custom */
|
||||
$this->custom = $settings['custom'] ?? [];
|
||||
unset($settings['custom']);
|
||||
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup all configured plugins
|
||||
*
|
||||
* @param array<string, mixed> $settings
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function setupPlugins(array $settings): array
|
||||
{
|
||||
/* @var array<int, array<string, mixed>> $pluginSettings */
|
||||
$pluginSettings = $settings['plugins'] ?? [];
|
||||
unset($settings['plugins']);
|
||||
|
||||
foreach ($pluginSettings as $plugin) {
|
||||
$name = (string) $plugin['plugin'];
|
||||
$options = isset($plugin['options']) && is_array($plugin['options'])
|
||||
? $plugin['options']
|
||||
: [];
|
||||
$this->plugins[$name] = new Config\Plugin($name, $options);
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract all running related settings into a run configuration
|
||||
*
|
||||
* @param array<string, mixed> $settings
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function setupRunConfig(array $settings): array
|
||||
{
|
||||
// extract the legacy settings
|
||||
$settingsToMove = [
|
||||
self::SETTING_RUN_MODE,
|
||||
self::SETTING_RUN_EXEC,
|
||||
self::SETTING_RUN_PATH,
|
||||
self::SETTING_RUN_GIT
|
||||
];
|
||||
$config = [];
|
||||
foreach ($settingsToMove as $setting) {
|
||||
if (!empty($settings[$setting])) {
|
||||
$config[substr($setting, 4)] = $settings[$setting];
|
||||
}
|
||||
unset($settings[$setting]);
|
||||
}
|
||||
// make sure the new run configuration supersedes the legacy settings
|
||||
if (isset($settings['run']) && is_array($settings['run'])) {
|
||||
$config = array_merge($config, $settings['run']);
|
||||
unset($settings['run']);
|
||||
}
|
||||
$this->runConfig = new Run($config);
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is configuration loaded from file
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLoadedFromFile(): bool
|
||||
{
|
||||
return $this->fileExists;
|
||||
}
|
||||
|
||||
/**
|
||||
* Are actions allowed to fail without stopping the git operation
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isFailureAllowed(): bool
|
||||
{
|
||||
return (bool) ($this->settings[self::SETTING_ALLOW_FAILURE] ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $hook
|
||||
* @param bool $withVirtual if true, also check if hook is enabled through any enabled virtual hook
|
||||
* @return bool
|
||||
*/
|
||||
public function isHookEnabled(string $hook, bool $withVirtual = true): bool
|
||||
{
|
||||
// either this hook is explicitly enabled
|
||||
$hookConfig = $this->getHookConfig($hook);
|
||||
if ($hookConfig->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// or any virtual hook that triggers it is enabled
|
||||
if ($withVirtual && Hooks::triggersVirtualHook($hookConfig->getName())) {
|
||||
$virtualHookConfig = $this->getHookConfig(Hooks::getVirtualHook($hookConfig->getName()));
|
||||
if ($virtualHookConfig->isEnabled()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Path getter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPath(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return git directory path if configured, CWD/.git if not
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGitDirectory(): string
|
||||
{
|
||||
if (empty($this->settings[self::SETTING_GIT_DIR])) {
|
||||
return getcwd() . '/.git';
|
||||
}
|
||||
|
||||
// if repo path is absolute use it otherwise create an absolute path relative to the configuration file
|
||||
return Check::isAbsolutePath($this->settings[self::SETTING_GIT_DIR])
|
||||
? $this->settings[self::SETTING_GIT_DIR]
|
||||
: dirname($this->path) . '/' . $this->settings[self::SETTING_GIT_DIR];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return bootstrap file if configured, CWD/vendor/autoload.php by default
|
||||
*
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public function getBootstrap(string $default = 'vendor/autoload.php'): string
|
||||
{
|
||||
return !empty($this->settings[self::SETTING_BOOTSTRAP])
|
||||
? $this->settings[self::SETTING_BOOTSTRAP]
|
||||
: $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured verbosity
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getVerbosity(): string
|
||||
{
|
||||
return !empty($this->settings[self::SETTING_VERBOSITY])
|
||||
? $this->settings[self::SETTING_VERBOSITY]
|
||||
: 'normal';
|
||||
}
|
||||
|
||||
/**
|
||||
* Should the output use ansi colors
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function useAnsiColors(): bool
|
||||
{
|
||||
return (bool) ($this->settings[self::SETTING_COLORS] ?? true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configured php-path
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPhpPath(): string
|
||||
{
|
||||
return (string) ($this->settings[self::SETTING_PHP_PATH] ?? '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get run configuration
|
||||
*
|
||||
* @return \CaptainHook\App\Config\Run
|
||||
*/
|
||||
public function getRunConfig(): Run
|
||||
{
|
||||
return $this->runConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the users custom config values
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function getCustomSettings(): array
|
||||
{
|
||||
return $this->custom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to abort the hook as soon as a any action has errored. Default is true.
|
||||
* Otherwise, all actions get executed (even if some of them have failed) and
|
||||
* finally, a non-zero exit code is returned if any action has errored.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function failOnFirstError(): bool
|
||||
{
|
||||
return (bool) ($this->settings[self::SETTING_FAIL_ON_FIRST_ERROR] ?? true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return config for given hook
|
||||
*
|
||||
* @param string $hook
|
||||
* @return \CaptainHook\App\Config\Hook
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function getHookConfig(string $hook): Config\Hook
|
||||
{
|
||||
if (!Hook\Util::isValid($hook)) {
|
||||
throw new InvalidArgumentException('Invalid hook name: ' . $hook);
|
||||
}
|
||||
return $this->hooks[$hook];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return hook configs
|
||||
*
|
||||
* @return array<string, \CaptainHook\App\Config\Hook>
|
||||
*/
|
||||
public function getHookConfigs(): array
|
||||
{
|
||||
return $this->hooks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hook config containing all the actions to execute
|
||||
*
|
||||
* Returns all actions from the triggered hook but also any actions of virtual hooks that might be triggered.
|
||||
* E.g. 'post-rewrite' or 'post-checkout' trigger the virtual/artificial 'post-change' hook.
|
||||
* Virtual hooks are special hooks to simplify configuration.
|
||||
*
|
||||
* @param string $hook
|
||||
* @return \CaptainHook\App\Config\Hook
|
||||
*/
|
||||
public function getHookConfigToExecute(string $hook): Config\Hook
|
||||
{
|
||||
$config = new Config\Hook($hook, true);
|
||||
$hookConfig = $this->getHookConfig($hook);
|
||||
$config->addAction(...$hookConfig->getActions());
|
||||
if (Hooks::triggersVirtualHook($hookConfig->getName())) {
|
||||
$vHookConfig = $this->getHookConfig(Hooks::getVirtualHook($hookConfig->getName()));
|
||||
if ($vHookConfig->isEnabled()) {
|
||||
$config->addAction(...$vHookConfig->getActions());
|
||||
}
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return plugins
|
||||
*
|
||||
* @return Config\Plugin[]
|
||||
*/
|
||||
public function getPlugins(): array
|
||||
{
|
||||
return $this->plugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return config array to write to disc
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getJsonData(): array
|
||||
{
|
||||
$data = [];
|
||||
$config = $this->getConfigJsonData();
|
||||
|
||||
if (!empty($config)) {
|
||||
$data['config'] = $config;
|
||||
}
|
||||
|
||||
foreach (Hooks::getValidHooks() as $hook => $value) {
|
||||
if ($this->hooks[$hook]->isEnabled() || $this->hooks[$hook]->hasActions()) {
|
||||
$data[$hook] = $this->hooks[$hook]->getJsonData();
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the "config" JSON section of the configuration file
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function getConfigJsonData(): array
|
||||
{
|
||||
$config = !empty($this->settings) ? $this->settings : [];
|
||||
|
||||
$runConfigData = $this->runConfig->getJsonData();
|
||||
if (!empty($runConfigData)) {
|
||||
$config['run'] = $runConfigData;
|
||||
}
|
||||
if (!empty($this->plugins)) {
|
||||
$config['plugins'] = $this->getPluginsJsonData();
|
||||
}
|
||||
if (!empty($this->custom)) {
|
||||
$config['custom'] = $this->custom;
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect and return plugin json data for all plugins
|
||||
*
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
private function getPluginsJsonData(): array
|
||||
{
|
||||
$plugins = [];
|
||||
foreach ($this->plugins as $plugin) {
|
||||
$plugins[] = $plugin->getJsonData();
|
||||
}
|
||||
return $plugins;
|
||||
}
|
||||
}
|
||||
236
lib/captainhook/captainhook/src/Config/Action.php
Normal file
236
lib/captainhook/captainhook/src/Config/Action.php
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Config;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
|
||||
/**
|
||||
* Class Action
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class Action
|
||||
{
|
||||
/**
|
||||
* Action php class, php static method, or cli script
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $action;
|
||||
|
||||
/**
|
||||
* Map of options name => value
|
||||
*
|
||||
* @var \CaptainHook\App\Config\Options
|
||||
*/
|
||||
private Options $options;
|
||||
|
||||
/**
|
||||
* List of action conditions
|
||||
*
|
||||
* @var \CaptainHook\App\Config\Condition[]
|
||||
*/
|
||||
private array $conditions = [];
|
||||
|
||||
/**
|
||||
* Action settings
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private array $settings = [];
|
||||
|
||||
/**
|
||||
* List of available settings
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private static array $availableSettings = [
|
||||
Config::SETTING_ALLOW_FAILURE,
|
||||
Config::SETTING_LABEL
|
||||
];
|
||||
|
||||
/**
|
||||
* Indicates if an action config was included from another file
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $isIncluded = false;
|
||||
|
||||
/**
|
||||
* Action constructor
|
||||
*
|
||||
* @param string $action
|
||||
* @param array<string, mixed> $options
|
||||
* @param array<string, mixed> $conditions
|
||||
* @param array<string, mixed> $settings
|
||||
*/
|
||||
public function __construct(string $action, array $options = [], array $conditions = [], array $settings = [])
|
||||
{
|
||||
$this->action = $action;
|
||||
$this->setupOptions($options);
|
||||
$this->setupConditions($conditions);
|
||||
$this->setupSettings($settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup options
|
||||
*
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
private function setupOptions(array $options): void
|
||||
{
|
||||
$this->options = new Options($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup action conditions
|
||||
*
|
||||
* @param array<string, array<string, mixed>> $conditions
|
||||
*/
|
||||
private function setupConditions(array $conditions): void
|
||||
{
|
||||
foreach ($conditions as $condition) {
|
||||
$this->conditions[] = new Condition($condition['exec'], $condition['args'] ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting up the action settings
|
||||
*
|
||||
* @param array<string, mixed> $settings
|
||||
* @return void
|
||||
*/
|
||||
private function setupSettings(array $settings): void
|
||||
{
|
||||
foreach (self::$availableSettings as $setting) {
|
||||
if (isset($settings[$setting])) {
|
||||
$this->settings[$setting] = $settings[$setting];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks a action config as included
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function markIncluded(): void
|
||||
{
|
||||
$this->isIncluded = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an action config was included
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isIncluded(): bool
|
||||
{
|
||||
return $this->isIncluded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the action can fail without stopping the git operation
|
||||
*
|
||||
* @param bool $default
|
||||
* @return bool
|
||||
*/
|
||||
public function isFailureAllowed(bool $default = false): bool
|
||||
{
|
||||
return (bool) ($this->settings[Config::SETTING_ALLOW_FAILURE] ?? $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the label or the action if no label is set
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLabel(): string
|
||||
{
|
||||
return (string) ($this->settings[Config::SETTING_LABEL] ?? $this->getAction());
|
||||
}
|
||||
|
||||
/**
|
||||
* Action getter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAction(): string
|
||||
{
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return option map
|
||||
*
|
||||
* @return \CaptainHook\App\Config\Options
|
||||
*/
|
||||
public function getOptions(): Options
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return condition configurations
|
||||
*
|
||||
* @return \CaptainHook\App\Config\Condition[]
|
||||
*/
|
||||
public function getConditions(): array
|
||||
{
|
||||
return $this->conditions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return config data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getJsonData(): array
|
||||
{
|
||||
$data = [
|
||||
'action' => $this->action
|
||||
];
|
||||
|
||||
$options = $this->options->getAll();
|
||||
if (!empty($options)) {
|
||||
$data['options'] = $options;
|
||||
}
|
||||
|
||||
$conditions = $this->getConditionJsonData();
|
||||
if (!empty($conditions)) {
|
||||
$data['conditions'] = $conditions;
|
||||
}
|
||||
|
||||
if (!empty($this->settings)) {
|
||||
$data['config'] = $this->settings;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return conditions json data
|
||||
*
|
||||
* @return array<int, mixed>
|
||||
*/
|
||||
private function getConditionJsonData(): array
|
||||
{
|
||||
$json = [];
|
||||
foreach ($this->conditions as $condition) {
|
||||
$json[] = $condition->getJsonData();
|
||||
}
|
||||
return $json;
|
||||
}
|
||||
}
|
||||
83
lib/captainhook/captainhook/src/Config/Condition.php
Normal file
83
lib/captainhook/captainhook/src/Config/Condition.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Config;
|
||||
|
||||
/**
|
||||
* Class Action
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 4.2.0
|
||||
* @internal
|
||||
*/
|
||||
class Condition
|
||||
{
|
||||
/**
|
||||
* Condition executable
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $exec;
|
||||
|
||||
/**
|
||||
* Condition arguments
|
||||
*
|
||||
* @var array<mixed>
|
||||
*/
|
||||
private array $args;
|
||||
|
||||
/**
|
||||
* Condition constructor
|
||||
*
|
||||
* @param string $exec
|
||||
* @param array<mixed> $args
|
||||
*/
|
||||
public function __construct(string $exec, array $args = [])
|
||||
{
|
||||
$this->exec = $exec;
|
||||
$this->args = $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exec getter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExec(): string
|
||||
{
|
||||
return $this->exec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Args getter
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function getArgs(): array
|
||||
{
|
||||
return $this->args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return config data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getJsonData(): array
|
||||
{
|
||||
return [
|
||||
'exec' => $this->exec,
|
||||
'args' => $this->args,
|
||||
];
|
||||
}
|
||||
}
|
||||
347
lib/captainhook/captainhook/src/Config/Factory.php
Normal file
347
lib/captainhook/captainhook/src/Config/Factory.php
Normal file
@@ -0,0 +1,347 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Config;
|
||||
|
||||
use CaptainHook\App\CH;
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Hook\Util as HookUtil;
|
||||
use CaptainHook\App\Storage\File\Json;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class Factory
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
* @internal
|
||||
*/
|
||||
final class Factory
|
||||
{
|
||||
/**
|
||||
* Maximal level in including config files
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private int $maxIncludeLevel = 1;
|
||||
|
||||
/**
|
||||
* Current level of inclusion
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private int $includeLevel = 0;
|
||||
|
||||
/**
|
||||
* Create a CaptainHook configuration
|
||||
*
|
||||
* @param string $path Path to the configuration file
|
||||
* @param array<string, mixed> $settings Settings passed as options on the command line
|
||||
* @return \CaptainHook\App\Config
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createConfig(string $path = '', array $settings = []): Config
|
||||
{
|
||||
$path = $path ?: getcwd() . DIRECTORY_SEPARATOR . CH::CONFIG;
|
||||
$file = new Json($path);
|
||||
$settings = $this->combineArgumentsAndSettingFile($file, $settings);
|
||||
|
||||
return $this->setupConfig($file, $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read settings from a local 'config' file
|
||||
*
|
||||
* If you prefer a different verbosity or use a different run mode locally then your teammates do.
|
||||
* You can create a 'captainhook.config.json' in the same directory as your captainhook
|
||||
* configuration file and use it to overwrite the 'config' settings of that configuration file.
|
||||
* Exclude the 'captainhook.config.json' from version control, and you don't have to edit the
|
||||
* version controlled configuration for your local specifics anymore.
|
||||
*
|
||||
* Settings provided as arguments still overrule config file settings:
|
||||
*
|
||||
* ARGUMENTS > SETTINGS_FILE > CONFIGURATION
|
||||
*
|
||||
* @param \CaptainHook\App\Storage\File\Json $file
|
||||
* @param array<string, mixed> $settings
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function combineArgumentsAndSettingFile(Json $file, array $settings): array
|
||||
{
|
||||
$settingsFile = new Json(dirname($file->getPath()) . '/captainhook.config.json');
|
||||
if ($settingsFile->exists()) {
|
||||
$fileSettings = $settingsFile->readAssoc();
|
||||
$settings = array_merge($fileSettings, $settings);
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Includes an external captainhook configuration
|
||||
*
|
||||
* @param string $path
|
||||
* @return \CaptainHook\App\Config
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function includeConfig(string $path): Config
|
||||
{
|
||||
$file = new Json($path);
|
||||
if (!$file->exists()) {
|
||||
throw new RuntimeException('Config to include not found: ' . $path);
|
||||
}
|
||||
return $this->setupConfig($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a configuration with data loaded from json file if it exists
|
||||
*
|
||||
* @param \CaptainHook\App\Storage\File\Json $file
|
||||
* @param array<string, mixed> $settings
|
||||
* @return \CaptainHook\App\Config
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function setupConfig(Json $file, array $settings = []): Config
|
||||
{
|
||||
return $file->exists()
|
||||
? $this->loadConfigFromFile($file, $settings)
|
||||
: new Config($file->getPath(), false, $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a given file into given the configuration
|
||||
*
|
||||
* @param \CaptainHook\App\Storage\File\Json $file
|
||||
* @param array<string, mixed> $settings
|
||||
* @return \CaptainHook\App\Config
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function loadConfigFromFile(Json $file, array $settings): Config
|
||||
{
|
||||
$json = $file->readAssoc();
|
||||
Util::validateJsonConfiguration($json);
|
||||
|
||||
$settings = Util::mergeSettings($this->extractSettings($json), $settings);
|
||||
$config = new Config($file->getPath(), true, $settings);
|
||||
if (!empty($settings)) {
|
||||
$json['config'] = $settings;
|
||||
}
|
||||
|
||||
$this->appendIncludedConfigurations($config, $json);
|
||||
|
||||
foreach (HookUtil::getValidHooks() as $hook => $class) {
|
||||
if (isset($json[$hook])) {
|
||||
$this->configureHook($config->getHookConfig($hook), $json[$hook]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->validatePhpPath($config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the `config` section of some json
|
||||
*
|
||||
* @param array<string, mixed> $json
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function extractSettings(array $json): array
|
||||
{
|
||||
return Util::extractListFromJson($json, 'config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `conditions` section of an actionJson
|
||||
*
|
||||
* @param array<string, mixed> $json
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function extractConditions(mixed $json): array
|
||||
{
|
||||
return Util::extractListFromJson($json, 'conditions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `options` section af some json
|
||||
*
|
||||
* @param array<string, mixed> $json
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function extractOptions(mixed $json): array
|
||||
{
|
||||
return Util::extractListFromJson($json, 'options');
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a hook configuration by json data
|
||||
*
|
||||
* @param \CaptainHook\App\Config\Hook $config
|
||||
* @param array<string, mixed> $json
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function configureHook(Config\Hook $config, array $json): void
|
||||
{
|
||||
$config->setEnabled($json['enabled'] ?? true);
|
||||
foreach ($json['actions'] as $actionJson) {
|
||||
$options = $this->extractOptions($actionJson);
|
||||
$conditions = $this->extractConditions($actionJson);
|
||||
$settings = $this->extractSettings($actionJson);
|
||||
$config->addAction(new Config\Action($actionJson['action'], $options, $conditions, $settings));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the configured PHP executable exists
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @return void
|
||||
*/
|
||||
private function validatePhpPath(Config $config): void
|
||||
{
|
||||
if (empty($config->getPhpPath())) {
|
||||
return;
|
||||
}
|
||||
$pathToCheck = [$config->getPhpPath()];
|
||||
$parts = explode(' ', $config->getPhpPath());
|
||||
// if there are spaces in the php-path and they are not escaped
|
||||
// it looks like an executable is used to find the PHP binary
|
||||
// so at least check if the executable exists
|
||||
if ($this->usesPathResolver($parts)) {
|
||||
$pathToCheck[] = $parts[0];
|
||||
}
|
||||
|
||||
foreach ($pathToCheck as $path) {
|
||||
if (file_exists($path)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException('The configured php-path is wrong: ' . $config->getPhpPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Is a binary used to resolve the php path
|
||||
*
|
||||
* @param array<int, string> $parts
|
||||
* @return bool
|
||||
*/
|
||||
private function usesPathResolver(array $parts): bool
|
||||
{
|
||||
return count($parts) > 1 && !str_ends_with($parts[0], '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Append all included configuration to the current configuration
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @param array<string, mixed> $json
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function appendIncludedConfigurations(Config $config, array $json): void
|
||||
{
|
||||
$this->readMaxIncludeLevel($json);
|
||||
|
||||
if ($this->includeLevel < $this->maxIncludeLevel) {
|
||||
$this->includeLevel++;
|
||||
$includes = $this->loadIncludedConfigs($json, $config->getPath());
|
||||
foreach (HookUtil::getValidHooks() as $hook => $class) {
|
||||
$this->mergeHookConfigFromIncludes($config->getHookConfig($hook), $includes);
|
||||
}
|
||||
$this->includeLevel--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check config section for 'includes-level' setting
|
||||
*
|
||||
* @param array<string, mixed> $json
|
||||
*/
|
||||
private function readMaxIncludeLevel(array $json): void
|
||||
{
|
||||
// read the include-level setting only for the actual configuration
|
||||
if ($this->includeLevel === 0 && isset($json['config'][Config::SETTING_INCLUDES_LEVEL])) {
|
||||
$this->maxIncludeLevel = (int) $json['config'][Config::SETTING_INCLUDES_LEVEL];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge a given hook config with the corresponding hook configs from a list of included configurations
|
||||
*
|
||||
* @param \CaptainHook\App\Config\Hook $hook
|
||||
* @param \CaptainHook\App\Config[] $includes
|
||||
* @return void
|
||||
*/
|
||||
private function mergeHookConfigFromIncludes(Hook $hook, array $includes): void
|
||||
{
|
||||
foreach ($includes as $includedConfig) {
|
||||
$includedHook = $includedConfig->getHookConfig($hook->getName());
|
||||
if ($includedHook->isEnabled()) {
|
||||
$hook->setEnabled(true);
|
||||
// This `setEnable` is solely to overwrite the main configuration in the special case that the hook
|
||||
// is not configured at all. In this case the empty config is disabled by default, and adding an
|
||||
// empty hook config just to enable the included actions feels a bit dull.
|
||||
// Since the main hook is processed last (if one is configured) the enabled flag will be overwritten
|
||||
// once again by the main config value. This is to make sure that if somebody disables a hook in its
|
||||
// main configuration, no actions will get executed, even if we have enabled hooks in any include file.
|
||||
$this->copyActionsFromTo($includedHook, $hook);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of included configurations to add them to the main configuration afterwards
|
||||
*
|
||||
* @param array<string, mixed> $json
|
||||
* @param string $path
|
||||
* @return \CaptainHook\App\Config[]
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function loadIncludedConfigs(array $json, string $path): array
|
||||
{
|
||||
$includes = [];
|
||||
$directory = dirname($path);
|
||||
$files = Util::extractListFromJson(Util::extractListFromJson($json, 'config'), Config::SETTING_INCLUDES);
|
||||
|
||||
foreach ($files as $file) {
|
||||
$includes[] = $this->includeConfig($directory . DIRECTORY_SEPARATOR . $file);
|
||||
}
|
||||
return $includes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy action from a given configuration to the second given configuration
|
||||
*
|
||||
* @param \CaptainHook\App\Config\Hook $sourceConfig
|
||||
* @param \CaptainHook\App\Config\Hook $targetConfig
|
||||
*/
|
||||
private function copyActionsFromTo(Hook $sourceConfig, Hook $targetConfig): void
|
||||
{
|
||||
foreach ($sourceConfig->getActions() as $action) {
|
||||
$action->markIncluded();
|
||||
$targetConfig->addAction($action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Config factory method
|
||||
*
|
||||
* @param string $path
|
||||
* @param array<string, mixed> $settings
|
||||
* @return \CaptainHook\App\Config
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function create(string $path = '', array $settings = []): Config
|
||||
{
|
||||
$factory = new static();
|
||||
return $factory->createConfig($path, $settings);
|
||||
}
|
||||
}
|
||||
137
lib/captainhook/captainhook/src/Config/Hook.php
Normal file
137
lib/captainhook/captainhook/src/Config/Hook.php
Normal file
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Config;
|
||||
|
||||
/**
|
||||
* Class Hook
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
* @internal
|
||||
*/
|
||||
class Hook
|
||||
{
|
||||
/**
|
||||
* Hook name e.g. pre-commit
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $name;
|
||||
|
||||
/**
|
||||
* Is hook enabled
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $isEnabled;
|
||||
|
||||
/**
|
||||
* List of Actions
|
||||
*
|
||||
* @var \CaptainHook\App\Config\Action[]
|
||||
*/
|
||||
private $actions = [];
|
||||
|
||||
/**
|
||||
* Hook constructor
|
||||
*
|
||||
* @param string $name
|
||||
* @param bool $enabled
|
||||
*/
|
||||
public function __construct(string $name, bool $enabled = false)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->isEnabled = $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Name getter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the hook
|
||||
*
|
||||
* @param bool $enabled
|
||||
* @return void
|
||||
*/
|
||||
public function setEnabled(bool $enabled): void
|
||||
{
|
||||
$this->isEnabled = $enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this hook enabled
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled(): bool
|
||||
{
|
||||
return $this->isEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a hook config has actions
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasActions(): bool
|
||||
{
|
||||
return !empty($this->actions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an action to the list
|
||||
*
|
||||
* @param \CaptainHook\App\Config\Action ...$actions
|
||||
* @return void
|
||||
*/
|
||||
public function addAction(Action ...$actions): void
|
||||
{
|
||||
foreach ($actions as $action) {
|
||||
$this->actions[] = $action;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the action list
|
||||
*
|
||||
* @return \CaptainHook\App\Config\Action[]
|
||||
*/
|
||||
public function getActions(): array
|
||||
{
|
||||
return $this->actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return config data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getJsonData(): array
|
||||
{
|
||||
$config = ['enabled' => $this->isEnabled, 'actions' => []];
|
||||
foreach ($this->actions as $action) {
|
||||
if (!$action->isIncluded()) {
|
||||
$config['actions'][] = $action->getJsonData();
|
||||
}
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
}
|
||||
63
lib/captainhook/captainhook/src/Config/Options.php
Normal file
63
lib/captainhook/captainhook/src/Config/Options.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Config;
|
||||
|
||||
/**
|
||||
* Class Options
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 1.0.0
|
||||
*/
|
||||
class Options
|
||||
{
|
||||
/**
|
||||
* Map of options
|
||||
*
|
||||
* @var array<string, mixed>
|
||||
*/
|
||||
private array $options;
|
||||
|
||||
/**
|
||||
* Options constructor
|
||||
*
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function __construct(array $options)
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a option value
|
||||
*
|
||||
* @template ProvidedDefault
|
||||
* @param string $name
|
||||
* @param ProvidedDefault $default
|
||||
* @return ProvidedDefault|mixed
|
||||
*/
|
||||
public function get(string $name, $default = null)
|
||||
{
|
||||
return $this->options[$name] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all options
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getAll(): array
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
}
|
||||
99
lib/captainhook/captainhook/src/Config/Plugin.php
Normal file
99
lib/captainhook/captainhook/src/Config/Plugin.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Config;
|
||||
|
||||
use CaptainHook\App\Exception\InvalidPlugin;
|
||||
use CaptainHook\App\Plugin\CaptainHook;
|
||||
|
||||
/**
|
||||
* Class Plugin
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.9.0
|
||||
*/
|
||||
class Plugin
|
||||
{
|
||||
/**
|
||||
* Plugin class
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $plugin;
|
||||
|
||||
/**
|
||||
* Map of options name => value
|
||||
*
|
||||
* @var Options
|
||||
*/
|
||||
private Options $options;
|
||||
|
||||
/**
|
||||
* Plugin constructor
|
||||
*
|
||||
* @param string $plugin
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function __construct(string $plugin, array $options = [])
|
||||
{
|
||||
if (!is_a($plugin, CaptainHook::class, true)) {
|
||||
throw new InvalidPlugin("{$plugin} is not a valid CaptainHook plugin.");
|
||||
}
|
||||
|
||||
$this->plugin = $plugin;
|
||||
$this->setupOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup options
|
||||
*
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
private function setupOptions(array $options): void
|
||||
{
|
||||
$this->options = new Options($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin class name getter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getPlugin(): string
|
||||
{
|
||||
return $this->plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return option map
|
||||
*
|
||||
* @return Options
|
||||
*/
|
||||
public function getOptions(): Options
|
||||
{
|
||||
return $this->options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return config data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getJsonData(): array
|
||||
{
|
||||
return [
|
||||
'plugin' => $this->plugin,
|
||||
'options' => $this->options->getAll(),
|
||||
];
|
||||
}
|
||||
}
|
||||
108
lib/captainhook/captainhook/src/Config/Run.php
Normal file
108
lib/captainhook/captainhook/src/Config/Run.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Config;
|
||||
|
||||
/**
|
||||
* Run Config
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.18.0
|
||||
*/
|
||||
class Run
|
||||
{
|
||||
private const MODE = 'mode';
|
||||
private const PATH = 'path';
|
||||
private const EXEC = 'exec';
|
||||
private const GIT = 'git';
|
||||
|
||||
/**
|
||||
* Map of options name => value
|
||||
*
|
||||
* @var \CaptainHook\App\Config\Options
|
||||
*/
|
||||
private Options $options;
|
||||
|
||||
/**
|
||||
* Run constructor
|
||||
*
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->setupOptions($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup options
|
||||
*
|
||||
* @param array<string, mixed> $options
|
||||
*/
|
||||
private function setupOptions(array $options): void
|
||||
{
|
||||
$this->options = new Options($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the run mode shell|docker|php|local|wsl
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMode(): string
|
||||
{
|
||||
return $this->options->get(self::MODE, 'shell');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path to the captain from within the container or to overwrite symlink resolution
|
||||
*
|
||||
* Since realpath() returns the real absolute path and not the absolute symlink path this
|
||||
* setting could be used to overwrite this behaviour.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCaptainsPath(): string
|
||||
{
|
||||
return $this->options->get(self::PATH, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the docker command to use to execute the captain
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getDockerCommand(): string
|
||||
{
|
||||
return $this->options->get(self::EXEC, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path mapping setting
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getGitPath(): string
|
||||
{
|
||||
return $this->options->get(self::GIT, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return config data
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getJsonData(): array
|
||||
{
|
||||
return $this->options->getAll();
|
||||
}
|
||||
}
|
||||
193
lib/captainhook/captainhook/src/Config/Util.php
Normal file
193
lib/captainhook/captainhook/src/Config/Util.php
Normal file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Config;
|
||||
|
||||
use CaptainHook\App\Hook\Util as HookUtil;
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Storage\File\Json;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class Util
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 1.0.3
|
||||
* @internal
|
||||
*/
|
||||
abstract class Util
|
||||
{
|
||||
/**
|
||||
* Validate a configuration
|
||||
*
|
||||
* @param array<string, mixed> $json
|
||||
* @return void
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function validateJsonConfiguration(array $json): void
|
||||
{
|
||||
self::validatePluginConfig($json);
|
||||
|
||||
foreach (HookUtil::getValidHooks() as $hook => $class) {
|
||||
if (isset($json[$hook])) {
|
||||
self::validateHookConfig($json[$hook]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a hook configuration
|
||||
*
|
||||
* @param array<string, mixed> $json
|
||||
* @return void
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function validateHookConfig(array $json): void
|
||||
{
|
||||
if (!self::keysExist(['enabled', 'actions'], $json)) {
|
||||
throw new RuntimeException('Config error: invalid hook configuration');
|
||||
}
|
||||
if (!is_array($json['actions'])) {
|
||||
throw new RuntimeException('Config error: \'actions\' must be an array');
|
||||
}
|
||||
self::validateActionsConfig($json['actions']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a plugin configuration
|
||||
*
|
||||
* @param array<string, mixed> $json
|
||||
* @return void
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function validatePluginConfig(array $json): void
|
||||
{
|
||||
if (!isset($json['config']['plugins'])) {
|
||||
return;
|
||||
}
|
||||
if (!is_array($json['config']['plugins'])) {
|
||||
throw new RuntimeException('Config error: \'plugins\' must be an array');
|
||||
}
|
||||
foreach ($json['config']['plugins'] as $plugin) {
|
||||
if (!self::keysExist(['plugin'], $plugin)) {
|
||||
throw new RuntimeException('Config error: \'plugin\' missing');
|
||||
}
|
||||
if (empty($plugin['plugin'])) {
|
||||
throw new RuntimeException('Config error: \'plugin\' can\'t be empty');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a list of action configurations
|
||||
*
|
||||
* @param array<string, mixed> $json
|
||||
* @return void
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function validateActionsConfig(array $json): void
|
||||
{
|
||||
foreach ($json as $action) {
|
||||
if (!self::keysExist(['action'], $action)) {
|
||||
throw new RuntimeException('Config error: \'action\' missing');
|
||||
}
|
||||
if (empty($action['action'])) {
|
||||
throw new RuntimeException('Config error: \'action\' can\'t be empty');
|
||||
}
|
||||
if (!empty($action['conditions'])) {
|
||||
self::validateConditionsConfig($action['conditions']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a list of condition configurations
|
||||
*
|
||||
* @param array<int, array<string, mixed>> $json
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public static function validateConditionsConfig(array $json): void
|
||||
{
|
||||
foreach ($json as $condition) {
|
||||
if (!self::keysExist(['exec'], $condition) || empty($condition['exec'])) {
|
||||
throw new RuntimeException('Config error: \'exec\' is required for conditions');
|
||||
}
|
||||
if (!empty($condition['args']) && !is_array($condition['args'])) {
|
||||
throw new RuntimeException('Config error: invalid \'args\' configuration');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a list from a json data struct with the necessary safeguards
|
||||
*
|
||||
* @param array<string, mixed> $json
|
||||
* @param string $value
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function extractListFromJson(array $json, string $value): array
|
||||
{
|
||||
return isset($json[$value]) && is_array($json[$value]) ? $json[$value] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the config to disk
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @return void
|
||||
*/
|
||||
public static function writeToDisk(Config $config): void
|
||||
{
|
||||
$filePath = $config->getPath();
|
||||
$file = new Json($filePath);
|
||||
$file->write($config->getJsonData());
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges a various list of settings arrays
|
||||
*
|
||||
* @param array<string, mixed> $settings
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public static function mergeSettings(array ...$settings): array
|
||||
{
|
||||
$includes = array_column($settings, Config::SETTING_INCLUDES);
|
||||
$custom = array_column($settings, Config::SETTING_CUSTOM);
|
||||
$mergedSettings = array_merge(...$settings);
|
||||
if (!empty($includes)) {
|
||||
$mergedSettings[Config::SETTING_INCLUDES] = array_merge(...$includes);
|
||||
}
|
||||
if (!empty($custom)) {
|
||||
$mergedSettings[Config::SETTING_CUSTOM] = array_merge(...$custom);
|
||||
}
|
||||
|
||||
return $mergedSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does an array have the expected keys
|
||||
*
|
||||
* @param array<string> $keys
|
||||
* @param array<string, mixed> $subject
|
||||
* @return bool
|
||||
*/
|
||||
private static function keysExist(array $keys, array $subject): bool
|
||||
{
|
||||
foreach ($keys as $key) {
|
||||
if (!isset($subject[$key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
143
lib/captainhook/captainhook/src/Console/Application.php
Normal file
143
lib/captainhook/captainhook/src/Console/Application.php
Normal file
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console;
|
||||
|
||||
use CaptainHook\App\CH;
|
||||
use CaptainHook\App\Console\Command as Cmd;
|
||||
use CaptainHook\App\Console\Runtime\Resolver;
|
||||
use Symfony\Component\Console\Application as SymfonyApplication;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class Application
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class Application extends SymfonyApplication
|
||||
{
|
||||
/**
|
||||
* Path to captainhook binary
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $executable;
|
||||
|
||||
/**
|
||||
* Cli constructor.
|
||||
*
|
||||
* @param string $executable
|
||||
*/
|
||||
public function __construct(string $executable)
|
||||
{
|
||||
$this->executable = $executable;
|
||||
|
||||
parent::__construct('CaptainHook', CH::VERSION);
|
||||
|
||||
$this->setDefaultCommand('list');
|
||||
$this->silenceXDebug();
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure the list command is run on default `-h|--help` executions
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
* @throws \Symfony\Component\Console\Exception\ExceptionInterface
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function doRun(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if ($this->isHelpWithoutCommand($input)) {
|
||||
// Run the `list` command not `list --help`
|
||||
return $this->find('list')->run($input, $output);
|
||||
}
|
||||
return parent::doRun($input, $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all the CaptainHook commands
|
||||
*
|
||||
* @return \Symfony\Component\Console\Command\Command[]
|
||||
*/
|
||||
public function getDefaultCommands(): array
|
||||
{
|
||||
$resolver = new Resolver($this->executable);
|
||||
$symfonyDefaults = parent::getDefaultCommands();
|
||||
|
||||
return array_merge(
|
||||
array_slice($symfonyDefaults, 0, 2),
|
||||
[
|
||||
new Cmd\Install($resolver),
|
||||
new Cmd\Uninstall($resolver),
|
||||
new Cmd\Configuration($resolver),
|
||||
new Cmd\Info($resolver),
|
||||
new Cmd\Add($resolver),
|
||||
new Cmd\Disable($resolver),
|
||||
new Cmd\Enable($resolver),
|
||||
new Cmd\Hook\CommitMsg($resolver),
|
||||
new Cmd\Hook\PostCheckout($resolver),
|
||||
new Cmd\Hook\PostCommit($resolver),
|
||||
new Cmd\Hook\PostMerge($resolver),
|
||||
new Cmd\Hook\PostRewrite($resolver),
|
||||
new Cmd\Hook\PreCommit($resolver),
|
||||
new Cmd\Hook\PrepareCommitMsg($resolver),
|
||||
new Cmd\Hook\PrePush($resolver),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append release date to version output
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLongVersion(): string
|
||||
{
|
||||
return sprintf(
|
||||
'<info>%s</info> version <comment>%s</comment> %s <fg=blue>#StandWith</><fg=yellow>Ukraine</>',
|
||||
$this->getName(),
|
||||
$this->getVersion(),
|
||||
CH::RELEASE_DATE
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure X-Debug does not interfere with the exception handling
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
private function silenceXDebug(): void
|
||||
{
|
||||
if (function_exists('ini_set') && extension_loaded('xdebug')) {
|
||||
ini_set('xdebug.show_exception_trace', '0');
|
||||
ini_set('xdebug.scream', '0');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the --help is called without any sub command
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @return bool
|
||||
*/
|
||||
private function isHelpWithoutCommand(InputInterface $input): bool
|
||||
{
|
||||
return $input->hasParameterOption(['--help', '-h'], true) && !$input->getFirstArgument();
|
||||
}
|
||||
}
|
||||
103
lib/captainhook/captainhook/src/Console/Command.php
Normal file
103
lib/captainhook/captainhook/src/Console/Command.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console;
|
||||
|
||||
use CaptainHook\App\Console\Runtime\Resolver;
|
||||
use Symfony\Component\Console\Command\Command as SymfonyCommand;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class Command
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.0.0
|
||||
*/
|
||||
abstract class Command extends SymfonyCommand
|
||||
{
|
||||
/**
|
||||
* Input output handler
|
||||
*
|
||||
* @var \CaptainHook\App\Console\IO|null
|
||||
*/
|
||||
private ?IO $io = null;
|
||||
|
||||
/**
|
||||
* Runtime resolver
|
||||
*
|
||||
* @var \CaptainHook\App\Console\Runtime\Resolver
|
||||
*/
|
||||
protected Resolver $resolver;
|
||||
|
||||
/**
|
||||
* Command constructor
|
||||
*
|
||||
* @param \CaptainHook\App\Console\Runtime\Resolver $resolver
|
||||
*/
|
||||
public function __construct(Resolver $resolver)
|
||||
{
|
||||
$this->resolver = $resolver;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* IO setter
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
*/
|
||||
public function setIO(IO $io): void
|
||||
{
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
/**
|
||||
* IO interface getter
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return \CaptainHook\App\Console\IO
|
||||
*/
|
||||
public function getIO(InputInterface $input, OutputInterface $output): IO
|
||||
{
|
||||
if (null === $this->io) {
|
||||
$this->io = new IO\DefaultIO($input, $output, $this->getHelperSet());
|
||||
}
|
||||
return $this->io;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a final error message
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $out
|
||||
* @param \Throwable $t
|
||||
* @return int
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function crash(OutputInterface $out, Throwable $t): int
|
||||
{
|
||||
if ($out->isDebug()) {
|
||||
throw $t;
|
||||
}
|
||||
|
||||
$out->writeln('<fg=red>' . $t->getMessage() . '</>');
|
||||
if ($out->isVerbose()) {
|
||||
$out->writeln(
|
||||
'<comment>Error triggered in file:</comment> ' . $t->getFile() .
|
||||
' <comment>in line:</comment> ' . $t->getLine()
|
||||
);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
73
lib/captainhook/captainhook/src/Console/Command/Add.php
Normal file
73
lib/captainhook/captainhook/src/Console/Command/Add.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command;
|
||||
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use CaptainHook\App\Runner\Config\Editor;
|
||||
use Exception;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class Add
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 4.2.0
|
||||
*/
|
||||
class Add extends ConfigAware
|
||||
{
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('config:add')
|
||||
->setAliases(['add'])
|
||||
->setDescription('Add an action to your hook configuration')
|
||||
->setHelp('Add an action to your hook configuration')
|
||||
->addArgument('hook', InputArgument::REQUIRED, 'Hook you want to add the action to');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
* @throws \CaptainHook\App\Exception\InvalidHookName
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
try {
|
||||
$io = $this->getIO($input, $output);
|
||||
$config = $this->createConfig($input, true);
|
||||
|
||||
$this->determineVerbosity($output, $config);
|
||||
|
||||
$editor = new Editor($io, $config);
|
||||
$editor->setHook(IOUtil::argToString($input->getArgument('hook')))
|
||||
->setChange('AddAction')
|
||||
->run();
|
||||
|
||||
return 0;
|
||||
} catch (Exception $e) {
|
||||
return $this->crash($output, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
129
lib/captainhook/captainhook/src/Console/Command/ConfigAware.php
Normal file
129
lib/captainhook/captainhook/src/Console/Command/ConfigAware.php
Normal file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command;
|
||||
|
||||
use CaptainHook\App\CH;
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Console\Command;
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use RuntimeException;
|
||||
use SebastianFeldmann\Camino\Check;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class ConfigAware
|
||||
*
|
||||
* Base class for all commands that need to be aware of the CaptainHook configuration.
|
||||
*
|
||||
* @package CaptainHook\App
|
||||
*/
|
||||
abstract class ConfigAware extends Command
|
||||
{
|
||||
/**
|
||||
* Set up the configuration command option
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->addOption(
|
||||
'configuration',
|
||||
'c',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Path to your captainhook.json configuration',
|
||||
'./' . CH::CONFIG
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Config object
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param bool $failIfNotFound
|
||||
* @param array<string> $settings
|
||||
* @return \CaptainHook\App\Config
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function createConfig(InputInterface $input, bool $failIfNotFound = false, array $settings = []): Config
|
||||
{
|
||||
$config = Config\Factory::create($this->getConfigPath($input), $this->fetchConfigSettings($input, $settings));
|
||||
if ($failIfNotFound && !$config->isLoadedFromFile()) {
|
||||
throw new RuntimeException(
|
||||
'Please create a captainhook configuration first' . PHP_EOL .
|
||||
'Run \'captainhook configure\'' . PHP_EOL .
|
||||
'If you have a configuration located elsewhere use the --configuration option'
|
||||
);
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the given config path option
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @return string
|
||||
*/
|
||||
private function getConfigPath(InputInterface $input): string
|
||||
{
|
||||
$path = IOUtil::argToString($input->getOption('configuration'));
|
||||
|
||||
// if path not absolute
|
||||
if (!Check::isAbsolutePath($path)) {
|
||||
// try to guess the config location and
|
||||
// transform relative path to absolute path
|
||||
if (substr($path, 0, 2) === './') {
|
||||
return getcwd() . substr($path, 1);
|
||||
}
|
||||
return getcwd() . '/' . $path;
|
||||
}
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of available options to overwrite the configuration settings
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param array<string> $settingNames
|
||||
* @return array<string, string>
|
||||
*/
|
||||
private function fetchConfigSettings(InputInterface $input, array $settingNames): array
|
||||
{
|
||||
$settings = [];
|
||||
foreach ($settingNames as $setting) {
|
||||
$value = IOUtil::argToString($input->getOption($setting));
|
||||
if (!empty($value)) {
|
||||
$settings[$setting] = $value;
|
||||
}
|
||||
}
|
||||
return $settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check which verbosity to use, either the cli option or the config file setting
|
||||
*
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $out
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @return void
|
||||
*/
|
||||
protected function determineVerbosity(OutputInterface $out, Config $config): void
|
||||
{
|
||||
$verbosity = IOUtil::mapConfigVerbosity($config->getVerbosity());
|
||||
$cliVerbosity = $out->getVerbosity();
|
||||
if ($cliVerbosity !== OutputInterface::VERBOSITY_NORMAL) {
|
||||
$verbosity = $cliVerbosity;
|
||||
}
|
||||
$out->setVerbosity($verbosity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command;
|
||||
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use CaptainHook\App\Console\Runtime\Resolver;
|
||||
use CaptainHook\App\Runner\Config\Creator;
|
||||
use Exception;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class Config
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class Configuration extends ConfigAware
|
||||
{
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('configure')
|
||||
->setDescription('Create or update a captainhook.json configuration')
|
||||
->setHelp('Create or update a captainhook.json configuration')
|
||||
->addOption('extend', 'e', InputOption::VALUE_NONE, 'Extend existing configuration file')
|
||||
->addOption('force', 'f', InputOption::VALUE_NONE, 'Overwrite existing configuration file')
|
||||
->addOption('advanced', 'a', InputOption::VALUE_NONE, 'More options, but more to type')
|
||||
->addOption(
|
||||
'bootstrap',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Path to composers vendor/autoload.php'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
try {
|
||||
$io = $this->getIO($input, $output);
|
||||
$config = $this->createConfig($input, false, ['bootstrap']);
|
||||
|
||||
$this->determineVerbosity($output, $config);
|
||||
|
||||
$configurator = new Creator($io, $config);
|
||||
$configurator->force(IOUtil::argToBool($input->getOption('force')))
|
||||
->extend(IOUtil::argToBool($input->getOption('extend')))
|
||||
->advanced(IOUtil::argToBool($input->getOption('advanced')))
|
||||
->setExecutable($this->resolver->getExecutable())
|
||||
->run();
|
||||
|
||||
return 0;
|
||||
} catch (Exception $e) {
|
||||
return $this->crash($output, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
lib/captainhook/captainhook/src/Console/Command/Disable.php
Normal file
73
lib/captainhook/captainhook/src/Console/Command/Disable.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command;
|
||||
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use CaptainHook\App\Runner\Config\Editor;
|
||||
use Exception;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class Add
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 4.2.0
|
||||
*/
|
||||
class Disable extends ConfigAware
|
||||
{
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('config:disable')
|
||||
->setAliases(['disable'])
|
||||
->setDescription('Disable the handling for a hook in your configuration')
|
||||
->setHelp('Disable the handling for a hook in your configuration')
|
||||
->addArgument('hook', InputArgument::REQUIRED, 'Hook you want to disable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
* @throws \CaptainHook\App\Exception\InvalidHookName
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
try {
|
||||
$io = $this->getIO($input, $output);
|
||||
$config = $this->createConfig($input, true);
|
||||
|
||||
$this->determineVerbosity($output, $config);
|
||||
|
||||
$editor = new Editor($io, $config);
|
||||
$editor->setHook(IOUtil::argToString($input->getArgument('hook')))
|
||||
->setChange('DisableHook')
|
||||
->run();
|
||||
|
||||
return 0;
|
||||
} catch (Exception $e) {
|
||||
return $this->crash($output, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
lib/captainhook/captainhook/src/Console/Command/Enable.php
Normal file
73
lib/captainhook/captainhook/src/Console/Command/Enable.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command;
|
||||
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use CaptainHook\App\Runner\Config\Editor;
|
||||
use Exception;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class Add
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 4.2.0
|
||||
*/
|
||||
class Enable extends ConfigAware
|
||||
{
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('config:enable')
|
||||
->setAliases(['enable'])
|
||||
->setDescription('Enable the handling for a hook in your configuration')
|
||||
->setHelp('Enable the handling for a hook in your configuration')
|
||||
->addArgument('hook', InputArgument::REQUIRED, 'Hook you want to enable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
* @throws \CaptainHook\App\Exception\InvalidHookName
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
try {
|
||||
$io = $this->getIO($input, $output);
|
||||
$config = $this->createConfig($input, true);
|
||||
|
||||
$this->determineVerbosity($output, $config);
|
||||
|
||||
$editor = new Editor($io, $config);
|
||||
$editor->setHook(IOUtil::argToString($input->getArgument('hook')))
|
||||
->setChange('EnableHook')
|
||||
->run();
|
||||
|
||||
return 0;
|
||||
} catch (Exception $e) {
|
||||
return $this->crash($output, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
159
lib/captainhook/captainhook/src/Console/Command/Hook.php
Normal file
159
lib/captainhook/captainhook/src/Console/Command/Hook.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Hook\Util as HookUtil;
|
||||
use CaptainHook\App\Runner\Bootstrap\Util as BootstrapUtil;
|
||||
use CaptainHook\App\Runner\Util as RunnerUtil;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class Hook
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
abstract class Hook extends RepositoryAware
|
||||
{
|
||||
/**
|
||||
* Name of the hook to execute
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $hookName;
|
||||
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('hook:' . $this->hookName)
|
||||
->setAliases([$this->hookName])
|
||||
->setDescription('Run git ' . $this->hookName . ' hook')
|
||||
->setHelp('This command executes the ' . $this->hookName . ' hook');
|
||||
|
||||
$this->addOption(
|
||||
'bootstrap',
|
||||
'b',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Relative path from your config file to your bootstrap file'
|
||||
);
|
||||
$this->addOption(
|
||||
'input',
|
||||
'i',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Original hook stdIn'
|
||||
);
|
||||
$this->addOption(
|
||||
'no-plugins',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Disable all hook plugins'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
* @throws \Throwable
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
if ($this->shouldHooksBeSkipped()) {
|
||||
$output->writeLn('all hooks were skipped because of the environment variable CAPTAINHOOK_SKIP_HOOKS or CI');
|
||||
return 0;
|
||||
}
|
||||
|
||||
$io = $this->getIO($input, $output);
|
||||
$config = $this->createConfig($input, true, ['git-directory', 'bootstrap']);
|
||||
$repository = $this->createRepository(dirname($config->getGitDirectory()));
|
||||
|
||||
// use ansi coloring if available and not disabled in captainhook.json
|
||||
$output->setDecorated($output->isDecorated() && $config->useAnsiColors());
|
||||
// use the configured verbosity to manage general output verbosity
|
||||
$this->determineVerbosity($output, $config);
|
||||
|
||||
try {
|
||||
$this->handleBootstrap($config);
|
||||
|
||||
$class = '\\CaptainHook\\App\\Runner\\Hook\\' . HookUtil::getHookCommand($this->hookName);
|
||||
/** @var \CaptainHook\App\Runner\Hook $hook */
|
||||
$hook = new $class($io, $config, $repository);
|
||||
$hook->setPluginsDisabled($input->getOption('no-plugins'));
|
||||
$hook->run();
|
||||
|
||||
return 0;
|
||||
} catch (Throwable $t) {
|
||||
return $this->crash($output, $t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If CaptainHook is executed via PHAR this handles the bootstrap file inclusion
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
*/
|
||||
private function handleBootstrap(Config $config): void
|
||||
{
|
||||
// we only have to care about bootstrapping PHAR builds because for
|
||||
// Composer installations the bootstrapping is already done in the bin script
|
||||
if ($this->resolver->isPharRelease()) {
|
||||
// check the custom and default autoloader
|
||||
$bootstrapFile = BootstrapUtil::validateBootstrapPath($this->resolver->isPharRelease(), $config);
|
||||
// since the phar has its own autoloader, we don't need to do anything
|
||||
// if the bootstrap file is not actively set
|
||||
if (empty($bootstrapFile)) {
|
||||
return;
|
||||
}
|
||||
// the bootstrap file exists, so let's load it
|
||||
try {
|
||||
require $bootstrapFile;
|
||||
} catch (Throwable $t) {
|
||||
throw new RuntimeException(
|
||||
'Loading bootstrap file failed: ' . $bootstrapFile . PHP_EOL .
|
||||
$t->getMessage() . PHP_EOL
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if hooks should be skipped
|
||||
*
|
||||
* Either because of CI environment or the SKIP environment variable is set.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function shouldHooksBeSkipped(): bool
|
||||
{
|
||||
foreach (['CAPTAINHOOK_SKIP_HOOKS', 'CI'] as $envVar) {
|
||||
$skip = (int) RunnerUtil::getEnv($envVar, "0");
|
||||
if ($skip === 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command\Hook;
|
||||
|
||||
use CaptainHook\App\Console\Command\Hook;
|
||||
use CaptainHook\App\Hooks;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
/**
|
||||
* Class CommitMessage
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class CommitMsg extends Hook
|
||||
{
|
||||
/**
|
||||
* Hook to execute
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $hookName = Hooks::COMMIT_MSG;
|
||||
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->addArgument(Hooks::ARG_MESSAGE_FILE, InputArgument::REQUIRED, 'File containing the commit message.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command\Hook;
|
||||
|
||||
use CaptainHook\App\Console\Command\Hook;
|
||||
use CaptainHook\App\Hooks;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
/**
|
||||
* Class PostCheckout
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 4.1.0
|
||||
*/
|
||||
class PostCheckout extends Hook
|
||||
{
|
||||
/**
|
||||
* Hook to execute.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $hookName = Hooks::POST_CHECKOUT;
|
||||
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->addArgument(Hooks::ARG_PREVIOUS_HEAD, InputArgument::OPTIONAL, 'Previous HEAD');
|
||||
$this->addArgument(Hooks::ARG_NEW_HEAD, InputArgument::OPTIONAL, 'New HEAD');
|
||||
$this->addArgument(Hooks::ARG_MODE, InputArgument::OPTIONAL, 'Checkout mode 1 branch 0 file');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command\Hook;
|
||||
|
||||
use CaptainHook\App\Console\Command\Hook;
|
||||
use CaptainHook\App\Hooks;
|
||||
|
||||
/**
|
||||
* Class PostCommit
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class PostCommit extends Hook
|
||||
{
|
||||
/**
|
||||
* Hook to execute.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $hookName = Hooks::POST_COMMIT;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command\Hook;
|
||||
|
||||
use CaptainHook\App\Console\Command\Hook;
|
||||
use CaptainHook\App\Hooks;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
/**
|
||||
* Class PostMerge
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 4.0.1
|
||||
*/
|
||||
class PostMerge extends Hook
|
||||
{
|
||||
/**
|
||||
* Hook to execute.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $hookName = Hooks::POST_MERGE;
|
||||
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->addArgument(Hooks::ARG_SQUASH, InputArgument::OPTIONAL, 'Merge was done with a squash merge.');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command\Hook;
|
||||
|
||||
use CaptainHook\App\Console\Command\Hook;
|
||||
use CaptainHook\App\Hooks;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
/**
|
||||
* Class PostRewrite
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.4.0
|
||||
*/
|
||||
class PostRewrite extends Hook
|
||||
{
|
||||
/**
|
||||
* Hook to execute.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $hookName = Hooks::POST_REWRITE;
|
||||
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->addArgument(Hooks::ARG_GIT_COMMAND, InputArgument::OPTIONAL, 'Executed command');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command\Hook;
|
||||
|
||||
use CaptainHook\App\Console\Command\Hook;
|
||||
use CaptainHook\App\Hooks;
|
||||
|
||||
/**
|
||||
* Class PreCommit
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class PreCommit extends Hook
|
||||
{
|
||||
/**
|
||||
* Hook to execute.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $hookName = Hooks::PRE_COMMIT;
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command\Hook;
|
||||
|
||||
use CaptainHook\App\Console\Command\Hook;
|
||||
use CaptainHook\App\Hooks;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
/**
|
||||
* Class PrePush
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class PrePush extends Hook
|
||||
{
|
||||
/**
|
||||
* Hook to execute.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $hookName = Hooks::PRE_PUSH;
|
||||
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->addArgument(Hooks::ARG_TARGET, InputArgument::OPTIONAL, 'Target repository name');
|
||||
$this->addArgument(Hooks::ARG_URL, InputArgument::OPTIONAL, 'Target repository url');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command\Hook;
|
||||
|
||||
use CaptainHook\App\Console\Command\Hook;
|
||||
use CaptainHook\App\Hooks;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
|
||||
/**
|
||||
* Class PrepareCommitMessage
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 3.1.0
|
||||
*/
|
||||
class PrepareCommitMsg extends Hook
|
||||
{
|
||||
/**
|
||||
* Hook to execute
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $hookName = Hooks::PREPARE_COMMIT_MSG;
|
||||
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->addArgument(Hooks::ARG_MESSAGE_FILE, InputArgument::REQUIRED, 'File containing the commit log message');
|
||||
$this->addArgument(Hooks::ARG_MODE, InputArgument::OPTIONAL, 'Current commit mode');
|
||||
$this->addArgument(Hooks::ARG_HASH, InputArgument::OPTIONAL, 'Given commit hash');
|
||||
}
|
||||
}
|
||||
108
lib/captainhook/captainhook/src/Console/Command/Info.php
Normal file
108
lib/captainhook/captainhook/src/Console/Command/Info.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command;
|
||||
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use CaptainHook\App\Runner\Config\Reader;
|
||||
use Exception;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Command to display configuration information
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.24.0
|
||||
*/
|
||||
class Info extends RepositoryAware
|
||||
{
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('config:info')
|
||||
->setAliases(['info'])
|
||||
->setDescription('Displays information about the configuration')
|
||||
->setHelp('Displays information about the configuration')
|
||||
->addArgument('hook', InputArgument::OPTIONAL, 'Hook you want to investigate')
|
||||
->addOption(
|
||||
'list-actions',
|
||||
'a',
|
||||
InputOption::VALUE_NONE,
|
||||
'List all actions'
|
||||
)
|
||||
->addOption(
|
||||
'list-conditions',
|
||||
'p',
|
||||
InputOption::VALUE_NONE,
|
||||
'List all conditions'
|
||||
)
|
||||
->addOption(
|
||||
'list-options',
|
||||
'o',
|
||||
InputOption::VALUE_NONE,
|
||||
'List all options'
|
||||
)
|
||||
->addOption(
|
||||
'list-config',
|
||||
's',
|
||||
InputOption::VALUE_NONE,
|
||||
'List all config settings'
|
||||
)
|
||||
->addOption(
|
||||
'extensive',
|
||||
'e',
|
||||
InputOption::VALUE_NONE,
|
||||
'Show more detailed information'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
* @throws \CaptainHook\App\Exception\InvalidHookName
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
try {
|
||||
$io = $this->getIO($input, $output);
|
||||
$config = $this->createConfig($input, true, ['git-directory']);
|
||||
$repo = $this->createRepository(dirname($config->getGitDirectory()));
|
||||
|
||||
$this->determineVerbosity($output, $config);
|
||||
|
||||
$editor = new Reader($io, $config, $repo);
|
||||
$editor->setHook(IOUtil::argToString($input->getArgument('hook')))
|
||||
->display(Reader::OPT_ACTIONS, $input->getOption('list-actions'))
|
||||
->display(Reader::OPT_CONDITIONS, $input->getOption('list-conditions'))
|
||||
->display(Reader::OPT_OPTIONS, $input->getOption('list-options'))
|
||||
->extensive($input->getOption('extensive'))
|
||||
->run();
|
||||
|
||||
return 0;
|
||||
} catch (Exception $e) {
|
||||
return $this->crash($output, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
156
lib/captainhook/captainhook/src/Console/Command/Install.php
Normal file
156
lib/captainhook/captainhook/src/Console/Command/Install.php
Normal file
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use CaptainHook\App\Hook\Template;
|
||||
use CaptainHook\App\Runner\Installer;
|
||||
use Exception;
|
||||
use RuntimeException;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class Install
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class Install extends RepositoryAware
|
||||
{
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('install')
|
||||
->setDescription('Install hooks to your .git/hooks directory')
|
||||
->setHelp('Install git hooks to your .git/hooks directory')
|
||||
->addArgument(
|
||||
'hook',
|
||||
InputArgument::OPTIONAL,
|
||||
'Limit the hooks you want to install. ' .
|
||||
'You can specify multiple hooks with comma as delimiter. ' .
|
||||
'By default all hooks get installed'
|
||||
)
|
||||
->addOption(
|
||||
'only-enabled',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Limit the hooks you want to install to those enabled in your conf. ' .
|
||||
'By default all hooks get installed'
|
||||
)
|
||||
->addOption(
|
||||
'force',
|
||||
'f',
|
||||
InputOption::VALUE_NONE,
|
||||
'Force install without confirmation'
|
||||
)
|
||||
->addOption(
|
||||
'skip-existing',
|
||||
's',
|
||||
InputOption::VALUE_NONE,
|
||||
'Do not overwrite existing hooks'
|
||||
)
|
||||
->addOption(
|
||||
'move-existing-to',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Move existing hooks to given directory'
|
||||
)
|
||||
->addOption(
|
||||
'bootstrap',
|
||||
'b',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Path to composers vendor/autoload.php'
|
||||
)
|
||||
->addOption(
|
||||
'run-mode',
|
||||
'm',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Git hook run mode [php|shell|docker]'
|
||||
)
|
||||
->addOption(
|
||||
'run-exec',
|
||||
'e',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'The Docker command to start your container e.g. \'docker exec CONTAINER\''
|
||||
)
|
||||
->addOption(
|
||||
'run-path',
|
||||
'p',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'The path to the CaptainHook executable \'/usr/bin/captainhook\''
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
try {
|
||||
$args = ['git-directory', 'run-mode', 'run-exec', 'run-path', 'bootstrap'];
|
||||
$io = $this->getIO($input, $output);
|
||||
$config = $this->createConfig($input, true, $args);
|
||||
$repo = $this->createRepository(dirname($config->getGitDirectory()));
|
||||
$template = $this->createTemplate($config, $repo);
|
||||
|
||||
$this->determineVerbosity($output, $config);
|
||||
|
||||
$installer = new Installer($io, $config, $repo, $template);
|
||||
$installer->setHook(IOUtil::argToString($input->getArgument('hook')))
|
||||
->setForce(IOUtil::argToBool($input->getOption('force')))
|
||||
->setSkipExisting(IOUtil::argToBool($input->getOption('skip-existing')))
|
||||
->setMoveExistingTo(IOUtil::argToString($input->getOption('move-existing-to')))
|
||||
->setOnlyEnabled(IOUtil::argToBool($input->getOption('only-enabled')))
|
||||
->run();
|
||||
|
||||
return 0;
|
||||
} catch (Exception $e) {
|
||||
return $this->crash($output, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the template to generate the hook source code
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @param \SebastianFeldmann\Git\Repository $repo
|
||||
* @return \CaptainHook\App\Hook\Template
|
||||
*/
|
||||
private function createTemplate(Config $config, Repository $repo): Template
|
||||
{
|
||||
if (
|
||||
$config->getRunConfig()->getMode() === Template::DOCKER
|
||||
&& empty($config->getRunConfig()->getDockerCommand())
|
||||
) {
|
||||
throw new RuntimeException('Run "exec" option missing for run-mode docker.');
|
||||
}
|
||||
|
||||
return Template\Builder::build($config, $repo, $this->resolver);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command;
|
||||
|
||||
use CaptainHook\App\Console\Runtime\Resolver;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* Trait RepositoryAware
|
||||
*
|
||||
* Trait for all commands that needs to be aware of the git repository.
|
||||
*
|
||||
* @package CaptainHook\App\Console\Command
|
||||
*/
|
||||
class RepositoryAware extends ConfigAware
|
||||
{
|
||||
/**
|
||||
* Configure method to set up the git-directory command option
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
|
||||
$this->addOption(
|
||||
'git-directory',
|
||||
'g',
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Path to your .git directory'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new git repository representation
|
||||
*
|
||||
* @param string $path
|
||||
* @return \SebastianFeldmann\Git\Repository
|
||||
*/
|
||||
protected function createRepository(string $path): Repository
|
||||
{
|
||||
return Repository::createVerified($path);
|
||||
}
|
||||
}
|
||||
100
lib/captainhook/captainhook/src/Console/Command/Uninstall.php
Normal file
100
lib/captainhook/captainhook/src/Console/Command/Uninstall.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Command;
|
||||
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use CaptainHook\App\Runner\Uninstaller;
|
||||
use Exception;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Class Uninstall
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.17.0
|
||||
*/
|
||||
class Uninstall extends RepositoryAware
|
||||
{
|
||||
/**
|
||||
* Configure the command
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function configure(): void
|
||||
{
|
||||
parent::configure();
|
||||
$this->setName('uninstall')
|
||||
->setDescription('Remove all git hooks from your .git/hooks directory')
|
||||
->setHelp('Remove all git hooks from your .git/hooks directory')
|
||||
->addArgument(
|
||||
'hook',
|
||||
InputArgument::OPTIONAL,
|
||||
'Remove only this one hook. By default all hooks get uninstalled'
|
||||
)
|
||||
->addOption(
|
||||
'force',
|
||||
'f',
|
||||
InputOption::VALUE_NONE,
|
||||
'Force install without confirmation'
|
||||
)
|
||||
->addOption(
|
||||
'only-disabled',
|
||||
null,
|
||||
InputOption::VALUE_NONE,
|
||||
'Limit the hooks you want to remove to those that are not enabled in your conf. ' .
|
||||
'By default all hooks get uninstalled'
|
||||
)
|
||||
->addOption(
|
||||
'move-existing-to',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Move existing hooks to this directory'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the command
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = $this->getIO($input, $output);
|
||||
|
||||
try {
|
||||
$config = $this->createConfig($input, true, ['git-directory']);
|
||||
$repo = $this->createRepository(dirname($config->getGitDirectory()));
|
||||
|
||||
$this->determineVerbosity($output, $config);
|
||||
|
||||
$uninstaller = new Uninstaller($io, $config, $repo);
|
||||
$uninstaller->setHook(IOUtil::argToString($input->getArgument('hook')))
|
||||
->setForce(IOUtil::argToBool($input->getOption('force')))
|
||||
->setOnlyDisabled(IOUtil::argToBool($input->getOption('only-disabled')))
|
||||
->setMoveExistingTo(IOUtil::argToString($input->getOption('move-existing-to')))
|
||||
->run();
|
||||
|
||||
return 0;
|
||||
} catch (Exception $e) {
|
||||
$this->crash($output, $e);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
139
lib/captainhook/captainhook/src/Console/IO.php
Normal file
139
lib/captainhook/captainhook/src/Console/IO.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console;
|
||||
|
||||
/**
|
||||
* Interface IO
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Nils Adermann <naderman@naderman.de>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
interface IO
|
||||
{
|
||||
public const QUIET = 1;
|
||||
public const NORMAL = 2;
|
||||
public const VERBOSE = 4;
|
||||
public const VERY_VERBOSE = 8;
|
||||
public const DEBUG = 16;
|
||||
|
||||
/**
|
||||
* Return the original cli arguments
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function getArguments(): array;
|
||||
|
||||
/**
|
||||
* Return the original cli argument or a given default
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public function getArgument(string $name, string $default = ''): string;
|
||||
|
||||
/**
|
||||
* Returns the piped in standard input
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getStandardInput(): array;
|
||||
|
||||
/**
|
||||
* Is this input interactive?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isInteractive();
|
||||
|
||||
/**
|
||||
* Is this output verbose?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isVerbose();
|
||||
|
||||
/**
|
||||
* Is the output very verbose?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isVeryVerbose();
|
||||
|
||||
/**
|
||||
* Is the output in debug verbosity?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isDebug();
|
||||
|
||||
/**
|
||||
* Writes a message to the output
|
||||
*
|
||||
* @param string|array<string> $messages The message as an array of lines or a single string
|
||||
* @param bool $newline Whether to add a newline or not
|
||||
* @param int $verbosity Verbosity level from the VERBOSITY_* constants
|
||||
* @return void
|
||||
*/
|
||||
public function write($messages, $newline = true, $verbosity = self::NORMAL);
|
||||
|
||||
/**
|
||||
* Writes a message to the error output
|
||||
*
|
||||
* @param string|array<string> $messages The message as an array of lines or a single string
|
||||
* @param bool $newline Whether to add a newline or not
|
||||
* @param int $verbosity Verbosity level from the VERBOSITY_* constants
|
||||
* @return void
|
||||
*/
|
||||
public function writeError($messages, $newline = true, $verbosity = self::NORMAL);
|
||||
|
||||
/**
|
||||
* Asks a question to the user
|
||||
*
|
||||
* @param string $question The question to ask
|
||||
* @param string $default The default answer if none is given by the user
|
||||
* @throws \RuntimeException If there is no data to read in the input stream
|
||||
* @return string The user answer
|
||||
*/
|
||||
public function ask($question, $default = null);
|
||||
|
||||
/**
|
||||
* Asks a confirmation to the user
|
||||
*
|
||||
* The question will be asked until the user answers by nothing, yes, or no.
|
||||
*
|
||||
* @param string $question The question to ask
|
||||
* @param bool $default The default answer if the user enters nothing
|
||||
* @return bool true if the user has confirmed, false otherwise
|
||||
*/
|
||||
public function askConfirmation($question, $default = true);
|
||||
|
||||
/**
|
||||
* Asks for a value and validates the response
|
||||
*
|
||||
* The validator receives the data to validate. It must return the
|
||||
* validated data when the data is valid and throw an exception
|
||||
* otherwise.
|
||||
*
|
||||
* @param string $question The question to ask
|
||||
* @param callable $validator A PHP callback
|
||||
* @param int $attempts Max number of times to ask before giving up (default of null means infinite)
|
||||
* @param mixed $default The default answer if none is given by the user
|
||||
* @throws \Exception When any of the validators return an error
|
||||
* @return mixed
|
||||
*/
|
||||
public function askAndValidate($question, $validator, $attempts = null, $default = null);
|
||||
}
|
||||
57
lib/captainhook/captainhook/src/Console/IO/Base.php
Normal file
57
lib/captainhook/captainhook/src/Console/IO/Base.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\IO;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
|
||||
/**
|
||||
* Class Base
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
abstract class Base implements IO
|
||||
{
|
||||
/**
|
||||
* Return the original cli arguments
|
||||
*
|
||||
* @return array<mixed>
|
||||
*/
|
||||
public function getArguments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original cli argument or a given default
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public function getArgument(string $name, string $default = ''): string
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the piped in standard input
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getStandardInput(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
151
lib/captainhook/captainhook/src/Console/IO/CollectorIO.php
Normal file
151
lib/captainhook/captainhook/src/Console/IO/CollectorIO.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\IO;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use SebastianFeldmann\Cli\Reader\StandardInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
/**
|
||||
* Class CollectorIO
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.19.0
|
||||
*/
|
||||
class CollectorIO implements IO
|
||||
{
|
||||
/**
|
||||
* @var \CaptainHook\App\Console\IO
|
||||
*/
|
||||
private IO $io;
|
||||
|
||||
/**
|
||||
* @var array<\CaptainHook\App\Console\IO\Message>
|
||||
*/
|
||||
private array $messages = [];
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(IO $io)
|
||||
{
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param string $messages
|
||||
* @param bool $newline
|
||||
* @param int $verbosity
|
||||
* @return void
|
||||
*/
|
||||
public function write($messages, $newline = true, $verbosity = IO::NORMAL)
|
||||
{
|
||||
$this->messages[] = new Message($messages, $newline, $verbosity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $messages
|
||||
* @param bool $newline
|
||||
* @param int $verbosity
|
||||
* @return void
|
||||
*/
|
||||
public function writeError($messages, $newline = true, $verbosity = IO::NORMAL)
|
||||
{
|
||||
$this->messages[] = new Message($messages, $newline, $verbosity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<\CaptainHook\App\Console\IO\Message>
|
||||
*/
|
||||
public function getMessages(): array
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original cli arguments
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getArguments(): array
|
||||
{
|
||||
return $this->io->getArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original cli argument or a given default
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public function getArgument(string $name, string $default = ''): string
|
||||
{
|
||||
return $this->io->getArgument($name, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the piped in standard input
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getStandardInput(): array
|
||||
{
|
||||
return $this->io->getStandardInput();
|
||||
}
|
||||
|
||||
public function isInteractive()
|
||||
{
|
||||
return $this->io->isInteractive();
|
||||
}
|
||||
|
||||
public function isVerbose()
|
||||
{
|
||||
return $this->io->isVerbose();
|
||||
}
|
||||
|
||||
public function isVeryVerbose()
|
||||
{
|
||||
return $this->io->isVeryVerbose();
|
||||
}
|
||||
|
||||
public function isDebug()
|
||||
{
|
||||
return $this->io->isDebug();
|
||||
}
|
||||
|
||||
public function ask($question, $default = null)
|
||||
{
|
||||
return $this->io->ask($question, $default);
|
||||
}
|
||||
|
||||
public function askConfirmation($question, $default = true)
|
||||
{
|
||||
return $this->io->askConfirmation($question, $default);
|
||||
}
|
||||
|
||||
public function askAndValidate($question, $validator, $attempts = null, $default = null)
|
||||
{
|
||||
return $this->io->askAndValidate($question, $validator, $attempts, $default);
|
||||
}
|
||||
}
|
||||
112
lib/captainhook/captainhook/src/Console/IO/ComposerIO.php
Normal file
112
lib/captainhook/captainhook/src/Console/IO/ComposerIO.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\IO;
|
||||
|
||||
use Composer\IO\IOInterface;
|
||||
|
||||
/**
|
||||
* Class ComposerIO
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class ComposerIO extends Base
|
||||
{
|
||||
/**
|
||||
* @var \Composer\IO\IOInterface
|
||||
*/
|
||||
private $io;
|
||||
|
||||
/**
|
||||
* ComposerIO constructor
|
||||
*
|
||||
* @param \Composer\IO\IOInterface $io
|
||||
*/
|
||||
public function __construct(IOInterface $io)
|
||||
{
|
||||
$this->io = $io;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isInteractive()
|
||||
{
|
||||
return $this->io->isInteractive();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isVerbose()
|
||||
{
|
||||
return $this->io->isVerbose();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isVeryVerbose()
|
||||
{
|
||||
return $this->io->isVeryVerbose();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isDebug()
|
||||
{
|
||||
return $this->io->isDebug();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function write($messages, $newline = true, $verbosity = self::NORMAL)
|
||||
{
|
||||
$this->io->write($messages, $newline, $verbosity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function writeError($messages, $newline = true, $verbosity = self::NORMAL)
|
||||
{
|
||||
$this->io->writeError($messages, $newline, $verbosity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function ask($question, $default = null)
|
||||
{
|
||||
return $this->io->ask($question, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function askConfirmation($question, $default = true)
|
||||
{
|
||||
return $this->io->askConfirmation($question, $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function askAndValidate($question, $validator, $attempts = null, $default = null)
|
||||
{
|
||||
return $this->io->askAndValidate($question, $validator, $attempts, $default);
|
||||
}
|
||||
}
|
||||
246
lib/captainhook/captainhook/src/Console/IO/DefaultIO.php
Normal file
246
lib/captainhook/captainhook/src/Console/IO/DefaultIO.php
Normal file
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\IO;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use RuntimeException;
|
||||
use SebastianFeldmann\Cli\Reader\StandardInput;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Helper\HelperSet;
|
||||
use Symfony\Component\Console\Output\ConsoleOutputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Question\ConfirmationQuestion;
|
||||
use Symfony\Component\Console\Question\Question;
|
||||
|
||||
/**
|
||||
* Class DefaultIO
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class DefaultIO extends Base
|
||||
{
|
||||
/**
|
||||
* Contents of the STDIN
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
private array $stdIn = [];
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Console\Input\InputInterface
|
||||
*/
|
||||
protected InputInterface $input;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Console\Output\OutputInterface
|
||||
*/
|
||||
protected OutputInterface $output;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Console\Helper\HelperSet|null
|
||||
*/
|
||||
protected ?HelperSet $helperSet;
|
||||
|
||||
/**
|
||||
* @var array<int, int>
|
||||
*/
|
||||
private array $verbosityMap;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \Symfony\Component\Console\Input\InputInterface $input
|
||||
* @param \Symfony\Component\Console\Output\OutputInterface $output
|
||||
* @param \Symfony\Component\Console\Helper\HelperSet|null $helperSet
|
||||
*/
|
||||
public function __construct(InputInterface $input, OutputInterface $output, ?HelperSet $helperSet = null)
|
||||
{
|
||||
$this->input = $input;
|
||||
$this->output = $output;
|
||||
$this->helperSet = $helperSet;
|
||||
$this->verbosityMap = [
|
||||
IO::QUIET => OutputInterface::VERBOSITY_QUIET,
|
||||
IO::NORMAL => OutputInterface::VERBOSITY_NORMAL,
|
||||
IO::VERBOSE => OutputInterface::VERBOSITY_VERBOSE,
|
||||
IO::VERY_VERBOSE => OutputInterface::VERBOSITY_VERY_VERBOSE,
|
||||
IO::DEBUG => OutputInterface::VERBOSITY_DEBUG
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original cli arguments
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getArguments(): array
|
||||
{
|
||||
return $this->input->getArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original cli argument or a given default
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public function getArgument(string $name, string $default = ''): string
|
||||
{
|
||||
return (string)($this->getArguments()[$name] ?? $default);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the piped in standard input
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getStandardInput(): array
|
||||
{
|
||||
if (empty($this->stdIn)) {
|
||||
$this->stdIn = explode(PHP_EOL, trim($this->input->getOption('input'), '"'));
|
||||
}
|
||||
return $this->stdIn;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isInteractive()
|
||||
{
|
||||
return $this->input->isInteractive();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isVerbose()
|
||||
{
|
||||
return $this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isVeryVerbose()
|
||||
{
|
||||
return $this->output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isDebug()
|
||||
{
|
||||
return $this->output->getVerbosity() >= OutputInterface::VERBOSITY_DEBUG;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function write($messages, $newline = true, $verbosity = self::NORMAL)
|
||||
{
|
||||
$this->doWrite($messages, $newline, false, $verbosity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function writeError($messages, $newline = true, $verbosity = self::NORMAL)
|
||||
{
|
||||
$this->doWrite($messages, $newline, true, $verbosity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the appropriate user output
|
||||
*
|
||||
* @param array<string>|string $messages
|
||||
* @param bool $newline
|
||||
* @param bool $stderr
|
||||
* @param int $verbosity
|
||||
* @return void
|
||||
*/
|
||||
private function doWrite($messages, $newline, $stderr, $verbosity)
|
||||
{
|
||||
$sfVerbosity = $this->verbosityMap[$verbosity];
|
||||
if ($sfVerbosity > $this->output->getVerbosity()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->getOutputToWriteTo($stderr)->write($messages, $newline, $sfVerbosity);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function ask($question, $default = null)
|
||||
{
|
||||
if ($this->helperSet === null) {
|
||||
throw new RuntimeException('You must set the helperSet before asking');
|
||||
}
|
||||
/** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */
|
||||
$helper = $this->helperSet->get('question');
|
||||
$question = new Question($question, $default);
|
||||
|
||||
return $helper->ask($this->input, $this->getOutputToWriteTo(), $question);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function askConfirmation($question, $default = true)
|
||||
{
|
||||
if ($this->helperSet === null) {
|
||||
throw new RuntimeException('You must set the helperSet before asking');
|
||||
}
|
||||
/** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */
|
||||
$helper = $this->helperSet->get('question');
|
||||
$question = new ConfirmationQuestion($question, $default);
|
||||
|
||||
return IOUtil::answerToBool($helper->ask($this->input, $this->getOutputToWriteTo(), $question));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function askAndValidate($question, $validator, $attempts = null, $default = null)
|
||||
{
|
||||
if ($this->helperSet === null) {
|
||||
throw new RuntimeException('You must set the helperSet before asking');
|
||||
}
|
||||
/** @var \Symfony\Component\Console\Helper\QuestionHelper $helper */
|
||||
$helper = $this->helperSet->get('question');
|
||||
$question = new Question($question, $default);
|
||||
$question->setValidator($validator);
|
||||
$question->setMaxAttempts($attempts);
|
||||
|
||||
return $helper->ask($this->input, $this->getOutputToWriteTo(), $question);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the output to write to
|
||||
*
|
||||
* @param bool $stdErr
|
||||
* @return \Symfony\Component\Console\Output\OutputInterface
|
||||
*/
|
||||
private function getOutputToWriteTo($stdErr = false)
|
||||
{
|
||||
if ($stdErr && $this->output instanceof ConsoleOutputInterface) {
|
||||
return $this->output->getErrorOutput();
|
||||
}
|
||||
|
||||
return $this->output;
|
||||
}
|
||||
}
|
||||
88
lib/captainhook/captainhook/src/Console/IO/Message.php
Normal file
88
lib/captainhook/captainhook/src/Console/IO/Message.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\IO;
|
||||
|
||||
/**
|
||||
* Class Message
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.19.0
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
/**
|
||||
* Message, either a string or list of string for multiple lines
|
||||
*
|
||||
* @var string|array<string>
|
||||
*/
|
||||
private string|array $message;
|
||||
|
||||
/**
|
||||
* Should message be ended with a new line character
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $newLine;
|
||||
|
||||
/**
|
||||
* Current application verbosity
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
private int $verbosity;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string|array<string> $message
|
||||
* @param bool $newLine
|
||||
* @param int $verbosity
|
||||
*/
|
||||
public function __construct(string|array $message, bool $newLine, int $verbosity)
|
||||
{
|
||||
$this->message = $message;
|
||||
$this->newLine = $newLine;
|
||||
$this->verbosity = $verbosity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message to print
|
||||
*
|
||||
* @return string|array<string>
|
||||
*/
|
||||
public function message(): string|array
|
||||
{
|
||||
return $this->message;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true message should end with a new line
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function newLine(): bool
|
||||
{
|
||||
return $this->newLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimum verbosity this message should be displayed at
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function verbosity(): int
|
||||
{
|
||||
return $this->verbosity;
|
||||
}
|
||||
}
|
||||
93
lib/captainhook/captainhook/src/Console/IO/NullIO.php
Normal file
93
lib/captainhook/captainhook/src/Console/IO/NullIO.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\IO;
|
||||
|
||||
/**
|
||||
* Class NullIO
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class NullIO extends Base
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isInteractive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isVerbose()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isVeryVerbose()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function isDebug()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function write($messages, $newline = true, $verbosity = self::NORMAL)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function writeError($messages, $newline = true, $verbosity = self::NORMAL)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function ask($question, $default = null)
|
||||
{
|
||||
return (string) $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function askConfirmation($question, $default = true)
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function askAndValidate($question, $validator, $attempts = null, $default = null)
|
||||
{
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
112
lib/captainhook/captainhook/src/Console/IOUtil.php
Normal file
112
lib/captainhook/captainhook/src/Console/IOUtil.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console;
|
||||
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* IOUtil class
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
abstract class IOUtil
|
||||
{
|
||||
public const PREFIX_OK = '<info>✔</info>';
|
||||
public const PREFIX_FAIL = '<fg=red>✘</>';
|
||||
|
||||
/**
|
||||
* Maps config values to Symfony verbosity values
|
||||
*
|
||||
* @var array<string, 16|32|64|128|256>
|
||||
*/
|
||||
private static array $verbosityMap = [
|
||||
'quiet' => OutputInterface::VERBOSITY_QUIET,
|
||||
'normal' => OutputInterface::VERBOSITY_NORMAL,
|
||||
'verbose' => OutputInterface::VERBOSITY_VERBOSE,
|
||||
'very-verbose' => OutputInterface::VERBOSITY_VERY_VERBOSE,
|
||||
'debug' => OutputInterface::VERBOSITY_DEBUG
|
||||
];
|
||||
|
||||
/**
|
||||
* Return the Symfony verbosity for a given config value
|
||||
*
|
||||
* @param string $verbosity
|
||||
* @return OutputInterface::VERBOSITY_*
|
||||
*/
|
||||
public static function mapConfigVerbosity(string $verbosity): int
|
||||
{
|
||||
return self::$verbosityMap[strtolower($verbosity)] ?? OutputInterface::VERBOSITY_NORMAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a user answer to boolean
|
||||
*
|
||||
* @param string $answer
|
||||
* @return bool
|
||||
*/
|
||||
public static function answerToBool(string $answer): bool
|
||||
{
|
||||
return in_array(strtolower($answer), ['y', 'yes', 'ok']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create formatted cli headline
|
||||
*
|
||||
* ">>>> HEADLINE <<<<"
|
||||
* "==== HEADLINE ===="
|
||||
*
|
||||
* @param string $headline
|
||||
* @param int $length
|
||||
* @param string $pre
|
||||
* @param string $post
|
||||
* @return string
|
||||
*/
|
||||
public static function formatHeadline(string $headline, int $length, string $pre = '=', string $post = '='): string
|
||||
{
|
||||
$headlineLength = mb_strlen($headline);
|
||||
if ($headlineLength > ($length - 3)) {
|
||||
return $headline;
|
||||
}
|
||||
|
||||
$prefix = (int) floor(($length - $headlineLength - 2) / 2);
|
||||
$suffix = (int) ceil(($length - $headlineLength - 2) / 2);
|
||||
|
||||
return str_repeat($pre, $prefix) . ' ' . $headline . ' ' . str_repeat($post, $suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert everything to a string
|
||||
*
|
||||
* @param array<string>|bool|string|null $arg
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public static function argToString(mixed $arg, string $default = ''): string
|
||||
{
|
||||
return is_string($arg) ? $arg : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert everything to a boolean
|
||||
*
|
||||
* @param array<string>|bool|string|null $arg
|
||||
* @param bool $default
|
||||
* @return bool
|
||||
*/
|
||||
public static function argToBool(mixed $arg, bool $default = false): bool
|
||||
{
|
||||
return is_bool($arg) ? $arg : $default;
|
||||
}
|
||||
}
|
||||
64
lib/captainhook/captainhook/src/Console/Runtime/Resolver.php
Normal file
64
lib/captainhook/captainhook/src/Console/Runtime/Resolver.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of captainhook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Console\Runtime;
|
||||
|
||||
/**
|
||||
* Class Resolver
|
||||
*
|
||||
* @package CaptainHook\App
|
||||
*/
|
||||
class Resolver
|
||||
{
|
||||
/**
|
||||
* PHAR flag, replaced by box during PHAR building
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $runtime = '@runtime@';
|
||||
|
||||
/**
|
||||
* Path to the currently executed 'binary'
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $executable;
|
||||
|
||||
/**
|
||||
* Resolver constructor.
|
||||
*
|
||||
* @param string $executable
|
||||
*/
|
||||
public function __construct(string $executable = 'bin/vendor/captainhook')
|
||||
{
|
||||
$this->executable = $executable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return current executed 'binary'
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExecutable(): string
|
||||
{
|
||||
return $this->executable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current runtime is executed via PHAR
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isPharRelease(): bool
|
||||
{
|
||||
return $this->runtime === 'PHAR';
|
||||
}
|
||||
}
|
||||
56
lib/captainhook/captainhook/src/Event.php
Normal file
56
lib/captainhook/captainhook/src/Event.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Event interface
|
||||
*
|
||||
* Allows event handlers to do output access the app setup or the repository.
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.11.0
|
||||
*/
|
||||
interface Event
|
||||
{
|
||||
/**
|
||||
* Returns the event trigger name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string;
|
||||
|
||||
/**
|
||||
* Returns the captainhook config, most likely needed to access any original command line arguments
|
||||
*
|
||||
* @return \CaptainHook\App\Config
|
||||
*/
|
||||
public function config(): Config;
|
||||
|
||||
/**
|
||||
* Returns IO to do some output
|
||||
*
|
||||
* @return \CaptainHook\App\Console\IO
|
||||
*/
|
||||
public function io(): IO;
|
||||
|
||||
/**
|
||||
* Returns the git repository
|
||||
*
|
||||
* @return \SebastianFeldmann\Git\Repository
|
||||
*/
|
||||
public function repository(): Repository;
|
||||
}
|
||||
109
lib/captainhook/captainhook/src/Event/Dispatcher.php
Normal file
109
lib/captainhook/captainhook/src/Event/Dispatcher.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Event;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Console\IO;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Event Dispatcher
|
||||
*
|
||||
* This allows the user to hook into the Cap'n on a deeper level. For example execute code if the hook execution fails.
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.11.0
|
||||
*/
|
||||
class Dispatcher
|
||||
{
|
||||
/**
|
||||
* List of all registered handlers
|
||||
*
|
||||
* @var array<string, array<int, \CaptainHook\App\Event\Handler>>
|
||||
*/
|
||||
private $config = [];
|
||||
|
||||
/**
|
||||
* Event factory to create all necessary events
|
||||
*
|
||||
* @var \CaptainHook\App\Event\Factory
|
||||
*/
|
||||
private $factory;
|
||||
|
||||
/**
|
||||
* Event Dispatcher
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
*/
|
||||
public function __construct(IO $io, Config $config, Repository $repository)
|
||||
{
|
||||
$this->factory = new Factory($io, $config, $repository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register handlers received from a Listener to the dispatcher
|
||||
*
|
||||
* @param array<string, array<int, \CaptainHook\App\Event\Handler>> $eventConfig
|
||||
* @return void
|
||||
*/
|
||||
public function subscribeHandlers(array $eventConfig): void
|
||||
{
|
||||
foreach ($eventConfig as $event => $handlers) {
|
||||
foreach ($handlers as $handler) {
|
||||
$this->subscribeHandlerToEvent($handler, $event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a single event handler to an event
|
||||
*
|
||||
* @param \CaptainHook\App\Event\Handler $handler
|
||||
* @param string $event
|
||||
* @return void
|
||||
*/
|
||||
public function subscribeHandlerToEvent(Handler $handler, string $event): void
|
||||
{
|
||||
$this->config[$event][] = $handler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger all event handlers registered for a given event
|
||||
*
|
||||
* @param string $eventName
|
||||
* @throws \Exception
|
||||
* @return void
|
||||
*/
|
||||
public function dispatch(string $eventName): void
|
||||
{
|
||||
$event = $this->factory->createEvent($eventName);
|
||||
|
||||
foreach ($this->handlersFor($event->name()) as $handler) {
|
||||
$handler->handle($event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of handlers for a given event
|
||||
*
|
||||
* @param string $event
|
||||
* @return \CaptainHook\App\Event\Handler[];
|
||||
*/
|
||||
private function handlersFor(string $event): array
|
||||
{
|
||||
return $this->config[$event] ?? [];
|
||||
}
|
||||
}
|
||||
97
lib/captainhook/captainhook/src/Event/Factory.php
Normal file
97
lib/captainhook/captainhook/src/Event/Factory.php
Normal file
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Event;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Event;
|
||||
use RuntimeException;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Event Factory
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.11.0
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
/**
|
||||
* @var \CaptainHook\App\Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \CaptainHook\App\Console\IO
|
||||
*/
|
||||
private $io;
|
||||
|
||||
/**
|
||||
* @var \SebastianFeldmann\Git\Repository
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/**
|
||||
* List of available events
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
private $validEventIDs = [
|
||||
'onHookFailure' => HookFailed::class,
|
||||
'onHookSuccess' => HookSucceeded::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Event Factory
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
*/
|
||||
public function __construct(IO $io, Config $config, Repository $repository)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->io = $io;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a CaptainHook event
|
||||
*
|
||||
* @param string $name
|
||||
* @return \CaptainHook\App\Event
|
||||
*/
|
||||
public function createEvent(string $name): Event
|
||||
{
|
||||
if (!$this->isEventIDValid($name)) {
|
||||
throw new RuntimeException('invalid event name: ' . $name);
|
||||
}
|
||||
|
||||
$class = $this->validEventIDs[$name];
|
||||
/** @var \CaptainHook\App\Event $event */
|
||||
$event = new $class($this->io, $this->config, $this->repository);
|
||||
return $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an event name
|
||||
*
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public function isEventIDValid(string $name): bool
|
||||
{
|
||||
return array_key_exists($name, $this->validEventIDs);
|
||||
}
|
||||
}
|
||||
34
lib/captainhook/captainhook/src/Event/Handler.php
Normal file
34
lib/captainhook/captainhook/src/Event/Handler.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Event;
|
||||
|
||||
use CaptainHook\App\Event;
|
||||
|
||||
/**
|
||||
* Interface EventListener
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.11.0
|
||||
*/
|
||||
interface Handler
|
||||
{
|
||||
/**
|
||||
* Executes the handler to handle the given event
|
||||
*
|
||||
* @param \CaptainHook\App\Event $event
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle(Event $event);
|
||||
}
|
||||
96
lib/captainhook/captainhook/src/Event/Hook.php
Normal file
96
lib/captainhook/captainhook/src/Event/Hook.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Event;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Event;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Basic event class
|
||||
*
|
||||
* Makes sure the handler has access to the output the current app setup and the repository.
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.11.0
|
||||
*/
|
||||
abstract class Hook implements Event
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var \CaptainHook\App\Console\IO
|
||||
*/
|
||||
protected $io;
|
||||
|
||||
/**
|
||||
* @var \CaptainHook\App\Config
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* @var \SebastianFeldmann\Git\Repository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* Event
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
*/
|
||||
public function __construct(IO $io, Config $config, Repository $repository)
|
||||
{
|
||||
$this->io = $io;
|
||||
$this->config = $config;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function name(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \CaptainHook\App\Config
|
||||
*/
|
||||
public function config(): Config
|
||||
{
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \CaptainHook\App\Console\IO
|
||||
*/
|
||||
public function io(): IO
|
||||
{
|
||||
return $this->io;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \SebastianFeldmann\Git\Repository
|
||||
*/
|
||||
public function repository(): Repository
|
||||
{
|
||||
return $this->repository;
|
||||
}
|
||||
}
|
||||
25
lib/captainhook/captainhook/src/Event/HookFailed.php
Normal file
25
lib/captainhook/captainhook/src/Event/HookFailed.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Event;
|
||||
|
||||
/**
|
||||
* Hook failed event
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.11.0
|
||||
*/
|
||||
class HookFailed extends Hook
|
||||
{
|
||||
protected $name = 'onHookFailure';
|
||||
}
|
||||
25
lib/captainhook/captainhook/src/Event/HookSucceeded.php
Normal file
25
lib/captainhook/captainhook/src/Event/HookSucceeded.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Event;
|
||||
|
||||
/**
|
||||
* Hook succeeded event
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.11.0
|
||||
*/
|
||||
class HookSucceeded extends Hook
|
||||
{
|
||||
protected $name = 'onHookSuccess';
|
||||
}
|
||||
26
lib/captainhook/captainhook/src/Exception/ActionFailed.php
Normal file
26
lib/captainhook/captainhook/src/Exception/ActionFailed.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class ActionFailed
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class ActionFailed extends Exception implements CaptainHookException
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Exception;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* CaptainHookException interface
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.9.0.
|
||||
*/
|
||||
interface CaptainHookException extends Throwable
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Exception;
|
||||
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* Class InvalidHookName
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
class InvalidHookName extends Exception implements CaptainHookException
|
||||
{
|
||||
}
|
||||
26
lib/captainhook/captainhook/src/Exception/InvalidPlugin.php
Normal file
26
lib/captainhook/captainhook/src/Exception/InvalidPlugin.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Exception;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Class InvalidPlugin
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.9.0
|
||||
*/
|
||||
class InvalidPlugin extends RuntimeException implements CaptainHookException
|
||||
{
|
||||
}
|
||||
62
lib/captainhook/captainhook/src/Git/ChangedFiles.php
Normal file
62
lib/captainhook/captainhook/src/Git/ChangedFiles.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Git\ChangedFiles\Detector\Factory;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class ChangedFiles
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.2.0
|
||||
*/
|
||||
abstract class ChangedFiles
|
||||
{
|
||||
/**
|
||||
* Returns the list of changed files using the necessary Detector
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @param array<string> $filter
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function getChangedFiles(IO $io, Repository $repository, array $filter): array
|
||||
{
|
||||
$factory = new Factory();
|
||||
$detector = $factory->getDetector($io, $repository);
|
||||
|
||||
$files = $detector->getChangedFiles($filter);
|
||||
self::displayFilesFound($io, $files);
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Output the changed files in verbose mode
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param array<string> $files
|
||||
* @return void
|
||||
*/
|
||||
private static function displayFilesFound(IO $io, array $files): void
|
||||
{
|
||||
if ($io->isVerbose()) {
|
||||
$io->write(' <comment>Changed files</comment>', true, IO::VERBOSE);
|
||||
foreach ($files as $file) {
|
||||
$io->write(' - ' . $file, true, IO::VERBOSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\ChangedFiles;
|
||||
|
||||
/**
|
||||
* Detector interface
|
||||
*
|
||||
* Interface to detect changed files for the different hooks.
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.20.0
|
||||
*/
|
||||
interface Detecting
|
||||
{
|
||||
/**
|
||||
* Returns a list of changed files
|
||||
*
|
||||
* @param array<string> $filter
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getChangedFiles(array $filter = []): array;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\ChangedFiles;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class Detector
|
||||
*
|
||||
* Base class for all ChangedFiles Detecting implementations.
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.20.0
|
||||
*/
|
||||
abstract class Detector implements Detecting
|
||||
{
|
||||
/**
|
||||
* Input output handling
|
||||
*
|
||||
* @var \CaptainHook\App\Console\IO
|
||||
*/
|
||||
protected IO $io;
|
||||
|
||||
/**
|
||||
* Git repository
|
||||
*
|
||||
* @var \SebastianFeldmann\Git\Repository
|
||||
*/
|
||||
protected Repository $repository;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
*/
|
||||
public function __construct(IO $io, Repository $repository)
|
||||
{
|
||||
$this->io = $io;
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of changed files
|
||||
*
|
||||
* @param array<string> $filter
|
||||
* @return array<string>
|
||||
*/
|
||||
abstract public function getChangedFiles(array $filter = []): array;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\ChangedFiles\Detector;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Git\ChangedFiles\Detecting;
|
||||
use CaptainHook\App\Hooks;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Factory class
|
||||
*
|
||||
* Responsible for finding the previous - current ranges in every scenario
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
class Factory
|
||||
{
|
||||
/**
|
||||
* List of available range detectors
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private static array $detectors = [
|
||||
'hook:pre-push' => '\\CaptainHook\\App\\Git\\ChangedFiles\\Detector\\PrePush',
|
||||
'hook:post-rewrite' => '\\CaptainHook\\App\\Git\\ChangedFiles\\Detector\\PostRewrite',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns a ChangedFiles Detector
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return \CaptainHook\App\Git\ChangedFiles\Detecting
|
||||
*/
|
||||
public function getDetector(IO $io, Repository $repository): Detecting
|
||||
{
|
||||
$command = $io->getArgument(Hooks::ARG_COMMAND);
|
||||
|
||||
/** @var \CaptainHook\App\Git\ChangedFiles\Detecting $class */
|
||||
$class = self::$detectors[$command] ?? '\\CaptainHook\\App\\Git\\ChangedFiles\\Detector\\Fallback';
|
||||
return new $class($io, $repository);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\ChangedFiles\Detector;
|
||||
|
||||
use CaptainHook\App\Git\ChangedFiles\Detector;
|
||||
use CaptainHook\App\Hooks;
|
||||
|
||||
/**
|
||||
* Class Fallback
|
||||
*
|
||||
* This class should not be used it is just a fallback if the `pre-push` or `post-rewrite`
|
||||
* variants are somehow not applicable.
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.20.0
|
||||
*/
|
||||
class Fallback extends Detector
|
||||
{
|
||||
/**
|
||||
* Returns the list of changed files in a best-guess kind of way
|
||||
*
|
||||
* @param array<string> $filter
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getChangedFiles(array $filter = []): array
|
||||
{
|
||||
return $this->repository->getDiffOperator()->getChangedFiles(
|
||||
$this->io->getArgument(Hooks::ARG_PREVIOUS_HEAD, 'HEAD@{1}'),
|
||||
'HEAD',
|
||||
$filter
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\ChangedFiles\Detector;
|
||||
|
||||
use CaptainHook\App\Git\ChangedFiles\Detector;
|
||||
use CaptainHook\App\Git\Range\Detector\PostRewrite as RangeDetector;
|
||||
|
||||
/**
|
||||
* Class PostRewrite
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.20.0
|
||||
*/
|
||||
class PostRewrite extends Detector
|
||||
{
|
||||
/**
|
||||
* Returns a list of changed files
|
||||
*
|
||||
* @param array<string> $filter
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getChangedFiles(array $filter = []): array
|
||||
{
|
||||
$detector = new RangeDetector();
|
||||
$ranges = $detector->getRanges($this->io);
|
||||
$old = $ranges[0]->from()->id();
|
||||
$new = $ranges[0]->to()->id();
|
||||
|
||||
return $this->repository->getDiffOperator()->getChangedFiles($old, $new, $filter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\ChangedFiles\Detector;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Git\ChangedFiles\Detector;
|
||||
use CaptainHook\App\Git\Range\Detector\PrePush as RangeDetector;
|
||||
use CaptainHook\App\Git\Range\PrePush as Range;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class PrePush
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.20.0
|
||||
*/
|
||||
class PrePush extends Detector
|
||||
{
|
||||
/**
|
||||
* Reflog fallback switch
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $reflogFallback = false;
|
||||
|
||||
/**
|
||||
* Activate the reflog fallback file detection
|
||||
*
|
||||
* @param bool $bool
|
||||
* @return void
|
||||
*/
|
||||
public function useReflogFallback(bool $bool): void
|
||||
{
|
||||
$this->reflogFallback = $bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return list of changed files
|
||||
*
|
||||
* @param array<string> $filter
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getChangedFiles(array $filter = []): array
|
||||
{
|
||||
$ranges = $this->getRanges();
|
||||
if (empty($ranges)) {
|
||||
return [];
|
||||
}
|
||||
$files = $this->collectChangedFiles($ranges, $filter);
|
||||
if (count($files) > 0 || !$this->reflogFallback) {
|
||||
return $files;
|
||||
}
|
||||
// by now we should have found something, but if the "branch: created" entry is gone from the reflog,
|
||||
// try to find as many commits belonging to this branch as possible
|
||||
$branch = $ranges[0]->to()->branch();
|
||||
$revisions = $this->repository->getLogOperator()->getBranchRevsFromRefLog($branch);
|
||||
return $this->repository->getLogOperator()->getChangedFilesInRevisions($revisions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ranges from stdIn
|
||||
*
|
||||
* @return array<\CaptainHook\App\Git\Range\PrePush>
|
||||
*/
|
||||
private function getRanges(): array
|
||||
{
|
||||
$detector = new RangeDetector();
|
||||
return $detector->getRanges($this->io);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect all changed files from all ranges
|
||||
*
|
||||
* @param array<\CaptainHook\App\Git\Range\PrePush> $ranges
|
||||
* @param array<string> $filter
|
||||
* @return array<string>
|
||||
*/
|
||||
private function collectChangedFiles(array $ranges, array $filter): array
|
||||
{
|
||||
$files = [];
|
||||
foreach ($ranges as $range) {
|
||||
if ($this->isKnownBranch($range)) {
|
||||
$oldHash = $range->from()->id();
|
||||
$newHash = $range->to()->id();
|
||||
} else {
|
||||
if (!$this->reflogFallback) {
|
||||
continue;
|
||||
}
|
||||
// remote branch does not exist
|
||||
// try to find the branch starting point with the reflog
|
||||
$oldHash = $this->repository->getLogOperator()->getBranchRevFromRefLog($range->to()->branch());
|
||||
$newHash = 'HEAD';
|
||||
}
|
||||
if (!empty($oldHash)) {
|
||||
$files[] = $this->repository->getDiffOperator()->getChangedFiles($oldHash, $newHash, $filter);
|
||||
}
|
||||
}
|
||||
return array_unique(array_merge(...$files));
|
||||
}
|
||||
|
||||
/**
|
||||
* If the remote branch is known the diff can be easily determined
|
||||
*
|
||||
* @param \CaptainHook\App\Git\Range\PrePush $range
|
||||
* @return bool
|
||||
*/
|
||||
private function isKnownBranch(Range $range): bool
|
||||
{
|
||||
return !$range->from()->isZeroRev();
|
||||
}
|
||||
}
|
||||
39
lib/captainhook/captainhook/src/Git/Diff/FilterUtil.php
Normal file
39
lib/captainhook/captainhook/src/Git/Diff/FilterUtil.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\Diff;
|
||||
|
||||
abstract class FilterUtil
|
||||
{
|
||||
/**
|
||||
* Converts a value into a valid diff filter array
|
||||
*
|
||||
* @param mixed $value
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function filterFromConfigValue($value): array
|
||||
{
|
||||
return self::sanitize(
|
||||
is_array($value) ? $value : str_split((string) strtoupper($value === null ? '' : $value))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all invalid filter options
|
||||
*
|
||||
* @param array<int, string> $data
|
||||
* @return array<int, string>
|
||||
*/
|
||||
public static function sanitize(array $data): array
|
||||
{
|
||||
return array_filter($data, fn($e) => in_array($e, ['A', 'C', 'D', 'M', 'R', 'T', 'U', 'X', 'B', '*']));
|
||||
}
|
||||
}
|
||||
39
lib/captainhook/captainhook/src/Git/Range.php
Normal file
39
lib/captainhook/captainhook/src/Git/Range.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git;
|
||||
|
||||
/**
|
||||
* Range class
|
||||
*
|
||||
* Represents a git range with a starting ref and an end ref.
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
interface Range
|
||||
{
|
||||
/**
|
||||
* Returns the start ref
|
||||
*
|
||||
* @return \CaptainHook\App\Git\Rev
|
||||
*/
|
||||
public function from(): Rev;
|
||||
|
||||
/**
|
||||
* Returns the end ref
|
||||
*
|
||||
* @return \CaptainHook\App\Git\Rev
|
||||
*/
|
||||
public function to(): Rev;
|
||||
}
|
||||
36
lib/captainhook/captainhook/src/Git/Range/Detecting.php
Normal file
36
lib/captainhook/captainhook/src/Git/Range/Detecting.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\Range;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
|
||||
/**
|
||||
* Detecting interface
|
||||
*
|
||||
* Interface to gathering the previous state to current state ranges.
|
||||
* To handle gathering the ranges for pre-push, post-rewrite, post-checkout separately.
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
interface Detecting
|
||||
{
|
||||
/**
|
||||
* Returns a list of ranges marking before and after points to collect the changes happening in between
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @return array<\CaptainHook\App\Git\Range>
|
||||
*/
|
||||
public function getRanges(IO $io): array;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\Range\Detector;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Git\Range;
|
||||
use CaptainHook\App\Git\Range\Detecting;
|
||||
use CaptainHook\App\Git\Rev;
|
||||
use CaptainHook\App\Hooks;
|
||||
|
||||
/**
|
||||
* Fallback Detector
|
||||
*
|
||||
* If no detection strategy matches the fallback detector is used to find the right range.
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
class Fallback implements Detecting
|
||||
{
|
||||
/**
|
||||
* Returns the fallback range
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @return \CaptainHook\App\Git\Range\Generic[]
|
||||
*/
|
||||
public function getRanges(IO $io): array
|
||||
{
|
||||
return [
|
||||
new Range\Generic(
|
||||
new Rev\Generic($io->getArgument(Hooks::ARG_PREVIOUS_HEAD, 'HEAD@{1}')),
|
||||
new Rev\Generic('HEAD')
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\Range\Detector;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Git\Range\Detecting;
|
||||
use CaptainHook\App\Git\Range\Generic as Range;
|
||||
use CaptainHook\App\Git\Rev\Generic as Rev;
|
||||
|
||||
/**
|
||||
* Class to access the pre-push stdIn data
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
class PostRewrite implements Detecting
|
||||
{
|
||||
/**
|
||||
* Returns list of refs
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @return \CaptainHook\App\Git\Range[]
|
||||
*/
|
||||
public function getRanges(IO $io): array
|
||||
{
|
||||
return $this->createFromStdIn($io->getStandardInput());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ranges from stdIn
|
||||
*
|
||||
* @param array<string> $stdIn
|
||||
* @return array<\CaptainHook\App\Git\Range>
|
||||
*/
|
||||
private function createFromStdIn(array $stdIn): array
|
||||
{
|
||||
$ranges = [];
|
||||
foreach ($stdIn as $line) {
|
||||
if (!empty($line)) {
|
||||
$parts = explode(' ', trim($line));
|
||||
$from = new Rev(!empty($parts[1]) ? $parts[1] . '^' : 'HEAD@{1}');
|
||||
$to = new Rev('HEAD');
|
||||
$ranges[] = new Range($from, $to);
|
||||
}
|
||||
}
|
||||
return $ranges;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\Range\Detector;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Git\Range\Detecting;
|
||||
use CaptainHook\App\Git\Range\PrePush as Range;
|
||||
use CaptainHook\App\Git\Rev\PrePush as Rev;
|
||||
use CaptainHook\App\Git\Rev\Util;
|
||||
|
||||
/**
|
||||
* Class to access the pre-push stdIn data
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
class PrePush implements Detecting
|
||||
{
|
||||
/**
|
||||
* Returns list of refs
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @return array<\CaptainHook\App\Git\Range\PrePush>
|
||||
*/
|
||||
public function getRanges(IO $io): array
|
||||
{
|
||||
return $this->createFromStdIn($io->getStandardInput());
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method
|
||||
*
|
||||
* @param array<string> $stdIn
|
||||
* @return array<\CaptainHook\App\Git\Range\PrePush>
|
||||
*/
|
||||
private function createFromStdIn(array $stdIn): array
|
||||
{
|
||||
$ranges = [];
|
||||
foreach ($stdIn as $line) {
|
||||
if (empty($line)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
[$localRef, $localHash, $remoteRef, $remoteHash] = explode(' ', trim($line));
|
||||
|
||||
$from = new Rev($remoteRef, $remoteHash, Util::extractBranchInfo($remoteRef)['branch']);
|
||||
$to = new Rev($localRef, $localHash, Util::extractBranchInfo($localRef)['branch']);
|
||||
$ranges[] = new Range($from, $to);
|
||||
}
|
||||
return $ranges;
|
||||
}
|
||||
}
|
||||
70
lib/captainhook/captainhook/src/Git/Range/Generic.php
Normal file
70
lib/captainhook/captainhook/src/Git/Range/Generic.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\Range;
|
||||
|
||||
use CaptainHook\App\Git\Range;
|
||||
use CaptainHook\App\Git\Rev;
|
||||
|
||||
/**
|
||||
* Generic range implementation
|
||||
*
|
||||
* Most simple range implementation
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
class Generic implements Range
|
||||
{
|
||||
/**
|
||||
* Starting reference
|
||||
*
|
||||
* @var \CaptainHook\App\Git\Rev
|
||||
*/
|
||||
private Rev $from;
|
||||
|
||||
/**
|
||||
* Ending reference
|
||||
*
|
||||
* @var \CaptainHook\App\Git\Rev
|
||||
*/
|
||||
private Rev $to;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(Rev $from, Rev $to)
|
||||
{
|
||||
$this->from = $from;
|
||||
$this->to = $to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the git reference
|
||||
*
|
||||
* @return \CaptainHook\App\Git\Rev
|
||||
*/
|
||||
public function from(): Rev
|
||||
{
|
||||
return $this->from;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \CaptainHook\App\Git\Rev
|
||||
*/
|
||||
public function to(): Rev
|
||||
{
|
||||
return $this->to;
|
||||
}
|
||||
}
|
||||
68
lib/captainhook/captainhook/src/Git/Range/PrePush.php
Normal file
68
lib/captainhook/captainhook/src/Git/Range/PrePush.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\Range;
|
||||
|
||||
use CaptainHook\App\Git;
|
||||
use CaptainHook\App\Git\Rev\PrePush as Rev;
|
||||
|
||||
/**
|
||||
* Class
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
class PrePush implements Git\Range
|
||||
{
|
||||
/**
|
||||
* @var \CaptainHook\App\Git\Rev\PrePush
|
||||
*/
|
||||
private Rev $from;
|
||||
|
||||
/**
|
||||
* @var \CaptainHook\App\Git\Rev\PrePush
|
||||
*/
|
||||
private Rev $to;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param \CaptainHook\App\Git\Rev\PrePush $from
|
||||
* @param \CaptainHook\App\Git\Rev\PrePush $to
|
||||
*/
|
||||
public function __construct(Rev $from, Rev $to)
|
||||
{
|
||||
$this->from = $from;
|
||||
$this->to = $to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start ref
|
||||
*
|
||||
* @return \CaptainHook\App\Git\Rev\PrePush
|
||||
*/
|
||||
public function from(): Rev
|
||||
{
|
||||
return $this->from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the end ref
|
||||
*
|
||||
* @return \CaptainHook\App\Git\Rev\PrePush
|
||||
*/
|
||||
public function to(): Rev
|
||||
{
|
||||
return $this->to;
|
||||
}
|
||||
}
|
||||
35
lib/captainhook/captainhook/src/Git/Rev.php
Normal file
35
lib/captainhook/captainhook/src/Git/Rev.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git;
|
||||
|
||||
/**
|
||||
* Ref interface
|
||||
*
|
||||
* Git references can be used in git commands to identify positions in the git history.
|
||||
* For example: HEAD, 4FD60E21,
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
interface Rev
|
||||
{
|
||||
/**
|
||||
* Returns the ref id that can be used in a git command
|
||||
*
|
||||
* This can be completely a hash, branch name, ref-log position...
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string;
|
||||
}
|
||||
54
lib/captainhook/captainhook/src/Git/Rev/Generic.php
Normal file
54
lib/captainhook/captainhook/src/Git/Rev/Generic.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\Rev;
|
||||
|
||||
use CaptainHook\App\Git\Rev;
|
||||
|
||||
/**
|
||||
* Generic range implementation
|
||||
*
|
||||
* The simplest imaginable range implementation without any extra information.
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
class Generic implements Rev
|
||||
{
|
||||
/**
|
||||
* Referencing a git state
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $id;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $id
|
||||
*/
|
||||
public function __construct(string $id)
|
||||
{
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the git reference
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
112
lib/captainhook/captainhook/src/Git/Rev/PrePush.php
Normal file
112
lib/captainhook/captainhook/src/Git/Rev/PrePush.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\Rev;
|
||||
|
||||
use CaptainHook\App\Git\Rev;
|
||||
|
||||
/**
|
||||
* Git pre-push reference
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
class PrePush implements Rev
|
||||
{
|
||||
/**
|
||||
* Head path - refs/heads/main
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $head;
|
||||
|
||||
/**
|
||||
* Git hash
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $hash;
|
||||
|
||||
/**
|
||||
* Branch name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $branch;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $head
|
||||
* @param string $hash
|
||||
* @param string $branch
|
||||
*/
|
||||
public function __construct(string $head, string $hash, string $branch)
|
||||
{
|
||||
$this->head = $head;
|
||||
$this->hash = $hash;
|
||||
$this->branch = $branch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Head getter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function head(): string
|
||||
{
|
||||
return $this->head;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash getter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function hash(): string
|
||||
{
|
||||
return $this->hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Branch getter
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function branch(): string
|
||||
{
|
||||
return $this->branch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ref id that can be used in a git command
|
||||
*
|
||||
* This can be completely different thing hash, branch name, ref-log position...
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function id(): string
|
||||
{
|
||||
return $this->hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this a git dummy hash
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isZeroRev(): bool
|
||||
{
|
||||
return Util::isZeroHash($this->hash);
|
||||
}
|
||||
}
|
||||
57
lib/captainhook/captainhook/src/Git/Rev/Util.php
Normal file
57
lib/captainhook/captainhook/src/Git/Rev/Util.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Git\Rev;
|
||||
|
||||
/**
|
||||
* Util class
|
||||
*
|
||||
* Does some simple format and validation stuff
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.15.0
|
||||
*/
|
||||
abstract class Util
|
||||
{
|
||||
/**
|
||||
* Indicates if commit hash is a zero hash 0000000000000000000000000000000000000000
|
||||
*
|
||||
* @param string $hash
|
||||
* @return bool
|
||||
*/
|
||||
public static function isZeroHash(string $hash): bool
|
||||
{
|
||||
return (bool) preg_match('/^0+$/', $hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits remote and branch
|
||||
*
|
||||
* origin/main => ['remote' => 'origin', 'branch' => 'main']
|
||||
* main => ['remote' => 'origin', 'branch' => 'main']
|
||||
* ref/origin/main => ['remote' => 'origin', 'branch' => 'main']
|
||||
*
|
||||
* @param string $ref
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function extractBranchInfo(string $ref): array
|
||||
{
|
||||
$ref = (string) preg_replace('#^refs/#', '', $ref);
|
||||
$parts = explode('/', $ref);
|
||||
|
||||
return [
|
||||
'remote' => count($parts) > 1 ? array_shift($parts) : 'origin',
|
||||
'branch' => implode('/', $parts),
|
||||
];
|
||||
}
|
||||
}
|
||||
39
lib/captainhook/captainhook/src/Hook/Action.php
Normal file
39
lib/captainhook/captainhook/src/Hook/Action.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Hook;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Console\IO;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Interface Action
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 0.9.0
|
||||
*/
|
||||
interface Action
|
||||
{
|
||||
/**
|
||||
* Executes the action
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @param \CaptainHook\App\Config\Action $action
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute(Config $config, IO $io, Repository $repository, Config\Action $action): void;
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Hook\Branch\Action;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Console\IOUtil;
|
||||
use CaptainHook\App\Exception\ActionFailed;
|
||||
use CaptainHook\App\Git\Range\Detector\PrePush;
|
||||
use CaptainHook\App\Hook\Action;
|
||||
use CaptainHook\App\Hook\Restriction;
|
||||
use CaptainHook\App\Hooks;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class BlockFixupAndSquashCommits
|
||||
*
|
||||
* This action blocks pushes that contain fixup! or squash! commits.
|
||||
* Just as a security layer, so you are not pushing stuff you wanted to autosquash.
|
||||
*
|
||||
* Configure like this:
|
||||
*
|
||||
* {
|
||||
* "action": "\\CaptainHook\\App\\Hook\\Branch\\Action\\BlockFixupAndSquashCommits",
|
||||
* "options": {
|
||||
* "blockSquashCommits": true,
|
||||
* "blockFixupCommits": true,
|
||||
* "protectedBranches": ["main", "master", "integration"]
|
||||
* },
|
||||
* "conditions": []
|
||||
* }
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.11.0
|
||||
*/
|
||||
class BlockFixupAndSquashCommits implements Action
|
||||
{
|
||||
/**
|
||||
* Should fixup! commits be blocked
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $blockFixupCommits = true;
|
||||
|
||||
/**
|
||||
* Should squash! commits be blocked
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private bool $blockSquashCommits = true;
|
||||
|
||||
/**
|
||||
* List of protected branches
|
||||
*
|
||||
* If not specified all branches are protected
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
private array $protectedBranches;
|
||||
|
||||
/**
|
||||
* Return hook restriction
|
||||
*
|
||||
* @return \CaptainHook\App\Hook\Restriction
|
||||
*/
|
||||
public static function getRestriction(): Restriction
|
||||
{
|
||||
return Restriction::fromArray([Hooks::PRE_PUSH]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the BlockFixupAndSquashCommits action
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @param \CaptainHook\App\Config\Action $action
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute(Config $config, IO $io, Repository $repository, Config\Action $action): void
|
||||
{
|
||||
$rangeDetector = new PrePush();
|
||||
$rangesToPush = $rangeDetector->getRanges($io);
|
||||
|
||||
if (!$this->hasFoundRangesToCheck($rangesToPush)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->handleOptions($action->getOptions());
|
||||
|
||||
foreach ($rangesToPush as $range) {
|
||||
if (!empty($this->protectedBranches) && !in_array($range->from()->branch(), $this->protectedBranches)) {
|
||||
return;
|
||||
}
|
||||
$commits = $this->getBlockedCommits($io, $repository, $range->from()->id(), $range->to()->id());
|
||||
|
||||
if (count($commits) > 0) {
|
||||
$this->handleFailure($commits, $range->from()->branch());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if fixup or squash should be blocked
|
||||
*
|
||||
* @param \CaptainHook\App\Config\Options $options
|
||||
* @return void
|
||||
*/
|
||||
private function handleOptions(Config\Options $options): void
|
||||
{
|
||||
$this->blockSquashCommits = (bool) $options->get('blockSquashCommits', true);
|
||||
$this->blockFixupCommits = (bool) $options->get('blockFixupCommits', true);
|
||||
$this->protectedBranches = $options->get('protectedBranches', []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of commits that should be blocked
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @param string $remoteHash
|
||||
* @param string $localHash
|
||||
* @return array<\SebastianFeldmann\Git\Log\Commit>
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getBlockedCommits(IO $io, Repository $repository, string $remoteHash, string $localHash): array
|
||||
{
|
||||
$typesToCheck = $this->getTypesToBlock();
|
||||
$blocked = [];
|
||||
foreach ($repository->getLogOperator()->getCommitsBetween($remoteHash, $localHash) as $commit) {
|
||||
$prefix = IOUtil::PREFIX_OK;
|
||||
if ($this->hasToBeBlocked($commit->getSubject(), $typesToCheck)) {
|
||||
$prefix = IOUtil::PREFIX_FAIL;
|
||||
$blocked[] = $commit;
|
||||
}
|
||||
$io->write(
|
||||
' ' . $prefix . ' ' . $commit->getHash() . ' ' . $commit->getSubject(),
|
||||
true,
|
||||
IO::VERBOSE
|
||||
);
|
||||
}
|
||||
return $blocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of strings to look for in commit messages
|
||||
*
|
||||
* Will most likely return ['fixup!', 'squash!']
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
private function getTypesToBlock(): array
|
||||
{
|
||||
$strings = [];
|
||||
if ($this->blockFixupCommits) {
|
||||
$strings[] = 'fixup!';
|
||||
}
|
||||
if ($this->blockSquashCommits) {
|
||||
$strings[] = 'squash!';
|
||||
}
|
||||
return $strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the commit message starts with any of the given strings
|
||||
*
|
||||
* @param string $message
|
||||
* @param array<string> $typesToCheck
|
||||
* @return bool
|
||||
*/
|
||||
private function hasToBeBlocked(string $message, array $typesToCheck): bool
|
||||
{
|
||||
foreach ($typesToCheck as $type) {
|
||||
if (str_starts_with($message, $type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a helpful error message and throw the exception
|
||||
*
|
||||
* @param \SebastianFeldmann\Git\Log\Commit[] $commits
|
||||
* @param string $branch
|
||||
* @return void
|
||||
* @throws \CaptainHook\App\Exception\ActionFailed
|
||||
*/
|
||||
private function handleFailure(array $commits, string $branch): void
|
||||
{
|
||||
$out = [];
|
||||
foreach ($commits as $commit) {
|
||||
$out[] = ' - ' . $commit->getHash() . ' ' . $commit->getSubject();
|
||||
}
|
||||
throw new ActionFailed(
|
||||
'You are prohibited to push the following commits:' . PHP_EOL
|
||||
. ' --[ ' . $branch . ' ]-- ' . PHP_EOL
|
||||
. PHP_EOL
|
||||
. implode(PHP_EOL, $out)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if we found valid ranges to check
|
||||
*
|
||||
* @param array<\CaptainHook\App\Git\Range\PrePush> $rangesToPush
|
||||
* @return bool
|
||||
*/
|
||||
private function hasFoundRangesToCheck(array $rangesToPush): bool
|
||||
{
|
||||
if (empty($rangesToPush)) {
|
||||
return false;
|
||||
}
|
||||
if ($rangesToPush[0]->from()->isZeroRev() || $rangesToPush[0]->to()->isZeroRev()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Hook\Branch\Action;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Exception\ActionFailed;
|
||||
use CaptainHook\App\Hook\Action;
|
||||
use CaptainHook\App\Hook\Restriction;
|
||||
use CaptainHook\App\Hooks;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class EnsureNaming
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Felix Edelmann <fxedel@gmail.com>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.4.0
|
||||
*/
|
||||
class EnsureNaming implements Action
|
||||
{
|
||||
/**
|
||||
* Return hook restriction
|
||||
*
|
||||
* @return \CaptainHook\App\Hook\Restriction
|
||||
*/
|
||||
public static function getRestriction(): Restriction
|
||||
{
|
||||
return Restriction::fromArray([Hooks::PRE_COMMIT, Hooks::PRE_PUSH, Hooks::POST_CHECKOUT]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the configured action
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @param \CaptainHook\App\Config\Action $action
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute(Config $config, IO $io, Repository $repository, Config\Action $action): void
|
||||
{
|
||||
$regex = $this->getRegex($action->getOptions());
|
||||
$errorMsg = $this->getErrorMessage($action->getOptions());
|
||||
$successMsg = $this->getSuccessMessage($action->getOptions());
|
||||
|
||||
$branch = $repository->getInfoOperator()->getCurrentBranch();
|
||||
if (!preg_match($regex, $branch)) {
|
||||
throw new ActionFailed(sprintf($errorMsg, $regex));
|
||||
}
|
||||
|
||||
$io->write(['', '', sprintf($successMsg, $regex), ''], true, IO::VERBOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract regex from options array
|
||||
*
|
||||
* @param \CaptainHook\App\Config\Options $options
|
||||
* @return string
|
||||
* @throws \CaptainHook\App\Exception\ActionFailed
|
||||
*/
|
||||
protected function getRegex(Config\Options $options): string
|
||||
{
|
||||
$regex = $options->get('regex', '');
|
||||
if (empty($regex)) {
|
||||
throw new ActionFailed('No regex option');
|
||||
}
|
||||
return $regex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the error message to use
|
||||
*
|
||||
* @param \CaptainHook\App\Config\Options $options
|
||||
* @return string
|
||||
*/
|
||||
protected function getErrorMessage(Config\Options $options): string
|
||||
{
|
||||
$msg = $options->get('error', '');
|
||||
return !empty($msg) ? $msg : '<error>FAIL</error> Branch name does not match regex: %s';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the error message to use
|
||||
*
|
||||
* @param \CaptainHook\App\Config\Options $options
|
||||
* @return string
|
||||
*/
|
||||
protected function getSuccessMessage(Config\Options $options): string
|
||||
{
|
||||
$msg = $options->get('success', '');
|
||||
return !empty($msg) ? $msg : '<info>OK</info> Branch name does match regex: %s';
|
||||
}
|
||||
}
|
||||
42
lib/captainhook/captainhook/src/Hook/Cli/Command.php
Normal file
42
lib/captainhook/captainhook/src/Hook/Cli/Command.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Hook\Cli;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Action;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class Notify
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.23.1
|
||||
*/
|
||||
class Command implements Action
|
||||
{
|
||||
/**
|
||||
* Executes the action
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @param \CaptainHook\App\Config\Action $action
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute(Config $config, IO $io, Repository $repository, Config\Action $action): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Hook\Composer\Action;
|
||||
|
||||
use CaptainHook\App\Config;
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Action;
|
||||
use Exception;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class CheckLockFile
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 1.0.1
|
||||
*/
|
||||
class CheckLockFile implements Action
|
||||
{
|
||||
/**
|
||||
* Composer configuration keys that are relevant for the 'content-hash' creation
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
private $relevantKeys = [
|
||||
'name',
|
||||
'version',
|
||||
'require',
|
||||
'require-dev',
|
||||
'conflict',
|
||||
'replace',
|
||||
'provide',
|
||||
'minimum-stability',
|
||||
'prefer-stable',
|
||||
'repositories',
|
||||
'extra',
|
||||
];
|
||||
|
||||
/**
|
||||
* Executes the action
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @param \CaptainHook\App\Config\Action $action
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function execute(Config $config, IO $io, Repository $repository, Config\Action $action): void
|
||||
{
|
||||
$path = $action->getOptions()->get('path', getcwd());
|
||||
$name = $action->getOptions()->get('name', 'composer');
|
||||
$pathname = $path . DIRECTORY_SEPARATOR . $name;
|
||||
$lockFileHash = $this->getLockFileHash($pathname . '.lock');
|
||||
$configFileHash = $this->getConfigFileHash($pathname . '.json');
|
||||
|
||||
if ($lockFileHash !== $configFileHash) {
|
||||
throw new Exception('Your composer.lock file is out of date');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the composer.lock file and extract the composer.json hash
|
||||
*
|
||||
* @param string $composerLock
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getLockFileHash(string $composerLock): string
|
||||
{
|
||||
$lockFile = json_decode($this->loadFile($composerLock));
|
||||
$hashKey = 'content-hash';
|
||||
|
||||
if (!isset($lockFile->$hashKey)) {
|
||||
throw new Exception('could not find content hash, please update composer to the latest version');
|
||||
}
|
||||
|
||||
return $lockFile->$hashKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the composer.json file and create a md5 hash on its relevant content
|
||||
*
|
||||
* This more or less is composer internal code to generate the content-hash so this might not be the best idea
|
||||
* and will be removed in the future.
|
||||
*
|
||||
* @param string $composerJson
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function getConfigFileHash(string $composerJson): string
|
||||
{
|
||||
$content = json_decode($this->loadFile($composerJson), true);
|
||||
$relevantContent = [];
|
||||
|
||||
foreach (array_intersect($this->relevantKeys, array_keys($content)) as $key) {
|
||||
$relevantContent[$key] = $content[$key];
|
||||
}
|
||||
if (isset($content['config']['platform'])) {
|
||||
$relevantContent['config']['platform'] = $content['config']['platform'];
|
||||
}
|
||||
ksort($relevantContent);
|
||||
|
||||
return md5((string)json_encode($relevantContent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a composer file
|
||||
*
|
||||
* @param string $file
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function loadFile(string $file): string
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
throw new Exception($file . ' not found');
|
||||
}
|
||||
return (string)file_get_contents($file);
|
||||
}
|
||||
}
|
||||
35
lib/captainhook/captainhook/src/Hook/Condition.php
Normal file
35
lib/captainhook/captainhook/src/Hook/Condition.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Hook;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Interface Conditions
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 4.2.0
|
||||
*/
|
||||
interface Condition
|
||||
{
|
||||
/**
|
||||
* Evaluates a condition
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(IO $io, Repository $repository): bool;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CaptainHook\App\Hook\Condition\Branch;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Condition;
|
||||
use CaptainHook\App\Hook\FileList;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Files condition
|
||||
*
|
||||
* Example configuration:
|
||||
*
|
||||
* "action": "some-action"
|
||||
* "conditions": [
|
||||
* {"exec": "\\CaptainHook\\App\\Hook\\Condition\\Branch\\Files",
|
||||
* "args": [
|
||||
* {"compare-to": "main", "of-type": "php"}
|
||||
* ]}
|
||||
* ]
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.21.0
|
||||
*/
|
||||
class Files implements Condition
|
||||
{
|
||||
/**
|
||||
* Options
|
||||
* - compare-to: source branch if known, otherwise the reflog is used to figure it out
|
||||
* - in-directory: only check for files in given directory
|
||||
* - of-type: only check for files of given type
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
private array $options;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param array<string> $options
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
{
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current branch contains changes to files
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(IO $io, Repository $repository): bool
|
||||
{
|
||||
$branch = $repository->getInfoOperator()->getCurrentBranch();
|
||||
$start = $this->options['compared-to'] ?? $repository->getLogOperator()->getBranchRevFromRefLog($branch);
|
||||
|
||||
if (empty($start)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$files = $repository->getLogOperator()->getChangedFilesSince($start, ['A', 'C', 'M', 'R']);
|
||||
|
||||
return count(FileList::filter($files, $this->options)) > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CaptainHook\App\Hook\Condition\Branch;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Condition;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* OnBranch condition
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.0.0
|
||||
*/
|
||||
abstract class Name implements Condition
|
||||
{
|
||||
/**
|
||||
* Branch name to compare
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected string $name;
|
||||
|
||||
/**
|
||||
* OnBranch constructor.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is the current branch is equal to the configured one
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isTrue(IO $io, Repository $repository): bool;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CaptainHook\App\Hook\Condition\Branch;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Condition;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* NotOn condition
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.20.2
|
||||
*/
|
||||
class NotOn extends Name
|
||||
{
|
||||
/**
|
||||
* Check is the current branch is not equal to the configured one
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(IO $io, Repository $repository): bool
|
||||
{
|
||||
return trim($repository->getInfoOperator()->getCurrentBranch()) !== $this->name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CaptainHook\App\Hook\Condition\Branch;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Condition;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* NotOnMatching condition
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.20.2
|
||||
*/
|
||||
class NotOnMatching extends Name
|
||||
{
|
||||
/**
|
||||
* Check is the current branch is not matched by the configured regex
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(IO $io, Repository $repository): bool
|
||||
{
|
||||
return preg_match($this->name, trim($repository->getInfoOperator()->getCurrentBranch())) === 0;
|
||||
}
|
||||
}
|
||||
41
lib/captainhook/captainhook/src/Hook/Condition/Branch/On.php
Normal file
41
lib/captainhook/captainhook/src/Hook/Condition/Branch/On.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CaptainHook\App\Hook\Condition\Branch;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Condition;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* On condition
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.20.2
|
||||
*/
|
||||
class On extends Name
|
||||
{
|
||||
/**
|
||||
* Check is the current branch is equal to the configured one
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(IO $io, Repository $repository): bool
|
||||
{
|
||||
return trim($repository->getInfoOperator()->getCurrentBranch()) === $this->name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook.
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace CaptainHook\App\Hook\Condition\Branch;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Condition;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* OnMatching condition
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.20.2
|
||||
*/
|
||||
class OnMatching extends Name
|
||||
{
|
||||
/**
|
||||
* Check is the current branch is matched by the configured regex
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(IO $io, Repository $repository): bool
|
||||
{
|
||||
return preg_match($this->name, trim($repository->getInfoOperator()->getCurrentBranch())) === 1;
|
||||
}
|
||||
}
|
||||
66
lib/captainhook/captainhook/src/Hook/Condition/Cli.php
Normal file
66
lib/captainhook/captainhook/src/Hook/Condition/Cli.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Hook\Condition;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Condition;
|
||||
use SebastianFeldmann\Cli\Processor;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class Cli
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 4.2.0
|
||||
*/
|
||||
class Cli implements Condition
|
||||
{
|
||||
/**
|
||||
* Binary executor
|
||||
*
|
||||
* @var \SebastianFeldmann\Cli\Processor
|
||||
*/
|
||||
private $processor;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $command;
|
||||
|
||||
/**
|
||||
* Cli constructor.
|
||||
*
|
||||
* @param \SebastianFeldmann\Cli\Processor $processor
|
||||
* @param string $command
|
||||
*/
|
||||
public function __construct(Processor $processor, string $command)
|
||||
{
|
||||
$this->processor = $processor;
|
||||
$this->command = $command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates a condition
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(IO $io, Repository $repository): bool
|
||||
{
|
||||
$result = $this->processor->run($this->command);
|
||||
|
||||
return $result->isSuccessful();
|
||||
}
|
||||
}
|
||||
71
lib/captainhook/captainhook/src/Hook/Condition/Config.php
Normal file
71
lib/captainhook/captainhook/src/Hook/Condition/Config.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Hook\Condition;
|
||||
|
||||
use CaptainHook\App\Config as AppConfig;
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Condition;
|
||||
use RuntimeException;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class FileChange
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 4.2.0
|
||||
*/
|
||||
abstract class Config implements ConfigDependant, Condition
|
||||
{
|
||||
/**
|
||||
* @var \CaptainHook\App\Config|null
|
||||
*/
|
||||
protected ?AppConfig $config = null;
|
||||
|
||||
/**
|
||||
* Config setter
|
||||
*
|
||||
* @param \CaptainHook\App\Config $config
|
||||
* @return void
|
||||
*/
|
||||
public function setConfig(AppConfig $config): void
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the customer value exists and return izs boolish value
|
||||
*
|
||||
* @param string $value
|
||||
* @param bool $default
|
||||
* @return bool
|
||||
*/
|
||||
protected function checkCustomValue(string $value, bool $default): bool
|
||||
{
|
||||
if (null === $this->config) {
|
||||
throw new RuntimeException('config not set');
|
||||
}
|
||||
$customSettings = $this->config->getCustomSettings();
|
||||
$valueToCheck = $customSettings[$value] ?? $default;
|
||||
return filter_var($valueToCheck, FILTER_VALIDATE_BOOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates a condition
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isTrue(IO $io, Repository $repository): bool;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Hook\Condition\Config;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Condition;
|
||||
use CaptainHook\App\Hooks;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class CustomValueIsFalsy
|
||||
*
|
||||
* Example configuration:
|
||||
*
|
||||
* "action": "some-action"
|
||||
* "conditions": [
|
||||
* {"exec": "\\CaptainHook\\App\\Hook\\Condition\\Config\\CustomValueIsFalsy",
|
||||
* "args": [
|
||||
* "NAME_OF_CUSTOM_VALUE"
|
||||
* ]}
|
||||
* ]
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.17.2
|
||||
*/
|
||||
class CustomValueIsFalsy extends Condition\Config
|
||||
{
|
||||
/**
|
||||
* Custom config value to check
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $value;
|
||||
|
||||
/**
|
||||
* CustomValueIsFalsy constructor
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the condition
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(IO $io, Repository $repository): bool
|
||||
{
|
||||
return !$this->checkCustomValue($this->value, false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of CaptainHook
|
||||
*
|
||||
* (c) Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace CaptainHook\App\Hook\Condition\Config;
|
||||
|
||||
use CaptainHook\App\Console\IO;
|
||||
use CaptainHook\App\Hook\Condition;
|
||||
use CaptainHook\App\Hooks;
|
||||
use SebastianFeldmann\Git\Repository;
|
||||
|
||||
/**
|
||||
* Class CustomValueIsTruthy
|
||||
*
|
||||
* Example configuration:
|
||||
*
|
||||
* "action": "some-action"
|
||||
* "conditions": [
|
||||
* {"exec": "\\CaptainHook\\App\\Hook\\Condition\\Config\\CustomValueIsTruthy",
|
||||
* "args": [
|
||||
* "NAME_OF_CUSTOM_VALUE"
|
||||
* ]}
|
||||
* ]
|
||||
*
|
||||
* @package CaptainHook
|
||||
* @author Sebastian Feldmann <sf@sebastian-feldmann.info>
|
||||
* @link https://github.com/captainhook-git/captainhook
|
||||
* @since Class available since Release 5.17.2
|
||||
*/
|
||||
class CustomValueIsTruthy extends Condition\Config
|
||||
{
|
||||
/**
|
||||
* Custom config value to check
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private string $value;
|
||||
|
||||
/**
|
||||
* CustomValueIsTruthy constructor
|
||||
*
|
||||
* @param string $value
|
||||
*/
|
||||
public function __construct(string $value)
|
||||
{
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the condition
|
||||
*
|
||||
* @param \CaptainHook\App\Console\IO $io
|
||||
* @param \SebastianFeldmann\Git\Repository $repository
|
||||
* @return bool
|
||||
*/
|
||||
public function isTrue(IO $io, Repository $repository): bool
|
||||
{
|
||||
return $this->checkCustomValue($this->value, false);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user