botble/git-commit-checker

View on GitHub
src/Commands/InstallCommand.php

Summary

Maintainability
A
35 mins
Test Coverage
<?php

namespace Botble\GitCommitChecker\Commands;

use Illuminate\Console\Application;
use Illuminate\Console\Command;
use Illuminate\Console\ConfirmableTrait;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputOption;

#[AsCommand('git-commit-checker:install', 'Install "pre_commit" hook into your git.')]
class InstallCommand extends Command
{
    use ConfirmableTrait;

    public function handle(): int
    {
        if (! $this->laravel->isLocal()) {
            $this->components->error('git-commit-checker is only available in local environment.');

            return self::FAILURE;
        }

        if (! $this->laravel['files']->isDirectory($this->laravel->basePath('.git'))) {
            $this->components->error('git-commit-checker is only available when using Git.');

            return self::FAILURE;
        }

        foreach ($this->laravel['config']->get('git-commit-checker.hooks') as $hook => $command) {
            $this->install($hook, $command)
                ? $this->components->info("Hook [$hook] is installed successfully.")
                : $this->components->error("Unable to install hook [$hook].");
        }

        $pintConfigFilePath = $this->laravel->basePath('pint.json');

        if ($this->laravel['files']->exists($pintConfigFilePath)) {
            if ($this->components->confirm('A <comment>pint.json</comment> exists. Do you want to overwrite this file?')) {
                $this->generatePintConfiguration($pintConfigFilePath);
            }

            return self::SUCCESS;
        }

        if ($this->components->confirm('A <comment>pint.json</comment> does not exists. Do you want to create this file?')) {
            $this->generatePintConfiguration($pintConfigFilePath);
        }

        return self::SUCCESS;
    }

    protected function install(string $hook, string $class): bool
    {
        if (! class_exists($class)) {
            $this->components->error("Class [$class] not found.");
            abort(1);
        }

        $command = new $class();

        if ($command instanceof Command === false) {
            $this->components->error("Class [$class] is not instance of " . Command::class . '.');
            abort(1);
        }

        $script = $this->generateHookScript($command->getName());

        $path = $this->laravel->basePath('.git/hooks/' . $hook);
        $relativePath = ltrim(str_replace($this->laravel->basePath(), '', $path), DIRECTORY_SEPARATOR);

        if (
            $this->laravel['files']->exists($path) &&
            ! $this->confirmToProceed($relativePath . ' already exists, do you want to overwrite it?', true)
        ) {
            return false;
        }

        return $this->writeHookScript($path, $script);
    }

    protected function generateHookScript(string $signature): string
    {
        return sprintf("#!/bin/sh\n\n%s\n", Application::formatCommandString($signature));
    }

    protected function generatePintConfiguration(string $path): void
    {
        $presets = $this->laravel['config']->get('git-commit-checker.pint.presets', []);

        if (empty($presets)) {
            $this->components->error('Do not found a list of supported presets');
            abort(1);
        }

        $standard = $this->components->choice('Which standard you want to use?', array_values($presets), 0);

        $preset = array_flip($presets)[$standard];

        if (! $this->laravel['files']->put(
            $path,
            json_encode(
                $preset !== 'recommended'
                    ? ['preset' => $preset]
                    : $this->laravel['config']->get('git-commit-checker.pint.recommended_preset'),
                JSON_PRETTY_PRINT
            ) . PHP_EOL
        )) {
            $this->components->error('Unable to write ' . $path);
            abort(1);
        }

        $this->components->info(
            "Created <comment>$path</comment> using <comment>$presets[$preset]</comment> preset successfully"
        );
    }

    protected function writeHookScript(string $path, string $script): bool
    {
        if (! $this->laravel['files']->put($path, $script)) {
            return false;
        }

        if (! $this->laravel['files']->chmod($path, 0755)) {
            return false;
        }

        return true;
    }

    protected function configure(): void
    {
        $this->addOption('force', 'f', InputOption::VALUE_NONE, 'Force the operation to overwrite existing git hook files');
    }
}