Build::execute()   F
last analyzed

Complexity

Conditions 22
Paths > 20000

Size

Total Lines 108
Code Lines 70

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 22
eloc 70
nc 73728
nop 2
dl 0
loc 108
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file is part of Cecil.
5
 *
6
 * (c) Arnaud Ligny <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Cecil\Command;
15
16
use Cecil\Util;
17
use Joli\JoliNotif\DefaultNotifier;
18
use Symfony\Component\Console\Helper\Table;
19
use Symfony\Component\Console\Input\InputArgument;
20
use Symfony\Component\Console\Input\InputInterface;
21
use Symfony\Component\Console\Input\InputOption;
22
use Symfony\Component\Console\Output\OutputInterface;
23
24
/**
25
 * Build command.
26
 *
27
 * This command generates the website in the output directory.
28
 * It can include drafts, optimize generated files, and perform a dry run.
29
 * It also allows building a specific page or a subset of pages, clearing the cache, and showing build metrics.
30
 */
31
class Build extends AbstractCommand
32
{
33
    /**
34
     * {@inheritdoc}
35
     */
36
    protected function configure()
37
    {
38
        $this
39
            ->setName('build')
40
            ->setDescription('Builds the website')
41
            ->setDefinition([
42
                new InputArgument('path', InputArgument::OPTIONAL, 'Use the given path as working directory'),
43
                new InputOption('drafts', 'd', InputOption::VALUE_NONE, 'Include drafts'),
44
                new InputOption('baseurl', 'u', InputOption::VALUE_REQUIRED, 'Set the base URL'),
45
                new InputOption('output', 'o', InputOption::VALUE_REQUIRED, 'Set the output directory'),
46
                new InputOption('optimize', null, InputOption::VALUE_NEGATABLE, 'Enable (or disable --no-optimize) optimization of generated files'),
47
                new InputOption('dry-run', null, InputOption::VALUE_NONE, 'Build without saving'),
48
                new InputOption('config', 'c', InputOption::VALUE_REQUIRED, 'Set the path to extra config files (comma-separated)'),
49
                new InputOption('clear-cache', null, InputOption::VALUE_OPTIONAL, 'Clear cache before build (optional cache key as regular expression)', false),
50
                new InputOption('page', 'p', InputOption::VALUE_REQUIRED, 'Build a specific page'),
51
                new InputOption('render-subset', null, InputOption::VALUE_REQUIRED, 'Render a subset of pages'),
52
                new InputOption('show-pages', null, InputOption::VALUE_NONE, 'Show list of built pages in a table'),
53
                new InputOption('metrics', 'm', InputOption::VALUE_NONE, 'Show build metrics (duration and memory) of each step'),
54
                new InputOption('notif', null, InputOption::VALUE_NONE, 'Send desktop notification on build completion'),
55
            ])
56
            ->setHelp(
57
                <<<'EOF'
58
The <info>%command.name%</> command generates the website in the <comment>output</comment> directory.
59
60
  <info>%command.full_name%</>
61
  <info>%command.full_name% path/to/the/working/directory</>
62
  <info>%command.full_name% --baseurl=https://example.com/</>
63
  <info>%command.full_name% --output=_site</>
64
65
To build the website with <comment>optimization</comment> of generated files, you can use the <info>--optimize</info> option.
66
This is useful to reduce the size of the generated files and <comment>improve performance</comment>:
67
68
  <info>%command.full_name% --optimize</>
69
  <info>%command.full_name% --no-optimize</>
70
71
To build the website <comment>without overwriting files in the output</comment> directory, you can use the <info>--dry-run</info> option.
72
This is useful to check what would be built without actually writing files:
73
74
  <info>%command.full_name% --dry-run</>
75
76
To build the website with a specific subset of rendered pages, you can use the <info>--render-subset</info> option.
77
This is useful to <comment>build only a part of the website</comment>, for example, only "hot" pages or a specific section:
78
79
  <info>%command.full_name% --render-subset=subset</>
80
81
To show build steps <comment>metrics</comment>, run:
82
83
  <info>%command.full_name% --metrics</>
84
85
Send a desktop <comment>notification</comment> on build completion, run:
86
87
  <info>%command.full_name% --notif</>
88
EOF
89
            );
90
    }
91
92
    /**
93
     * {@inheritdoc}
94
     */
95
    protected function execute(InputInterface $input, OutputInterface $output): int
96
    {
97
        $config = [];
98
        $options = [];
99
        $messageOpt = '';
100
101
        if ($input->getOption('baseurl')) {
102
            $config['baseurl'] = $input->getOption('baseurl');
103
        }
104
        if ($input->getOption('output')) {
105
            $config['output']['dir'] = $input->getOption('output');
106
            if ($input->getOption('output') != self::SERVE_OUTPUT) {
107
                Util\File::getFS()->dumpFile(Util::joinFile($this->getPath(), self::TMP_DIR, 'output'), (string) $input->getOption('output'));
108
            }
109
        }
110
        if ($input->getOption('optimize') === true) {
111
            $config['optimize']['enabled'] = true;
112
        }
113
        if ($input->getOption('optimize') === false) {
114
            $config['optimize']['enabled'] = false;
115
        }
116
        if ($input->getOption('clear-cache') === null) {
117
            $config['cache']['enabled'] = false;
118
        }
119
120
        $builder = $this->getBuilder($config);
121
122
        if ($input->getOption('drafts')) {
123
            $options['drafts'] = true;
124
            $messageOpt .= ' with drafts';
125
        }
126
        if ($input->getOption('dry-run')) {
127
            $options['dry-run'] = true;
128
            $messageOpt .= ' (dry-run)';
129
        }
130
        if ($input->getOption('page')) {
131
            $options['page'] = $input->getOption('page');
132
        }
133
        if ($input->getOption('render-subset')) {
134
            $options['render-subset'] = (string) $input->getOption('render-subset');
135
        }
136
        if ($input->getOption('clear-cache')) {
137
            if (0 < $removedFiles = (new \Cecil\Cache($this->getBuilder()))->clearByPattern((string) $input->getOption('clear-cache'))) {
138
                $output->writeln(\sprintf('<info>%s cache files removed by regular expression "%s"</info>', $removedFiles, $input->getOption('clear-cache')));
139
            }
140
        }
141
142
        $output->writeln(\sprintf('Building website%s...', $messageOpt));
143
        $output->writeln(\sprintf('<comment>Path:   %s</comment>', $this->getPath()), OutputInterface::VERBOSITY_VERY_VERBOSE);
144
        if (!empty($this->getConfigFiles())) {
145
            $output->writeln(\sprintf('<comment>Config: %s</comment>', implode(', ', $this->getConfigFiles())), OutputInterface::VERBOSITY_VERY_VERBOSE);
146
        }
147
        $output->writeln(\sprintf('<comment>Output: %s</comment>', $this->getBuilder()->getConfig()->getOutputPath()), OutputInterface::VERBOSITY_VERY_VERBOSE);
148
        if ($builder->getConfig()->isEnabled('cache') !== false) {
149
            $output->writeln(\sprintf('<comment>Cache:  %s</comment>', $builder->getConfig()->getCachePath()), OutputInterface::VERBOSITY_VERY_VERBOSE);
150
        }
151
152
        // build
153
        $builder->build($options);
154
        $output->writeln('Build done 🎉');
155
156
        // notification
157
        if ($input->getOption('notif')) {
158
            $notifier = new DefaultNotifier();
159
            $this->notification->setBody('Build done 🎉');
160
            $notifier->send($this->notification);
161
        }
162
163
        // show build steps metrics
164
        if ($input->getOption('metrics')) {
165
            $table = new Table($output);
166
            $table
167
                ->setHeaderTitle('Build steps metrics')
168
                ->setHeaders(['Step', 'Duration', 'Memory'])
169
                ->setRows($builder->getMetrics()['steps'])
170
            ;
171
            $table->setStyle('box')->render();
172
        }
173
174
        // show built pages as table
175
        if ($input->getOption('show-pages')) {
176
            $pagesAsArray = [];
177
            foreach (
178
                $this->getBuilder()->getPages()->filter(function (\Cecil\Collection\Page\Page $page) {
179
                    return $page->getVariable('published');
180
                })->usort(function (\Cecil\Collection\Page\Page $pageA, \Cecil\Collection\Page\Page $pageB) {
181
                    return strnatcmp((string) $pageA['language'], (string) $pageB['language']);
182
                }) as $page
183
            ) {
184
                /** @var \Cecil\Collection\Page\Page $page */
185
                $pagesAsArray[] = [
186
                    $page->getId(),
187
                    $page->getVariable('language'),
188
                    \sprintf("%s %s", $page->getType(), $page->getType() !== \Cecil\Collection\Page\Type::PAGE->value ? "(" . \count($page->getPages() ?: []) . ")" : ''),
189
                    $page->getSection(),
190
                    $page->isVirtual() ? 'true' : 'false',
191
                ];
192
            }
193
            $table = new Table($output);
194
            $table
195
                ->setHeaderTitle(\sprintf("Built pages (%s)", \count($pagesAsArray)))
196
                ->setHeaders(['ID', 'Lang', 'Type', 'Section', 'Virtual'])
197
                ->setRows($pagesAsArray)
198
            ;
199
            $table->setStyle('box')->render();
200
        }
201
202
        return 0;
203
    }
204
}
205