WorkflowCommand::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 4
rs 10
1
<?php declare(strict_types=1);
2
3
/*
4
 * This file is part of Biurad opensource projects.
5
 *
6
 * @copyright 2022 Biurad Group (https://biurad.com/)
7
 * @license   https://opensource.org/licenses/BSD-3-Clause License
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
namespace Biurad\Monorepo;
14
15
use Symfony\Component\Console\Command\Command;
16
use Symfony\Component\Console\Input\{InputArgument, InputInterface, InputOption};
17
use Symfony\Component\Console\Output\OutputInterface;
18
use Symfony\Component\Console\Style\SymfonyStyle;
19
20
/**
21
 * The Monorepo's workflow command.
22
 *
23
 * @author Divine Niiquaye Ibok <[email protected]>
24
 */
25
class WorkflowCommand extends Command
26
{
27
    private Monorepo $monorepo;
28
    private SymfonyStyle $output;
29
30
    /** @var array<int,WorkerInterface> */
31
    private array $workers = [];
32
33
    public function __construct(private string $rootPath)
34
    {
35
        parent::__construct('workflow');
36
        $this->ignoreValidationErrors();
37
    }
38
39
    /**
40
     * {@inheritdoc}
41
     */
42
    public function execute(InputInterface $input, OutputInterface $output): int
43
    {
44
        if (0 === $totalWorkerCount = \count($jobWorkers = $this->workers)) {
45
            return self::FAILURE;
46
        }
47
48
        $isDryRun = (bool) $input->getOption('dry-run');
49
        $stage = $input->getArgument('job');
50
        $status = self::SUCCESS;
51
52
        foreach ($jobWorkers as $i => $jobWorker) {
53
            $this->output->newLine();
54
            $this->output->title(\sprintf('%s/%d) ', ++$i, $totalWorkerCount).$jobWorker->getDescription());
55
56
            if ($this->output->isVerbose()) {
57
                $output->writeln(\sprintf('class: %s; stage: %s;', $jobWorker::class, $stage));
58
                $this->output->newLine();
59
            }
60
61
            if (!$isDryRun && self::SUCCESS !== $status = $jobWorker->work($this->monorepo, $input, $this->output)) {
62
                break;
63
            }
64
        }
65
66
        if (self::SUCCESS !== $status) {
67
            $this->output->error(\sprintf('Workflow job "%s" failed with exit code %d', $stage, $status));
68
        } elseif ($isDryRun) {
69
            $this->output->note(\sprintf('Workflow job "%s" running in dry mode, nothing is changed', $stage));
70
        } else {
71
            $this->output->success(\sprintf('Workflow job "%s" is now completed and successful!', $stage));
72
        }
73
74
        return $status;
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    protected function configure(): void
81
    {
82
        $this->setDescription('Workflow job runner for working the monorepo repositories');
83
        $this->addArgument('job', InputArgument::OPTIONAL, 'The specified workflow job to run', 'main');
84
        $this->addOption('force', 'f', InputOption::VALUE_NONE, 'Force the workflow job to run');
85
        $this->addOption('no-push', null, InputOption::VALUE_NONE, 'Do not push to the remote repositories.');
86
        $this->addOption('dry-run', 'd', InputOption::VALUE_NONE, 'Do not perform operations, just their preview');
87
        $this->addOption('path', 'p', InputOption::VALUE_OPTIONAL, 'Absolute path to project\'s directory, defaults to directory were script is called.');
88
        $this->addOption('cache', 'c', InputOption::VALUE_OPTIONAL, 'Absolute path to cache directory, defaults to .monorepo-cache in the project directory.');
89
        $this->addOption('clean', 'x', InputOption::VALUE_NONE, 'Re-clone cached repositories of monorepo\'s sub-repo');
90
        $this->addOption('only', 'o', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_OPTIONAL, 'List of sub-repos specified by repo name', []);
91
    }
92
93
    /**
94
     * {@inheritdoc}
95
     */
96
    protected function initialize(InputInterface $input, OutputInterface $output): void
97
    {
98
        $repositories = [];
99
        $this->output = new SymfonyStyle($input, $output);
100
        $config = new Config(\rtrim($input->getOption('path') ?? $this->rootPath, '\/'), $input->getOption('cache'), $input->getOption('clean'));
101
        $exclusive = $input->getOption('only');
102
103
        if (empty($jobWorkers = $config['workers'][$stage = $input->getArgument('job')] ?? [])) {
104
            $this->output->error(\sprintf('There are workflow workers registered. Be sure to add them to "%s"', $stage));
105
106
            return;
107
        }
108
109
        foreach ($jobWorkers as $offset => $worker) {
110
            $worker = $worker::configure($this);
111
112
            if ($worker instanceof PriorityInterface && $offset !== $worker->getPriority()) {
113
                $this->output->error(\sprintf(
114
                    'The "%s" worker must indexed "%s" and not "%s" in stage "%s"',
115
                    $worker::class,
116
                    $worker->getPriority(),
117
                    $offset,
118
                    $stage
119
                ));
120
                break;
121
            }
122
123
            $this->workers[] = $worker;
124
        }
125
126
        foreach ($config['repositories'] as $repoName => $data) {
127
            if ($exclusive && !\in_array($repoName, $exclusive, true)) {
128
                continue;
129
            }
130
131
            if (\in_array($stage, $data['workers'] ?? ['main'], true)) {
132
                $repositories[] = [$data['url'], $repoName, $data['path'], $data['merge'] ? 'true' : 'false'];
133
            }
134
        }
135
136
        $this->output->table(['Repo Url', 'Repo Name', 'Repo Path', 'Allow Merge'], $repositories);
137
        $this->monorepo = new Monorepo($config, $output->getVerbosity(), $repositories);
138
        $input->bind($this->getDefinition());
139
    }
140
}
141