Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
3 / 3
CRAP
100.00% covered (success)
100.00%
1 / 1
ChangelogNextVersionCommand
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
3 / 3
5
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%
23 / 23
100.00% covered (success)
100.00%
1 / 1
3
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 Throwable;
24use FastForward\DevTools\Changelog\Manager\ChangelogManagerInterface;
25use FastForward\DevTools\Console\Input\HasJsonOption;
26use FastForward\DevTools\Filesystem\FilesystemInterface;
27use Psr\Log\LoggerInterface;
28use Symfony\Component\Console\Attribute\AsCommand;
29use Symfony\Component\Console\Command\Command;
30use Symfony\Component\Console\Input\InputInterface;
31use Symfony\Component\Console\Input\InputOption;
32use Symfony\Component\Console\Output\OutputInterface;
33
34/**
35 * Infers the next semantic version from changelog content.
36 */
37#[AsCommand(
38    name: 'changelog:next-version',
39    description: 'Infers the next semantic version from the Unreleased changelog section.'
40)]
41 class ChangelogNextVersionCommand extends Command
42{
43    use HasJsonOption;
44    use LogsCommandResults;
45
46    /**
47     * @param FilesystemInterface $filesystem
48     * @param ChangelogManagerInterface $changelogManager
49     * @param LoggerInterface $logger
50     */
51    public function __construct(
52        private  FilesystemInterface $filesystem,
53        private  ChangelogManagerInterface $changelogManager,
54        private  LoggerInterface $logger,
55    ) {
56        parent::__construct();
57    }
58
59    /**
60     * Configures version inference options.
61     */
62    protected function configure(): void
63    {
64        $this->setHelp(
65            'This command inspects Unreleased changelog categories and prints the next semantic version inferred'
66            . ' from the current changelog state.'
67        );
68
69        $this->addJsonOption()
70            ->addOption(
71                name: 'file',
72                mode: InputOption::VALUE_REQUIRED,
73                description: 'Path to the changelog file.',
74                default: 'CHANGELOG.md',
75            )
76            ->addOption(
77                name: 'current-version',
78                mode: InputOption::VALUE_REQUIRED,
79                description: 'Explicit current version to use as the bump base.',
80            );
81    }
82
83    /**
84     * Prints the inferred next semantic version.
85     *
86     * @param InputInterface $input
87     * @param OutputInterface $output
88     */
89    protected function execute(InputInterface $input, OutputInterface $output): int
90    {
91        try {
92            $path = $this->filesystem->getAbsolutePath($input->getOption('file'));
93            $currentVersion = $input->getOption('current-version');
94            $nextVersion = $this->changelogManager->inferNextVersion($path, $currentVersion);
95
96            // This command is consumed via shell capture in changelog.yml, so
97            // plain-text mode MUST keep emitting the raw version string.
98            if (! $this->isJsonOutput($input)) {
99                $output->writeln($nextVersion);
100
101                return self::SUCCESS;
102            }
103
104            return $this->success(
105                $nextVersion,
106                $input,
107                [
108                    'current_version' => $currentVersion,
109                    'next_version' => $nextVersion,
110                ],
111            );
112        } catch (Throwable $throwable) {
113            return $this->failure(
114                'Unable to infer the next changelog version.',
115                $input,
116                [
117                    'exception_message' => $throwable->getMessage(),
118                ],
119                (string) $input->getOption('file'),
120            );
121        }
122    }
123}