config = $config;
$this->filesystem = $filesystem;
$this->processBuilder = $processBuilder;
$this->paths = $paths;
}
public static function getDefaultName(): string
{
return self::COMMAND_NAME;
}
/**
* Configure command.
*/
protected function configure(): void
{
$this->setDescription('Registers the Git hooks');
}
/**
* @return int|void
*/
public function execute(InputInterface $input, OutputInterface $output): int
{
$this->input = $input;
$gitHooksPath = $this->paths->getGitHooksDir();
$resourceHooksPath = $this->filesystem->buildPath(
$this->paths->getInternalGitHookTemplatesPath(),
$this->config->getHooksPreset()
);
$customHooksPath = $this->config->getHooksDir();
// Some git clients do not automatically create a git hooks folder.
if (!$this->filesystem->exists($gitHooksPath)) {
$this->filesystem->mkdir($gitHooksPath);
$output->writeln(sprintf(
'Created git hooks folder at: %s',
$gitHooksPath
));
}
foreach (self::$hooks as $hook) {
$gitHook = $this->filesystem->buildPath($gitHooksPath, $hook);
$hookTemplate = $this->filesystem->guessFile(
array_filter([
$customHooksPath,
$resourceHooksPath,
]),
[$hook]
);
if (!$this->filesystem->exists($hookTemplate)) {
throw new RuntimeException(
sprintf('Could not find hook template for %s at %s.', $hook, $hookTemplate)
);
}
$content = $this->parseHookBody($hook, $hookTemplate);
$this->filesystem->dumpFile($gitHook, $content);
$this->filesystem->chmod($gitHook, 0775);
}
$output->writeln('Watch out! GrumPHP is sniffing your commits!');
return 0;
}
protected function parseHookBody(string $hook, string $templateFile): string
{
$content = $this->filesystem->readPath($templateFile);
$replacements = [
'${HOOK_EXEC_PATH}' => $this->paths->getProjectDirRelativeToGitDir(),
'$(HOOK_COMMAND)' => $this->generateHookCommand('git:'.$hook),
];
foreach ($this->config->getGitHookVariables() as $key => $value) {
$replacements[sprintf('$(%s)', $key)] = ProcessUtils::escapeArgumentsFromString($value);
}
return str_replace(array_keys($replacements), array_values($replacements), $content);
}
/**
* @throws \GrumPHP\Exception\FileNotFoundException
*/
protected function generateHookCommand(string $command): string
{
$configFile = $this->useExoticConfigFile();
$arguments = $this->processBuilder->createArgumentsForCommand(
'grumphp',
function (string $executablePath): string {
return $this->proposeRelativeUnixPath($executablePath);
}
);
$arguments->add($command);
$arguments->addOptionalArgument('--config=%s', $configFile);
$process = $this->processBuilder->buildProcess($arguments);
return $process->getCommandLine();
}
/**
* This method will tell you which exotic configuration file should be used.
*
* @return null|string
*/
protected function useExoticConfigFile()
{
if (!$configPath = $this->input->getOption('config')) {
// Auto discovereable ...
return null;
}
if (!$this->filesystem->exists($configPath)) {
return null;
}
return $this->proposeRelativeUnixPath($configPath);
}
/**
* Always try to make paths relative against the project dir inside the git hooks.
* If it is not possible: the full path will be used.
*/
private function proposeRelativeUnixPath(string $path): string
{
return $this->filesystem->ensureUnixPath(
$this->paths->makePathRelativeToProjectDirWhenInSubFolder($path)
);
}
}