feat: implement service environment variable parsing and add unit tests for port detection logic

This commit is contained in:
Andras Bacsai
2025-11-11 11:19:33 +01:00
parent 6d6ebe92ff
commit 7fc4a2f7f6
4 changed files with 274 additions and 38 deletions

View File

@@ -453,13 +453,9 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
// for example SERVICE_FQDN_APP_3000 (without a value)
if ($key->startsWith('SERVICE_FQDN_')) {
// SERVICE_FQDN_APP or SERVICE_FQDN_APP_3000
if (substr_count(str($key)->value(), '_') === 3) {
$fqdnFor = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower()->value();
$port = $key->afterLast('_')->value();
} else {
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
$port = null;
}
$parsed = parseServiceEnvironmentVariable($key->value());
$fqdnFor = $parsed['service_name'];
$port = $parsed['port'];
$fqdn = $resource->fqdn;
if (blank($resource->fqdn)) {
$fqdn = generateFqdn(server: $server, random: "$uuid", parserVersion: $resource->compose_parsing_version);
@@ -482,7 +478,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
$resource->save();
}
if (substr_count(str($key)->value(), '_') === 2) {
if (! $parsed['has_port']) {
$resource->environment_variables()->updateOrCreate([
'key' => $key->value(),
'resourceable_type' => get_class($resource),
@@ -492,7 +488,7 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
'is_preview' => false,
]);
}
if (substr_count(str($key)->value(), '_') === 3) {
if ($parsed['has_port']) {
$newKey = str($key)->beforeLast('_');
$resource->environment_variables()->updateOrCreate([
@@ -565,13 +561,9 @@ function applicationParser(Application $resource, int $pull_request_id = 0, ?int
} elseif ($command->value() === 'URL') {
// SERVICE_URL_APP or SERVICE_URL_APP_3000
// Detect if there's a port suffix
if (substr_count(str($key)->value(), '_') === 3) {
$urlFor = $key->after('SERVICE_URL_')->beforeLast('_')->lower()->value();
$port = $key->afterLast('_')->value();
} else {
$urlFor = $key->after('SERVICE_URL_')->lower()->value();
$port = null;
}
$parsed = parseServiceEnvironmentVariable($key->value());
$urlFor = $parsed['service_name'];
$port = $parsed['port'];
$originalUrlFor = str($urlFor)->replace('_', '-');
if (str($urlFor)->contains('-')) {
$urlFor = str($urlFor)->replace('-', '_')->replace('.', '_');
@@ -1538,27 +1530,16 @@ function serviceParser(Service $resource): Collection
// Get magic environments where we need to preset the FQDN / URL
if ($key->startsWith('SERVICE_FQDN_') || $key->startsWith('SERVICE_URL_')) {
// SERVICE_FQDN_APP or SERVICE_FQDN_APP_3000
if (substr_count(str($key)->value(), '_') === 3) {
if ($key->startsWith('SERVICE_FQDN_')) {
$urlFor = null;
$fqdnFor = $key->after('SERVICE_FQDN_')->beforeLast('_')->lower()->value();
}
if ($key->startsWith('SERVICE_URL_')) {
$fqdnFor = null;
$urlFor = $key->after('SERVICE_URL_')->beforeLast('_')->lower()->value();
}
$port = $key->afterLast('_')->value();
} else {
if ($key->startsWith('SERVICE_FQDN_')) {
$urlFor = null;
$fqdnFor = $key->after('SERVICE_FQDN_')->lower()->value();
}
if ($key->startsWith('SERVICE_URL_')) {
$fqdnFor = null;
$urlFor = $key->after('SERVICE_URL_')->lower()->value();
}
$port = null;
$parsed = parseServiceEnvironmentVariable($key->value());
if ($key->startsWith('SERVICE_FQDN_')) {
$urlFor = null;
$fqdnFor = $parsed['service_name'];
}
if ($key->startsWith('SERVICE_URL_')) {
$fqdnFor = null;
$urlFor = $parsed['service_name'];
}
$port = $parsed['port'];
if (blank($savedService->fqdn)) {
if ($fqdnFor) {
$fqdn = generateFqdn(server: $server, random: "$fqdnFor-$uuid", parserVersion: $resource->compose_parsing_version);
@@ -1603,7 +1584,7 @@ function serviceParser(Service $resource): Collection
}
$savedService->save();
}
if (substr_count(str($key)->value(), '_') === 2) {
if (! $parsed['has_port']) {
$resource->environment_variables()->updateOrCreate([
'key' => $key->value(),
'resourceable_type' => get_class($resource),
@@ -1621,7 +1602,7 @@ function serviceParser(Service $resource): Collection
'is_preview' => false,
]);
}
if (substr_count(str($key)->value(), '_') === 3) {
if ($parsed['has_port']) {
// For port-specific variables (e.g., SERVICE_FQDN_UMAMI_3000),
// keep the port suffix in the key and use the URL with port
$resource->environment_variables()->updateOrCreate([

View File

@@ -184,3 +184,53 @@ function serviceKeys()
{
return get_service_templates()->keys();
}
/**
* Parse a SERVICE_URL_* or SERVICE_FQDN_* variable to extract the service name and port.
*
* This function detects if a service environment variable has a port suffix by checking
* if the last segment after the underscore is numeric.
*
* Examples:
* - SERVICE_URL_APP_3000 ['service_name' => 'app', 'port' => '3000', 'has_port' => true]
* - SERVICE_URL_MY_API_8080 ['service_name' => 'my_api', 'port' => '8080', 'has_port' => true]
* - SERVICE_URL_MY_APP ['service_name' => 'my_app', 'port' => null, 'has_port' => false]
* - SERVICE_FQDN_REDIS_CACHE_6379 ['service_name' => 'redis_cache', 'port' => '6379', 'has_port' => true]
*
* @param string $key The environment variable key (e.g., SERVICE_URL_APP_3000)
* @return array{service_name: string, port: string|null, has_port: bool} Parsed service information
*/
function parseServiceEnvironmentVariable(string $key): array
{
$strKey = str($key);
$lastSegment = $strKey->afterLast('_')->value();
$hasPort = is_numeric($lastSegment) && ctype_digit($lastSegment);
if ($hasPort) {
// Port-specific variable (e.g., SERVICE_URL_APP_3000)
if ($strKey->startsWith('SERVICE_URL_')) {
$serviceName = $strKey->after('SERVICE_URL_')->beforeLast('_')->lower()->value();
} elseif ($strKey->startsWith('SERVICE_FQDN_')) {
$serviceName = $strKey->after('SERVICE_FQDN_')->beforeLast('_')->lower()->value();
} else {
$serviceName = '';
}
$port = $lastSegment;
} else {
// Base variable without port (e.g., SERVICE_URL_APP)
if ($strKey->startsWith('SERVICE_URL_')) {
$serviceName = $strKey->after('SERVICE_URL_')->lower()->value();
} elseif ($strKey->startsWith('SERVICE_FQDN_')) {
$serviceName = $strKey->after('SERVICE_FQDN_')->lower()->value();
} else {
$serviceName = '';
}
$port = null;
}
return [
'service_name' => $serviceName,
'port' => $port,
'has_port' => $hasPort,
];
}