Skip to content

Commit cd52bc6

Browse files
committed
Converted info, login, logout, reset and update-vortex to the PHP scripts.
1 parent 4fd611d commit cd52bc6

21 files changed

Lines changed: 2390 additions & 37 deletions

.vortex/tooling/composer.json

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,20 @@
1616
"issues": "https://github.com/drevops/vortex/issues",
1717
"source": "https://github.com/drevops/vortex"
1818
},
19+
"require": {
20+
"php": ">=8.2"
21+
},
1922
"require-dev": {
20-
"php": ">=8.2",
2123
"alexskrypnyk/file": "^0.15",
2224
"alexskrypnyk/phpunit-helpers": "^0.14.0",
2325
"dealerdirect/phpcodesniffer-composer-installer": "^1.2.0",
2426
"drevops/phpcs-standard": "^0.5",
2527
"drupal/coder": "^9@alpha",
26-
"ergebnis/composer-normalize": "^2.48.2",
27-
"php-mock/php-mock-phpunit": "^2.14",
28-
"phpstan/phpstan": "^2.1.33",
29-
"phpunit/phpunit": "^12.5.3",
30-
"rector/rector": "^2.2.14",
28+
"ergebnis/composer-normalize": "^2.50.0",
29+
"php-mock/php-mock-phpunit": "^2.15",
30+
"phpstan/phpstan": "^2.1.39",
31+
"phpunit/phpunit": "^12.5.14",
32+
"rector/rector": "^2.3.7",
3133
"squizlabs/php_codesniffer": "^4.0.1"
3234
},
3335
"minimum-stability": "alpha",
@@ -46,7 +48,11 @@
4648
"allow-plugins": {
4749
"dealerdirect/phpcodesniffer-composer-installer": true,
4850
"ergebnis/composer-normalize": true
49-
}
51+
},
52+
"bump-after-update": true,
53+
"discard-changes": true,
54+
"process-timeout": 0,
55+
"sort-packages": true
5056
},
5157
"scripts": {
5258
"check-no-exit": "php check-no-exit.php",

.vortex/tooling/phpunit.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,7 @@
4343
<logging>
4444
<junit outputFile=".logs/junit.xml"/>
4545
</logging>
46+
<php>
47+
<ini name="memory_limit" value="4G"/>
48+
</php>
4649
</phpunit>

.vortex/tooling/src/deploy-lagoon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ if ($ssh_exit_code !== 0) {
112112
}
113113

114114
// Install Lagoon CLI if needed.
115-
if (!command_exists('lagoon') || !empty($lagooncli_force_install)) {
115+
if (!command_path('lagoon') || !empty($lagooncli_force_install)) {
116116
task('Installing Lagoon CLI.');
117117

118118
$platform = strtolower(php_uname('s'));

.vortex/tooling/src/doctor

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
/**
5+
* @file
6+
* Check project requirements or print system info.
7+
*
8+
* Usage:
9+
* doctor - Check project requirements.
10+
* doctor info - Show system information.
11+
*
12+
* IMPORTANT! This script runs outside the container on the host system.
13+
*/
14+
15+
declare(strict_types=1);
16+
17+
namespace DrevOps\VortexTooling;
18+
19+
require_once __DIR__ . '/helpers.php';
20+
execute_override(basename(__FILE__));
21+
22+
// -----------------------------------------------------------------------------
23+
24+
// Check minimal Doctor requirements.
25+
$check_minimal = getenv_default('VORTEX_DOCTOR_CHECK_MINIMAL', '0');
26+
27+
// Check pre-flight Doctor requirements.
28+
$check_preflight = getenv_default('VORTEX_DOCTOR_CHECK_PREFLIGHT', '0');
29+
30+
if ($check_minimal === '1') {
31+
putenv('VORTEX_DOCTOR_CHECK_PORT=0');
32+
putenv('VORTEX_DOCTOR_CHECK_PYGMY=0');
33+
putenv('VORTEX_DOCTOR_CHECK_SSH=0');
34+
putenv('VORTEX_DOCTOR_CHECK_WEBSERVER=0');
35+
putenv('VORTEX_DOCTOR_CHECK_BOOTSTRAP=0');
36+
}
37+
38+
if ($check_preflight === '1') {
39+
if (getenv('VORTEX_DOCTOR_CHECK_TOOLS') === FALSE) {
40+
putenv('VORTEX_DOCTOR_CHECK_TOOLS=1');
41+
}
42+
if (getenv('VORTEX_DOCTOR_CHECK_PORT') === FALSE) {
43+
putenv('VORTEX_DOCTOR_CHECK_PORT=1');
44+
}
45+
if (getenv('VORTEX_DOCTOR_CHECK_PYGMY') === FALSE) {
46+
putenv('VORTEX_DOCTOR_CHECK_PYGMY=1');
47+
}
48+
if (getenv('VORTEX_DOCTOR_CHECK_CONTAINERS') === FALSE) {
49+
putenv('VORTEX_DOCTOR_CHECK_CONTAINERS=0');
50+
}
51+
if (getenv('VORTEX_DOCTOR_CHECK_SSH') === FALSE) {
52+
putenv('VORTEX_DOCTOR_CHECK_SSH=0');
53+
}
54+
if (getenv('VORTEX_DOCTOR_CHECK_WEBSERVER') === FALSE) {
55+
putenv('VORTEX_DOCTOR_CHECK_WEBSERVER=0');
56+
}
57+
if (getenv('VORTEX_DOCTOR_CHECK_BOOTSTRAP') === FALSE) {
58+
putenv('VORTEX_DOCTOR_CHECK_BOOTSTRAP=0');
59+
}
60+
}
61+
62+
// Check Doctor requirements for presence of tools.
63+
$check_tools = getenv_default('VORTEX_DOCTOR_CHECK_TOOLS', '1');
64+
65+
// Check Doctor requirements for port availability.
66+
$check_port = getenv_default('VORTEX_DOCTOR_CHECK_PORT', '1');
67+
68+
// Check Doctor requirements for Pygmy availability.
69+
$check_pygmy = getenv_default('VORTEX_DOCTOR_CHECK_PYGMY', '1');
70+
71+
// Check Doctor requirements for container status.
72+
$check_containers = getenv_default('VORTEX_DOCTOR_CHECK_CONTAINERS', '1');
73+
74+
// Check Doctor requirements for SSH key.
75+
$check_ssh = getenv_default('VORTEX_DOCTOR_CHECK_SSH', '1');
76+
77+
// Check Doctor requirements for webserver status.
78+
$check_webserver = getenv_default('VORTEX_DOCTOR_CHECK_WEBSERVER', '1');
79+
80+
// Check Doctor requirements for application bootstrap status.
81+
$check_bootstrap = getenv_default('VORTEX_DOCTOR_CHECK_BOOTSTRAP', '1');
82+
83+
// Default SSH key file.
84+
$ssh_file = getenv_default('VORTEX_SSH_FILE', (getenv('HOME') ?: '') . '/.ssh/id_rsa');
85+
86+
// -----------------------------------------------------------------------------
87+
88+
// Handle "info" subcommand.
89+
if (isset($GLOBALS['argv'][1]) && $GLOBALS['argv'][1] === 'info') {
90+
system_info();
91+
quit(0);
92+
}
93+
94+
command_must_exist('docker');
95+
command_must_exist('pygmy');
96+
command_must_exist('ahoy');
97+
98+
info('Checking project requirements');
99+
100+
if ($check_tools === '1') {
101+
if (!command_path('docker')) {
102+
fail('Please install Docker (https://www.docker.com/get-started).');
103+
}
104+
if (!command_path('docker compose')) {
105+
fail('Please install docker compose (https://docs.docker.com/compose/install/).');
106+
}
107+
if (!command_path('pygmy')) {
108+
fail('Please install Pygmy (https://pygmy.readthedocs.io/).');
109+
}
110+
if (!command_path('ahoy')) {
111+
fail('Please install Ahoy (https://ahoy-cli.readthedocs.io/).');
112+
}
113+
pass('All required tools are present.');
114+
}
115+
116+
if ($check_port === '1' && PHP_OS_FAMILY !== 'Linux') {
117+
passthru("lsof -i :80 2>/dev/null | grep -v 'CLOSED' | grep 'LISTEN' | grep -vq 'om.docke'", $exit_code);
118+
if ($exit_code === 0) {
119+
fail("Port 80 is occupied by a service other than Docker. Stop this service and run 'pygmy up'.");
120+
}
121+
pass('Port 80 is available.');
122+
}
123+
124+
if ($check_pygmy === '1') {
125+
passthru('pygmy status 2>/dev/null | tr -d "\\000" > /tmp/vortex_pygmy_status.txt', $exit_code);
126+
127+
$pygmy_services = [
128+
'amazeeio-ssh-agent',
129+
'amazeeio-mailhog',
130+
'amazeeio-haproxy',
131+
'amazeeio-dnsmasq',
132+
];
133+
134+
foreach ($pygmy_services as $service) {
135+
passthru(sprintf('grep -q "%s: Running" /tmp/vortex_pygmy_status.txt', $service), $exit_code);
136+
if ($exit_code !== 0) {
137+
fail("Pygmy service %s is not running. Run 'pygmy up' or 'pygmy restart' to fix.", $service);
138+
}
139+
}
140+
pass('Pygmy is running.');
141+
}
142+
143+
if ($check_containers === '1') {
144+
$container_services = ['cli', 'php', 'nginx', 'database'];
145+
foreach ($container_services as $service) {
146+
passthru(sprintf('docker compose ps --status=running --services 2>/dev/null | grep -q %s', escapeshellarg($service)), $exit_code);
147+
if ($exit_code !== 0) {
148+
fail_no_exit('%s container is not running.', $service);
149+
note("Run 'ahoy up'.");
150+
note("Run 'ahoy logs %s' to see error logs.", $service);
151+
quit(1);
152+
}
153+
}
154+
pass('All containers are running');
155+
}
156+
157+
if ($check_ssh === '1') {
158+
$ssh_key_added_to_pygmy = FALSE;
159+
$ssh_key_volume_mounted = FALSE;
160+
161+
// Check that the key is injected into pygmy ssh-agent container.
162+
passthru(sprintf('pygmy status 2>&1 | grep -q %s', escapeshellarg($ssh_file)), $exit_code);
163+
if ($exit_code !== 0) {
164+
fail_no_exit('SSH key is not added to pygmy.');
165+
note("The SSH key will not be available in CLI container. Run 'pygmy restart' and then 'ahoy up'");
166+
}
167+
else {
168+
$ssh_key_added_to_pygmy = TRUE;
169+
}
170+
171+
// Check that the volume is mounted into CLI container.
172+
passthru('docker compose exec -T cli bash -c \'grep "^/dev" /etc/mtab | grep -q /tmp/amazeeio_ssh-agent\'', $exit_code);
173+
if ($exit_code !== 0) {
174+
fail_no_exit('SSH key volume is not mounted into CLI container.');
175+
note('Make sure that your "docker-compose.yml" has the following lines for CLI service:');
176+
note(' volumes_from:');
177+
note(' - container:amazeeio-ssh-agent');
178+
note("After adding these lines, run 'ahoy up'.");
179+
}
180+
else {
181+
$ssh_key_volume_mounted = TRUE;
182+
}
183+
184+
// Check that ssh key is available in the container.
185+
if ($ssh_key_added_to_pygmy && $ssh_key_volume_mounted) {
186+
passthru("docker compose exec -T cli bash -c \"ssh-add -L | grep -q 'ssh-rsa'\"", $exit_code);
187+
if ($exit_code !== 0) {
188+
fail_no_exit("SSH key was not added into container. Run 'pygmy restart'.");
189+
}
190+
else {
191+
pass('SSH key is available within CLI container.');
192+
}
193+
}
194+
}
195+
196+
if ($check_webserver === '1') {
197+
$local_dev_url = trim((string) shell_exec("docker compose exec -T cli bash -c 'echo \${VORTEX_LOCALDEV_URL}' 2>/dev/null"));
198+
199+
if ($local_dev_url !== '') {
200+
// Depending on the type of installation, the homepage may return 200 or 403.
201+
passthru(sprintf('curl -L -s -o /dev/null -w "%%{http_code}" %s 2>/dev/null | grep -q "200\\|403"', escapeshellarg($local_dev_url)), $exit_code);
202+
if ($exit_code !== 0) {
203+
fail('Web server is not accessible at http://%s.', $local_dev_url);
204+
}
205+
pass('Web server is running and accessible at http://%s.', $local_dev_url);
206+
207+
if ($check_bootstrap === '1') {
208+
passthru(sprintf('curl -L -s -N %s 2>/dev/null | grep -q -i "charset="', escapeshellarg($local_dev_url)), $exit_code);
209+
if ($exit_code !== 0) {
210+
fail('Website is running, but cannot be bootstrapped. Check web-server configuration.');
211+
}
212+
pass('Bootstrapped website at http://%s.', $local_dev_url);
213+
}
214+
}
215+
}
216+
217+
pass('All required checks have passed.');
218+
echo PHP_EOL;
219+
220+
// -----------------------------------------------------------------------------
221+
222+
/**
223+
* Sanitize system information output to remove PII data.
224+
*/
225+
function sanitize_system_info(string $input): string {
226+
$username = trim((string) shell_exec('whoami'));
227+
228+
$input = str_replace('/Users/' . $username . '/', '/Users/[USERNAME_REDACTED]/', $input);
229+
$input = str_replace('/home/' . $username . '/', '/home/[USERNAME_REDACTED]/', $input);
230+
$input = str_replace($username, '[USERNAME_REDACTED]', $input);
231+
$input = (string) preg_replace('/ID: [a-f0-9-]{36}/', 'ID: [REDACTED]', $input);
232+
$input = (string) preg_replace('/[a-f0-9]{40,}/', '[HASH_REDACTED]', $input);
233+
$input = (string) preg_replace('/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/', '[IP_REDACTED]', $input);
234+
235+
return $input;
236+
}
237+
238+
/**
239+
* Print system information.
240+
*/
241+
function system_info(): void {
242+
echo 'System information report' . PHP_EOL;
243+
echo PHP_EOL;
244+
245+
echo 'OPERATING SYSTEM' . PHP_EOL;
246+
if (PHP_OS === 'Darwin') {
247+
passthru('sw_vers');
248+
}
249+
else {
250+
// @codeCoverageIgnoreStart
251+
passthru('lsb_release -a 2>/dev/null');
252+
// @codeCoverageIgnoreEnd
253+
}
254+
echo PHP_EOL;
255+
256+
echo 'DOCKER' . PHP_EOL;
257+
$docker_path = (string) command_path('docker');
258+
echo 'Path to binary: ' . sanitize_system_info($docker_path) . PHP_EOL;
259+
passthru('docker -v');
260+
$docker_info = (string) shell_exec('docker info 2>/dev/null');
261+
if ($docker_info !== '') {
262+
echo sanitize_system_info($docker_info);
263+
}
264+
else {
265+
echo 'Docker is not running or not installed.' . PHP_EOL;
266+
}
267+
echo PHP_EOL;
268+
269+
echo 'DOCKER COMPOSE V2' . PHP_EOL;
270+
echo 'Version: ';
271+
passthru('docker compose version 2>/dev/null || echo "Docker Compose V2 is not installed."');
272+
echo PHP_EOL;
273+
274+
echo 'DOCKER-COMPOSE V1' . PHP_EOL;
275+
$dc_path = (string) command_path('docker-compose');
276+
echo 'Path to binary: ' . sanitize_system_info($dc_path) . PHP_EOL;
277+
echo 'Version: ';
278+
passthru('docker-compose version 2>/dev/null || echo "Docker Compose V1 is not installed."');
279+
echo PHP_EOL;
280+
281+
echo 'PYGMY' . PHP_EOL;
282+
$pygmy_path = (string) command_path('pygmy');
283+
echo 'Path to binary: ' . sanitize_system_info($pygmy_path) . PHP_EOL;
284+
echo 'Version: ';
285+
passthru('pygmy version 2>/dev/null || echo "Pygmy is not installed."');
286+
echo PHP_EOL;
287+
288+
echo 'AHOY' . PHP_EOL;
289+
$ahoy_path = (string) command_path('ahoy');
290+
echo 'Path to binary: ' . sanitize_system_info($ahoy_path) . PHP_EOL;
291+
echo 'Version: ';
292+
passthru('ahoy --version 2>/dev/null || echo "Ahoy is not installed."');
293+
echo PHP_EOL;
294+
}

.vortex/tooling/src/helpers.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -276,18 +276,22 @@ function term_supports_color(): bool {
276276
}
277277

278278
/**
279-
* Check if a command exists in the system.
279+
* Get the path to a command, or FALSE if the command does not exist.
280280
*
281281
* @param string $command
282282
* Command name.
283283
*
284-
* @return bool
285-
* TRUE if command exists, FALSE otherwise.
284+
* @return string|false
285+
* Path to the command, or FALSE if the command does not exist.
286286
*/
287-
function command_exists(string $command): bool {
287+
function command_path(string $command): string|false {
288+
if (!preg_match('/^[A-Za-z0-9_\-]+(?: [A-Za-z0-9_\-]+)*$/', $command)) {
289+
return FALSE;
290+
}
291+
288292
exec(sprintf('command -v %s 2>/dev/null', $command), $output, $code);
289293

290-
return $code === 0;
294+
return $code === 0 && !empty($output[0]) ? trim($output[0]) : FALSE;
291295
}
292296

293297
/**
@@ -297,7 +301,7 @@ function command_exists(string $command): bool {
297301
* Command name.
298302
*/
299303
function command_must_exist(string $command): void {
300-
if (!command_exists($command)) {
304+
if (!command_path($command)) {
301305
fail(sprintf("Command '%s' is not available", $command));
302306
}
303307
}

0 commit comments

Comments
 (0)