1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace TheAentMachine\Prompt\Helper; |
4
|
|
|
|
5
|
|
|
use Safe\Exceptions\ArrayException; |
6
|
|
|
use Safe\Exceptions\StringsException; |
7
|
|
|
use Symfony\Component\Console\Exception\InvalidArgumentException; |
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; |
12
|
|
|
use TheAentMachine\Aent\Registry\AentItemRegistry; |
13
|
|
|
use TheAentMachine\Aent\Registry\ColonyRegistry; |
14
|
|
|
use TheAentMachine\Aenthill\Pheromone; |
15
|
|
|
use TheAentMachine\Prompt\Input; |
16
|
|
|
use TheAentMachine\Prompt\Select; |
17
|
|
|
use TheAentMachine\Registry\RegistryClient; |
18
|
|
|
use TheAentMachine\Registry\TagsAnalyzer; |
19
|
|
|
use TheAentMachine\Service\Volume\BindVolume; |
20
|
|
|
use function Safe\mkdir; |
21
|
|
|
use function Safe\chown; |
22
|
|
|
use function Safe\chgrp; |
23
|
|
|
use function Safe\sprintf; |
24
|
|
|
|
25
|
|
|
final class PromptHelper |
26
|
|
|
{ |
27
|
|
|
/** @var InputInterface */ |
28
|
|
|
private $input; |
29
|
|
|
|
30
|
|
|
/** @var OutputInterface */ |
31
|
|
|
private $output; |
32
|
|
|
|
33
|
|
|
/** @var QuestionHelper */ |
34
|
|
|
private $questionHelper; |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* PromptHelper constructor. |
38
|
|
|
* @param InputInterface $input |
39
|
|
|
* @param OutputInterface $output |
40
|
|
|
* @param QuestionHelper $questionHelper |
41
|
|
|
*/ |
42
|
|
|
public function __construct(InputInterface $input, OutputInterface $output, QuestionHelper $questionHelper) |
43
|
|
|
{ |
44
|
|
|
$this->input = $input; |
45
|
|
|
$this->output = $output; |
46
|
|
|
$this->questionHelper = $questionHelper; |
47
|
|
|
} |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @param ColonyRegistry $registry |
51
|
|
|
* @param string $text |
52
|
|
|
* @param null|string $helpText |
53
|
|
|
* @return AentItemRegistry |
54
|
|
|
* @throws ArrayException |
55
|
|
|
* @throws StringsException |
56
|
|
|
*/ |
57
|
|
|
public function getFromColonyRegistry(ColonyRegistry $registry, string $text, ?string $helpText = null): AentItemRegistry |
58
|
|
|
{ |
59
|
|
|
$aents = $registry->getAents(); |
60
|
|
|
$assoc = []; |
61
|
|
|
foreach ($aents as $aent) { |
62
|
|
|
$assoc[$aent->getName()] = $aent; |
63
|
|
|
} |
64
|
|
|
$items = \array_keys($assoc); |
65
|
|
|
$items[] = 'Custom'; |
66
|
|
|
$select = new Select($this->input, $this->output, $this->questionHelper); |
67
|
|
|
$select |
68
|
|
|
->setText($text) |
69
|
|
|
->setHelpText($helpText) |
70
|
|
|
->setCompulsory(true); |
71
|
|
|
$select |
72
|
|
|
->setItems($items); |
73
|
|
|
$response = $select->run(); |
74
|
|
|
if ($response === 'Custom') { |
75
|
|
|
$image = $this->getDockerHubImage(); |
76
|
|
|
return new AentItemRegistry($image, $image); |
77
|
|
|
} |
78
|
|
|
return $assoc[$response]; |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @return string |
83
|
|
|
* @throws ArrayException |
84
|
|
|
* @throws StringsException |
85
|
|
|
*/ |
86
|
|
|
public function getDockerHubImage(): string |
87
|
|
|
{ |
88
|
|
|
$dockerHubImageInput = new Input($this->input, $this->output, $this->questionHelper); |
89
|
|
|
$dockerHubImageInput |
90
|
|
|
->setText("\nThe docker image of your custom aent (without tag)") |
91
|
|
|
->setCompulsory(true) |
92
|
|
|
->setValidator(ValidatorHelper::getDockerImageWithoutTagValidator()); |
93
|
|
|
$image = $dockerHubImageInput->run(); |
94
|
|
|
$version = $this->getVersion($image); |
|
|
|
|
95
|
|
|
$aent = $image . ':' . $version; |
96
|
|
|
return $aent; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* @param string $image |
101
|
|
|
* @return string |
102
|
|
|
* @throws ArrayException |
103
|
|
|
* @throws StringsException |
104
|
|
|
*/ |
105
|
|
|
public function getVersion(string $image): string |
106
|
|
|
{ |
107
|
|
|
$registryClient = new RegistryClient(); |
108
|
|
|
$tags = $registryClient->getImageTagsOnDockerHub($image); |
109
|
|
|
$tagsAnalyzer = new TagsAnalyzer(); |
110
|
|
|
$proposedTags = $tagsAnalyzer->filterBestTags($tags); |
111
|
|
|
$default = $proposedTags[0] ?? $tags[0]; |
112
|
|
|
$this->output->writeln("\nLet's choose the version of the <info>$image</info> image!"); |
113
|
|
|
if (!empty($proposedTags)) { |
114
|
|
|
$this->output->writeln('Possible values include: <info>' . \implode('</info>, <info>', $proposedTags) . '</info>'); |
115
|
|
|
} |
116
|
|
|
$this->output->writeln('Enter "v" to view all available versions, "?" for help.'); |
117
|
|
|
$question = new Question("Version [$default]: ", $default); |
118
|
|
|
$question->setAutocompleterValues($tags); |
119
|
|
|
$question->setValidator(function (string $response) use ($tags, $image) { |
120
|
|
|
$response = \trim($response); |
121
|
|
|
if ($response === 'v') { |
122
|
|
|
$this->output->writeln('Available versions: <info>' . \implode('</info>, <info>', $tags) . '</info>'); |
123
|
|
|
return 'v'; |
124
|
|
|
} |
125
|
|
|
if ($response === '?') { |
126
|
|
|
$this->output->writeln("Please choose the version (i.e. the tag) of the <info>$image</info> image you are about to install. Press 'v' to view the list of available tags."); |
127
|
|
|
return '?'; |
128
|
|
|
} |
129
|
|
|
if (!\in_array($response, $tags, true)) { |
130
|
|
|
throw new InvalidArgumentException("Version \"$response\" is invalid."); |
131
|
|
|
} |
132
|
|
|
return $response; |
133
|
|
|
}); |
134
|
|
|
do { |
135
|
|
|
$version = $this->questionHelper->ask($this->input, $this->output, $question); |
136
|
|
|
} while ($version === 'v' || $version === '?'); |
137
|
|
|
$aent = $image . ':' . $version; |
138
|
|
|
$this->output->writeln("\n👌 Alright, I'm going to use <info>$aent</info>!"); |
139
|
|
|
return $version; |
|
|
|
|
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* @return string |
144
|
|
|
*/ |
145
|
|
|
public function getServiceName(): string |
146
|
|
|
{ |
147
|
|
|
$input = new Input($this->input, $this->output, $this->questionHelper); |
148
|
|
|
$text = "\nYour service name"; |
149
|
|
|
$helpText = "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."; |
150
|
|
|
$input |
151
|
|
|
->setText($text) |
152
|
|
|
->setHelpText($helpText) |
153
|
|
|
->setCompulsory(true) |
154
|
|
|
->setValidator(ValidatorHelper::getAlphaWithAdditionalCharactersValidator(['_', '.', '-'])); |
155
|
|
|
$response = $input->run(); |
156
|
|
|
return $response ?? ''; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
/** |
160
|
|
|
* @param string $text |
161
|
|
|
* @param string $target |
162
|
|
|
* @param null|string $helpText |
163
|
|
|
* @return BindVolume |
164
|
|
|
*/ |
165
|
|
|
public function getBindVolume(string $text, string $target, ?string $helpText = null): BindVolume |
166
|
|
|
{ |
167
|
|
|
$input = new Input($this->input, $this->output, $this->questionHelper); |
168
|
|
|
$input |
169
|
|
|
->setText($text) |
170
|
|
|
->setHelpText($helpText) |
171
|
|
|
->setCompulsory(true) |
172
|
|
|
->setValidator(function (string $dir) { |
173
|
|
|
$dir = trim($dir, '/') ?: '.'; |
174
|
|
|
$rootDir = Pheromone::getContainerProjectDirectory(); |
175
|
|
|
$fullDir = $rootDir.'/'.$dir; |
176
|
|
|
if (!file_exists($fullDir)) { |
177
|
|
|
mkdir($fullDir); |
178
|
|
|
$containerProjectDirInfo = new \SplFileInfo(\dirname($fullDir)); |
179
|
|
|
chown($fullDir, $containerProjectDirInfo->getOwner()); |
180
|
|
|
chgrp($fullDir, $containerProjectDirInfo->getGroup()); |
181
|
|
|
} |
182
|
|
|
return $dir; |
183
|
|
|
}); |
184
|
|
|
$source = $input->run() ?? ''; |
185
|
|
|
return new BindVolume($source, $target); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* @param string $serviceName |
190
|
|
|
* @param int $port |
191
|
|
|
* @param string $baseVirtualHost |
192
|
|
|
* @return string |
193
|
|
|
* @throws StringsException |
194
|
|
|
*/ |
195
|
|
|
public function getSubdomain(string $serviceName, int $port, string $baseVirtualHost): string |
196
|
|
|
{ |
197
|
|
|
$input = new Input($this->input, $this->output, $this->questionHelper); |
198
|
|
|
$text = sprintf( |
199
|
|
|
"\nSubdomain for your service <info>%s</info> on port <info>%s</info> (relative to <info>%s</info>)", |
200
|
|
|
$serviceName, |
201
|
|
|
$port, |
202
|
|
|
$baseVirtualHost |
203
|
|
|
); |
204
|
|
|
$input |
205
|
|
|
->setText($text) |
206
|
|
|
->setCompulsory(true) |
207
|
|
|
->setValidator(ValidatorHelper::getAlphaWithAdditionalCharactersValidator(['-'])); |
208
|
|
|
return $input->run() ?? ''; |
209
|
|
|
} |
210
|
|
|
} |
211
|
|
|
|