Completed
Push — master ( ae5fca...d9758b )
by Julien
13s
created

AentHelper::setEnvType()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
4
namespace TheAentMachine\Helper;
5
6
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
7
use Symfony\Component\Console\Helper\FormatterHelper;
8
use Symfony\Component\Console\Helper\QuestionHelper;
9
use Symfony\Component\Console\Input\InputInterface;
10
use Symfony\Component\Console\Output\OutputInterface;
11
use Symfony\Component\Console\Question\Question as SymfonyQuestion;
12
use TheAentMachine\Exception\ManifestException;
13
use TheAentMachine\Exception\MissingEnvironmentVariableException;
14
use TheAentMachine\Aenthill\Manifest;
15
use TheAentMachine\Aenthill\Metadata;
16
use TheAentMachine\Registry\RegistryClient;
17
use TheAentMachine\Registry\TagsAnalyzer;
18
19
/**
20
 * A helper class for the most common questions asked in the console.
21
 */
22
class AentHelper
23
{
24
    /** @var InputInterface */
25
    private $input;
26
27
    /** @var OutputInterface */
28
    private $output;
29
30
    /** @var QuestionHelper */
31
    private $questionHelper;
32
33
    /** @var FormatterHelper */
34
    private $formatterHelper;
35
36
    public function __construct(InputInterface $input, OutputInterface $output, QuestionHelper $questionHelper, FormatterHelper $formatterHelper)
37
    {
38
        $this->input = $input;
39
        $this->output = $output;
40
        $this->questionHelper = $questionHelper;
41
        $this->formatterHelper = $formatterHelper;
42
    }
43
44
    private function registerStyle(): void
45
    {
46
        $outputStyle = new OutputFormatterStyle('black', 'cyan', ['bold']);
47
        $this->output->getFormatter()->setStyle('title', $outputStyle);
48
    }
49
50
    /**
51
     * Displays text in a big block
52
     */
53
    public function title(string $title): void
54
    {
55
        $this->registerStyle();
56
        $this->output->writeln($this->formatterHelper->formatBlock($title, 'title', true));
57
    }
58
59
    /**
60
     * Displays text in a small block
61
     */
62
    public function subTitle(string $title): void
63
    {
64
        $this->registerStyle();
65
        $this->output->writeln($this->formatterHelper->formatBlock($title, 'title', false));
66
    }
67
68
    public function askForServiceName(string $serviceName, string $applicationName = ''): string
69
    {
70
        $answer = $this->question("$applicationName service name")
71
            ->setDefault($serviceName)
72
            ->compulsory()
73
            ->setHelpText('The "service name" is used as an identifier for the container you are creating. It is also bound in Docker internal network DNS and can be used from other containers to reference your container.')
74
            ->setValidator(function (string $value) {
75
                $value = trim($value);
76
                if (!\preg_match('/^[a-zA-Z0-9_.-]+$/', $value)) {
77
                    throw new \InvalidArgumentException('Invalid service name "' . $value . '". Service names can contain alphanumeric characters, and "_", ".", "-".');
78
                }
79
                return $value;
80
            })
81
            ->ask();
82
83
        $this->output->writeln("<info>Service name: $answer</info>");
84
        $this->spacer();
85
86
        return $answer;
87
    }
88
89
    public function spacer(): void
90
    {
91
        $this->output->writeln('');
92
    }
93
94
    public function askForTag(string $dockerHubImage, string $applicationName = ''): string
95
    {
96
        $registryClient = new RegistryClient();
97
        $availableVersions = $registryClient->getImageTagsOnDockerHub($dockerHubImage);
98
99
        $tagsAnalyzer = new TagsAnalyzer();
100
        $proposedTags = $tagsAnalyzer->filterBestTags($availableVersions);
101
        $default = $proposedTags[0] ?? null;
102
        $this->output->writeln("Please choose your $applicationName version.");
103
        if (!empty($proposedTags)) {
104
            $this->output->writeln('Possible values include: <info>' . \implode('</info>, <info>', $proposedTags) . '</info>');
105
        }
106
        $this->output->writeln('Enter "v" to view all available versions, "?" for help');
107
        $question = new SymfonyQuestion(
108
            "Select your $applicationName version [$default]: ",
109
            $default
110
        );
111
        $question->setAutocompleterValues($availableVersions);
112
        $question->setValidator(function (string $value) use ($availableVersions, $dockerHubImage) {
113
            $value = trim($value);
114
115
            if ($value === 'v') {
116
                $this->output->writeln('Available versions: <info>' . \implode('</info>, <info>', $availableVersions) . '</info>');
117
                return 'v';
118
            }
119
120
            if ($value === '?') {
121
                $this->output->writeln("Please choose the version (i.e. the tag) of the $dockerHubImage image you are about to install. Press 'v' to view the list of available tags.");
122
                return '?';
123
            }
124
125
            if (!\in_array($value, $availableVersions)) {
126
                throw new \InvalidArgumentException("Version '$value' is invalid.");
127
            }
128
129
            return $value;
130
        });
131
        do {
132
            $version = $this->questionHelper->ask($this->input, $this->output, $question);
133
        } while ($version === 'v' || $version === '?');
134
135
        $this->output->writeln("<info>Selected version: $version</info>");
136
        $this->spacer();
137
138
        return $version;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $version could return the type null|boolean which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
139
    }
140
141
    public function question(string $question): Question
142
    {
143
        return new Question($this->questionHelper, $this->input, $this->output, $question);
144
    }
145
146
    /**
147
     * @param string[] $choices
148
     * @return ChoiceQuestion
149
     */
150
    public function choiceQuestion(string $question, array $choices): ChoiceQuestion
151
    {
152
        return new ChoiceQuestion($this->questionHelper, $this->input, $this->output, $question, $choices);
153
    }
154
155
    public function setEnvType(): string
156
    {
157
        $envType = $this->choiceQuestion('Select your environment type', [Metadata::ENV_TYPE_DEV, Metadata::ENV_TYPE_TEST, Metadata::ENV_TYPE_PROD])
158
            ->askSingleChoiceQuestion();
159
        $this->output->writeln("<info>Selected environment type: $envType</info>");
160
        $this->spacer();
161
162
        Manifest::addMetadata(Metadata::ENV_TYPE_KEY, $envType);
163
        return $envType;
164
    }
165
166
    /**
167
     * @return string
168
     * @throws MissingEnvironmentVariableException
169
     * @throws ManifestException
170
     */
171
    public function registerCI(): string
172
    {
173
        $ci = $this->choiceQuestion('Select your CI/CD:', ['gitlab-ci', 'travis-ci', 'circle-ci'])
174
            ->askSingleChoiceQuestion();
175
        $this->output->writeln("<info>Your CI: $ci</info>");
176
        $this->spacer();
177
178
        Manifest::addDependency("theaentmachine/aent-$ci", Metadata::CI_KEY, [
179
            Metadata::ENV_NAME_KEY => Manifest::getMetadata(Metadata::ENV_NAME_KEY),
180
            Metadata::ENV_TYPE_KEY => Manifest::getMetadata(Metadata::ENV_TYPE_KEY)
181
        ]);
182
183
        return Manifest::getDependency(Metadata::CI_KEY);
184
    }
185
186
    /**
187
     * @return string
188
     * @throws MissingEnvironmentVariableException
189
     * @throws ManifestException
190
     */
191
    public function registerReverseProxy(): string
192
    {
193
        $reverseProxy = $this->choiceQuestion('Select your reverse proxy:', ['traefik', 'nginx', 'ingress'])
194
            ->askSingleChoiceQuestion();
195
        $this->output->writeln("<info>Your reverse proxy: $reverseProxy</info>");
196
        $this->spacer();
197
198
        Manifest::addDependency("theaentmachine/aent-$reverseProxy", Metadata::REVERSE_PROXY_KEY, [
199
            Metadata::ENV_NAME_KEY => Manifest::getMetadata(Metadata::ENV_NAME_KEY),
200
            Metadata::ENV_TYPE_KEY => Manifest::getMetadata(Metadata::ENV_TYPE_KEY)
201
        ]);
202
203
        return Manifest::getDependency(Metadata::REVERSE_PROXY_KEY);
204
    }
205
}
206