Issues (224)

src/Console/Command/Validate.php (2 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the box project.
7
 *
8
 * (c) Kevin Herrera <[email protected]>
9
 *     Théo Fidry <[email protected]>
10
 *
11
 * This source file is subject to the MIT license that is bundled
12
 * with this source code in the file LICENSE.
13
 */
14
15
namespace KevinGH\Box\Console\Command;
16
17
use Fidry\Console\Command\Command;
18
use Fidry\Console\Command\Configuration as ConsoleConfiguration;
19
use Fidry\Console\ExitCode;
20
use Fidry\Console\IO;
0 ignored issues
show
The type Fidry\Console\IO was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use KevinGH\Box\Configuration\Configuration;
22
use KevinGH\Box\Console\ConfigurationLoader;
23
use KevinGH\Box\Console\ConfigurationLocator;
24
use KevinGH\Box\Console\MessageRenderer;
25
use KevinGH\Box\Json\JsonValidationException;
26
use Symfony\Component\Console\Exception\RuntimeException;
27
use Symfony\Component\Console\Input\InputArgument;
28
use Symfony\Component\Console\Input\InputOption;
29
use Throwable;
30
use Webmozart\Assert\Assert;
31
use function count;
32
use function sprintf;
33
34
/**
35
 * @private
36
 */
37
final class Validate implements Command
38
{
39
    private const FILE_ARGUMENT = 'file';
40
    private const IGNORE_MESSAGES_OPTION = 'ignore-recommendations-and-warnings';
41
42
    public function getConfiguration(): ConsoleConfiguration
43
    {
44
        return new ConsoleConfiguration(
45
            'validate',
46
            '⚙  Validates the configuration file',
47
            <<<'HELP'
48
                The <info>%command.name%</info> command will validate the configuration file
49
                and report any errors found, if any.
50
                <comment>
51
                  This command relies on a configuration file for loading
52
                  PHAR packaging settings. If a configuration file is not
53
                  specified through the <info>--configuration|-c</info> option, one of
54
                  the following files will be used (in order): <info>box.json,
55
                  box.json.dist</info>
56
                </comment>
57
                HELP,
58
            [
59
                new InputArgument(
60
                    self::FILE_ARGUMENT,
61
                    InputArgument::OPTIONAL,
62
                    'The configuration file. (default: box.json, box.json.dist)',
63
                ),
64
            ],
65
            [
66
                new InputOption(
67
                    self::IGNORE_MESSAGES_OPTION,
68
                    'i',
69
                    InputOption::VALUE_NONE,
70
                    'Will not return a faulty code when a recommendation or warning is found',
71
                ),
72
            ],
73
        );
74
    }
75
76
    public function execute(IO $io): int
77
    {
78
        try {
79
            $config = ConfigurationLoader::getConfig(
80
                $io->getTypedArgument(self::FILE_ARGUMENT)->asNullableNonEmptyString() ?? ConfigurationLocator::findDefaultPath(),
81
                $io,
82
                false,
83
            );
84
        } catch (Throwable $throwable) {
85
            // Continue
86
        }
87
88
        if (isset($config)) {
89
            return self::checkConfig($config, $io);
90
        }
91
92
        Assert::true(isset($throwable));
93
94
        return self::handleFailure($throwable, $io);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $throwable does not seem to be defined for all execution paths leading up to this point.
Loading history...
95
    }
96
97
    private static function checkConfig(Configuration $config, IO $io): int
98
    {
99
        $ignoreRecommendationsAndWarnings = $io->getTypedOption(self::IGNORE_MESSAGES_OPTION)->asBoolean();
100
101
        $recommendations = $config->getRecommendations();
102
        $warnings = $config->getWarnings();
103
104
        MessageRenderer::render($io, $recommendations, $warnings);
105
106
        $hasRecommendationsOrWarnings = 0 === count($recommendations) && 0 === count($warnings);
107
108
        if (false === $hasRecommendationsOrWarnings) {
109
            if (0 === count($recommendations)) {
110
                $io->caution('The configuration file passed the validation with warnings.');
111
            } elseif (0 === count($warnings)) {
112
                $io->caution('The configuration file passed the validation with recommendations.');
113
            } else {
114
                $io->caution('The configuration file passed the validation with recommendations and warnings.');
115
            }
116
        } else {
117
            $io->success('The configuration file passed the validation.');
118
        }
119
120
        return $hasRecommendationsOrWarnings || $ignoreRecommendationsAndWarnings
121
            ? ExitCode::SUCCESS
122
            : ExitCode::FAILURE;
123
    }
124
125
    private static function handleFailure(Throwable $throwable, IO $io): int
126
    {
127
        if ($io->isVerbose()) {
128
            throw new RuntimeException(
129
                sprintf(
130
                    'The configuration file failed validation: %s',
131
                    $throwable->getMessage(),
132
                ),
133
                $throwable->getCode(),
134
                $throwable,
135
            );
136
        }
137
138
        return $throwable instanceof JsonValidationException
139
            ? self::handleJsonValidationFailure($throwable, $io)
140
            : self::handleGenericFailure($throwable, $io);
141
    }
142
143
    private static function handleJsonValidationFailure(JsonValidationException $exception, IO $io): int
144
    {
145
        $io->writeln(
146
            sprintf(
147
                '<error>The configuration file failed validation: "%s" does not match the expected JSON '
148
                .'schema:</error>',
149
                $exception->getValidatedFile(),
150
            ),
151
        );
152
153
        $io->writeln('');
154
155
        foreach ($exception->getErrors() as $error) {
156
            $io->writeln("<comment>  - {$error}</comment>");
157
        }
158
159
        return ExitCode::FAILURE;
160
    }
161
162
    private static function handleGenericFailure(Throwable $throwable, IO $io): int
163
    {
164
        $errorMessage = sprintf('The configuration file failed validation: %s', $throwable->getMessage());
165
166
        $io->writeln(
167
            sprintf(
168
                '<error>%s</error>',
169
                $errorMessage,
170
            ),
171
        );
172
173
        return ExitCode::FAILURE;
174
    }
175
}
176