Hook::execute()   B
last analyzed

Complexity

Conditions 7
Paths 15

Size

Total Lines 48

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 48
rs 8.2012
c 0
b 0
f 0
cc 7
nc 15
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright (C) 2016 Billie Thompson
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace PurpleBooth\GitLintValidators\Command;
13
14
use PurpleBooth\GitLintValidators\MessageImplementation;
15
use PurpleBooth\GitLintValidators\Status\Status;
16
use PurpleBooth\GitLintValidators\ValidatorFactoryImplementation;
17
use Symfony\Component\Console\Command\Command;
18
use Symfony\Component\Console\Input\InputArgument;
19
use Symfony\Component\Console\Input\InputDefinition;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Input\InputOption;
22
use Symfony\Component\Console\Output\OutputInterface;
23
use Symfony\Component\Console\Style\SymfonyStyle;
24
25
/**
26
 * A command that allows you to try out the library.
27
 */
28
class Hook extends Command
29
{
30
    const COMMAND_NAME = 'git-lint-validator:hook';
31
32
    const ARGUMENT_COMMIT_MESSAGE_FILE = 'commit-message-file';
33
34
    const OPTION_COMMENT_CHAR = 'comment-char';
35
36
    const OPTION_IGNORE = 'ignore';
37
38
    /**
39
     * Configures the current command.
40
     */
41
    protected function configure()
42
    {
43
        $this->setName(self::COMMAND_NAME);
44
        $this->setDescription('Checks the style of commit messages');
45
46
        $help = '';
47
        $help .= "Check your commit messages to ensure they follow the guidelines described by Chris Beams.\n";
48
        $help .= "To enable the Git hook in your project run 'composer install-git-hook.'\n";
49
        $help .= "\n";
50
        $help .= "\n";
51
        $help .= "Here are some good articles on commit message style:\n";
52
        $help .= "\n";
53
        $help .= "* http://chris.beams.io/posts/git-commit/\n";
54
        $help .= "* https://git-scm.com/book/ch5-2.html#Commit-Guidelines\n";
55
        $help .= "* https://github.com/blog/926-shiny-new-commit-styles\n";
56
57
        $this->setHelp($help);
58
        $this->setDefinition(
59
            new InputDefinition(
60
                [
61
                    new InputArgument(
62
                        self::ARGUMENT_COMMIT_MESSAGE_FILE,
63
                        InputArgument::REQUIRED,
64
                        'Path to commit message file'
65
                    ),
66
                    new InputOption(
67
                        self::OPTION_IGNORE,
68
                        ['i'],
69
                        InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL,
70
                        'Ignore a commit message that matches this pattern and don\'t test it',
71
                        ['/^Merge branch/']
72
                    ),
73
                    new InputOption(
74
                        self::OPTION_COMMENT_CHAR,
75
                        ['c'],
76
                        InputOption::VALUE_OPTIONAL,
77
                        'Ignore lines that are prefixed with this character',
78
                        '#'
79
                    ),
80
                ]
81
            )
82
        );
83
    }
84
85
    /**
86
     * Executes the current command.
87
     *
88
     * This method is not abstract because you can use this class
89
     * as a concrete class. In this case, instead of defining the
90
     * execute() method, you set the code to execute by passing
91
     * a Closure to the setCode() method.
92
     *
93
     * @param InputInterface  $input  An InputInterface instance
94
     * @param OutputInterface $output An OutputInterface instance
95
     *
96
     * @return null|int null or 0 if everything went fine, or an error code
97
     *
98
     * @see setCode()
99
     */
100
    protected function execute(InputInterface $input, OutputInterface $output)
101
    {
102
        $styleHelper = new SymfonyStyle($input, $output);
103
        $validatorFactory = new ValidatorFactoryImplementation();
104
        $validators = $validatorFactory->getMessageValidator();
105
106
        $commitMessage = file_get_contents($input->getArgument(self::ARGUMENT_COMMIT_MESSAGE_FILE));
107
        $commentCharacter = $input->getOption(self::OPTION_COMMENT_CHAR);
108
        $ignorePatterns = $input->getOption(self::OPTION_IGNORE);
109
110
        foreach ($ignorePatterns as $ignorePattern) {
0 ignored issues
show
Bug introduced by
The expression $ignorePatterns of type string|array<integer,string>|boolean|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
111
            if (preg_match($ignorePattern, $commitMessage)) {
112
                return 0;
113
            }
114
        }
115
116
        $safeCommitMessage = preg_replace('/'.preg_quote($commentCharacter).'.*/', '', $commitMessage);
117
        $message = new MessageImplementation($safeCommitMessage);
118
119
        $validators->validate($message);
120
121
        if (count($message->getStatuses()) < 1) {
122
            return 0;
123
        }
124
125
        $statusList = [];
126
        $isPositive = true;
127
128
        /** @var Status $status */
129
        foreach ($message->getStatuses() as $status) {
130
            $statusList[] = $status->getMessage().' ('.$status->getDetailsUrl().')';
131
132
            $isPositive = $status->isPositive() && $isPositive;
133
        }
134
135
        if ($isPositive) {
136
            return 0;
137
        }
138
139
        $styleHelper->error('Incorrectly formatted commit message');
140
        $styleHelper->listing($statusList);
141
142
        $styleHelper->section('Your Commit Message');
143
        $styleHelper->block($commitMessage);
144
        $styleHelper->warning('A commit has not been created');
145
146
        return 1;
147
    }
148
}
149