Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
ChangelogCheckCommand
100.00% covered (success)
100.00%
37 / 37
100.00% covered (success)
100.00%
3 / 3
4
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 configure
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
1
 execute
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5/**
6 * Fast Forward Development Tools for PHP projects.
7 *
8 * This file is part of fast-forward/dev-tools project.
9 *
10 * @author   Felipe SayĆ£o Lobato Abreu <github@mentordosnerds.com>
11 * @license  https://opensource.org/licenses/MIT MIT License
12 *
13 * @see      https://github.com/php-fast-forward/
14 * @see      https://github.com/php-fast-forward/dev-tools
15 * @see      https://github.com/php-fast-forward/dev-tools/issues
16 * @see      https://php-fast-forward.github.io/dev-tools/
17 * @see      https://datatracker.ietf.org/doc/html/rfc2119
18 */
19
20namespace FastForward\DevTools\Console\Command;
21
22use FastForward\DevTools\Console\Command\Traits\LogsCommandResults;
23use FastForward\DevTools\Changelog\Checker\UnreleasedEntryCheckerInterface;
24use FastForward\DevTools\Console\Input\HasJsonOption;
25use FastForward\DevTools\Filesystem\FilesystemInterface;
26use Psr\Log\LoggerInterface;
27use Symfony\Component\Console\Attribute\AsCommand;
28use Symfony\Component\Console\Command\Command;
29use Symfony\Component\Console\Input\InputInterface;
30use Symfony\Component\Console\Input\InputOption;
31use Symfony\Component\Console\Output\OutputInterface;
32
33/**
34 * Verifies that the changelog contains pending unreleased notes.
35 */
36#[AsCommand(
37    name: 'changelog:check',
38    description: 'Checks whether a changelog file contains meaningful unreleased entries.'
39)]
40 class ChangelogCheckCommand extends Command
41{
42    use HasJsonOption;
43    use LogsCommandResults;
44
45    /**
46     * @param FilesystemInterface $filesystem
47     * @param UnreleasedEntryCheckerInterface $unreleasedEntryChecker
48     * @param LoggerInterface $logger
49     */
50    public function __construct(
51        private  FilesystemInterface $filesystem,
52        private  UnreleasedEntryCheckerInterface $unreleasedEntryChecker,
53        private  LoggerInterface $logger,
54    ) {
55        parent::__construct();
56    }
57
58    /**
59     * Configures changelog verification options.
60     */
61    protected function configure(): void
62    {
63        $this->setHelp(
64            'This command validates the current Unreleased section and may compare it against a base git'
65            . ' reference to enforce pull request changelog updates.'
66        );
67
68        $this->addJsonOption()
69            ->addOption(
70                name: 'against',
71                mode: InputOption::VALUE_REQUIRED,
72                description: 'Optional git reference used as the baseline changelog file.',
73            )
74            ->addOption(
75                name: 'file',
76                mode: InputOption::VALUE_REQUIRED,
77                description: 'Path to the changelog file.',
78                default: 'CHANGELOG.md',
79            );
80    }
81
82    /**
83     * Executes the changelog verification.
84     *
85     * @param InputInterface $input
86     * @param OutputInterface $output
87     */
88    protected function execute(InputInterface $input, OutputInterface $output): int
89    {
90        $path = $this->filesystem->getAbsolutePath($input->getOption('file'));
91        $against = $input->getOption('against');
92
93        $hasPendingChanges = $this->unreleasedEntryChecker
94            ->hasPendingChanges($path, $against);
95
96        if ($hasPendingChanges) {
97            return $this->success(
98                'The changelog contains unreleased changes ready for review.',
99                $input,
100                [
101                    'has_pending_changes' => true,
102                ],
103            );
104        }
105
106        return $this->failure(
107            'The changelog must add a meaningful entry to the Unreleased section.',
108            $input,
109            [
110                'has_pending_changes' => false,
111            ],
112            (string) $input->getOption('file'),
113        );
114    }
115}