Completed
Push — master ( 830b42...366775 )
by Jonathan
10s
created

GenerateChangelogCommand::getChangelogConfig()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 12
nc 2
nop 1
dl 0
loc 18
ccs 13
cts 13
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ChangelogGenerator\Command;
6
7
use ChangelogGenerator\ChangelogConfig;
8
use ChangelogGenerator\ChangelogGenerator;
9
use InvalidArgumentException;
10
use Symfony\Component\Console\Command\Command;
11
use Symfony\Component\Console\Input\InputInterface;
12
use Symfony\Component\Console\Input\InputOption;
13
use Symfony\Component\Console\Output\OutputInterface;
14
use Symfony\Component\Console\Output\StreamOutput;
15
use function count;
16
use function current;
17
use function file_exists;
18
use function fopen;
19
use function getcwd;
20
use function is_array;
21
use function sprintf;
22
23
class GenerateChangelogCommand extends Command
24
{
25
    /** @var ChangelogGenerator */
26
    private $changelogGenerator;
27
28 13
    public function __construct(ChangelogGenerator $changelogGenerator)
29
    {
30 13
        $this->changelogGenerator = $changelogGenerator;
31
32 13
        parent::__construct();
33 13
    }
34
35 13
    protected function configure() : void
36
    {
37
        $this
38 13
            ->setName('generate')
39 13
            ->setDescription('Generate a changelog markdown document from a GitHub milestone.')
40 13
            ->setHelp(<<<EOT
41 13
The <info>%command.name%</info> command generates a changelog markdown document from a GitHub milestone:
42
43
    <info>%command.full_name% --user=doctrine --repository=migrations --milestone=2.0</info>
44
45
You can filter the changelog by label names using the --label option:
46
47
    <info>%command.full_name% --user=doctrine --repository=migrations --milestone=2.0 --label=Enhancement --label=Bug</info>
48
EOT
49
            )
50 13
            ->addOption(
51 13
                'user',
52 13
                null,
53 13
                InputOption::VALUE_REQUIRED,
54 13
                'User that owns the repository.'
55
            )
56 13
            ->addOption(
57 13
                'repository',
58 13
                null,
59 13
                InputOption::VALUE_REQUIRED,
60 13
                'The repository owned by the user.'
61
            )
62 13
            ->addOption(
63 13
                'milestone',
64 13
                null,
65 13
                InputOption::VALUE_REQUIRED,
66 13
                'The milestone to build the changelog for.'
67
            )
68 13
            ->addOption(
69 13
                'file',
70 13
                null,
71 13
                InputOption::VALUE_OPTIONAL,
72 13
                'Write the changelog to a file.',
73 13
                false
74
            )
75 13
            ->addOption(
76 13
                'append',
77 13
                null,
78 13
                InputOption::VALUE_NONE,
79 13
                'Append the changelog to the file.'
80
            )
81 13
            ->addOption(
82 13
                'label',
83 13
                null,
84 13
                InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
85 13
                'The labels to generate a changelog for.'
86
            )
87 13
            ->addOption(
88 13
                'config',
89 13
                'c',
90 13
                InputOption::VALUE_REQUIRED,
91 13
                'The path to a configuration file.'
92
            )
93 13
            ->addOption(
94 13
                'project',
95 13
                'p',
96 13
                InputOption::VALUE_REQUIRED,
97 13
                'The project from the configuration to generate a changelog for.'
98
            )
99
        ;
100 13
    }
101
102 11
    protected function execute(InputInterface $input, OutputInterface $output) : void
103
    {
104 11
        $this->changelogGenerator->generate(
105 11
            $this->getChangelogConfig($input),
106 8
            $this->getChangelogOutput($input, $output)
107
        );
108 8
    }
109
110 11
    private function getChangelogConfig(InputInterface $input) : ChangelogConfig
111
    {
112 11
        $user       = $input->getOption('user');
113 11
        $repository = $input->getOption('repository');
114 11
        $milestone  = $input->getOption('milestone');
115 11
        $labels     = $input->getOption('label');
116
117 11
        $changelogConfig = $this->loadConfigFile($input);
118
119 8
        if ($changelogConfig !== null) {
120 3
            return $changelogConfig;
121
        }
122
123 5
        return new ChangelogConfig(
124 5
            $user,
125 5
            $repository,
126 5
            $milestone,
127 5
            $labels
128
        );
129
    }
130
131
    /**
132
     * @return false|resource
133
     */
134 1
    protected function fopen(string $file, string $mode)
135
    {
136 1
        return fopen($file, $mode);
137
    }
138
139
    /**
140
     * @throws InvalidArgumentException
141
     */
142 2
    protected function createStreamOutput(string $file, bool $append) : StreamOutput
143
    {
144 2
        $handle = $this->fopen($file, $this->getFileHandleMode($append));
145
146 2
        if ($handle === false) {
147 1
            throw new InvalidArgumentException(sprintf('Could not open handle for %s', $file));
148
        }
149
150 1
        return new StreamOutput($handle);
151
    }
152
153 2
    private function getFileHandleMode(bool $append) : string
154
    {
155 2
        return $append ? 'a+' : 'w+';
156
    }
157
158 8
    private function getChangelogOutput(InputInterface $input, OutputInterface $output) : OutputInterface
159
    {
160 8
        $file   = $input->getOption('file');
161 8
        $append = (bool) $input->getOption('append');
162
163 8
        $changelogOutput = $output;
164
165 8
        if ($file !== false) {
166 3
            $changelogOutput = $this->createStreamOutput($this->getChangelogFilePath($file), $append);
167
        }
168
169 8
        return $changelogOutput;
170
    }
171
172 3
    private function getChangelogFilePath(?string $file) : string
173
    {
174 3
        return $file === null ? sprintf('%s/CHANGELOG.md', getcwd()) : $file;
175
    }
176
177
    /**
178
     * @throws InvalidArgumentException
179
     */
180 11
    private function loadConfigFile(InputInterface $input) : ?ChangelogConfig
181
    {
182 11
        $config = $input->getOption('config');
183
184 11
        if ($config === null) {
185 5
            return null;
186
        }
187
188 6
        if (! file_exists($config)) {
189 1
            throw new InvalidArgumentException(sprintf('Configuration file "%s" does not exist.', $config));
190
        }
191
192 5
        $changelogConfigs = include $config;
193
194 5
        if (! is_array($changelogConfigs) || count($changelogConfigs) === 0) {
195 1
            throw new InvalidArgumentException(sprintf('Configuration file "%s" did not return anything.', $config));
196
        }
197
198 4
        $changelogConfig = $this->findChangelogConfig($input, $changelogConfigs);
199
200 3
        $this->overrideChangelogConfig($input, $changelogConfig);
201
202 3
        return $changelogConfig;
203
    }
204
205
    /**
206
     * @param ChangelogConfig[] $changelogConfigs
207
     */
208 4
    private function findChangelogConfig(InputInterface $input, array $changelogConfigs) : ChangelogConfig
209
    {
210 4
        $project = $input->getOption('project');
211
212 4
        $changelogConfig = current($changelogConfigs);
213
214 4
        if ($project !== null) {
215 4
            if (! isset($changelogConfigs[$project])) {
216 1
                throw new InvalidArgumentException(sprintf('Could not find project named "%s" configured', $project));
217
            }
218
219 3
            $changelogConfig = $changelogConfigs[$project];
220
        }
221
222 3
        return $changelogConfig;
223
    }
224
225 3
    private function overrideChangelogConfig(InputInterface $input, ChangelogConfig $changelogConfig) : void
226
    {
227 3
        $user       = $input->getOption('user');
228 3
        $repository = $input->getOption('repository');
229 3
        $milestone  = $input->getOption('milestone');
230 3
        $labels     = $input->getOption('label');
231
232 3
        if ($user !== null) {
233 3
            $changelogConfig->setUser($user);
234
        }
235
236 3
        if ($repository !== null) {
237 3
            $changelogConfig->setRepository($repository);
238
        }
239
240 3
        if ($milestone !== null) {
241 3
            $changelogConfig->setMilestone($milestone);
242
        }
243
244 3
        if ($labels === []) {
245 1
            return;
246
        }
247
248 2
        $changelogConfig->setLabels($labels);
249 2
    }
250
}
251