fix: handle map-style environment variables in updateCompose
The updateCompose() function now correctly detects SERVICE_URL_* and SERVICE_FQDN_* variables regardless of whether they are defined in YAML list-style or map-style format. Previously, the code only worked with list-style environment definitions: ```yaml environment: - SERVICE_URL_APP_3000 ``` Now it also handles map-style definitions: ```yaml environment: SERVICE_URL_TRIGGER_3000: "" SERVICE_FQDN_DB: localhost ``` The fix distinguishes between the two formats by checking if the array key is numeric (list-style) or a string (map-style), then extracts the variable name from the appropriate location. Added 5 comprehensive unit tests covering: - Map-style environment format detection - Multiple map-style variables - References vs declarations in map-style - Abbreviated service names with map-style - Verification of dual-format handling This fixes variable detection for service templates like trigger.yaml, langfuse.yaml, and paymenter.yaml that use map-style format. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
56f32d0f87
commit
a5ce1db871
2 changed files with 172 additions and 3 deletions
|
|
@ -123,16 +123,23 @@ function updateCompose(ServiceApplication|ServiceDatabase $resource)
|
|||
$environment = data_get($serviceConfig, 'environment', []);
|
||||
$templateVariableNames = [];
|
||||
|
||||
foreach ($environment as $envVar) {
|
||||
if (is_string($envVar)) {
|
||||
foreach ($environment as $key => $value) {
|
||||
if (is_int($key) && is_string($value)) {
|
||||
// List-style: "- SERVICE_URL_APP_3000" or "- SERVICE_URL_APP_3000=value"
|
||||
// Extract variable name (before '=' if present)
|
||||
$envVarName = str($envVar)->before('=')->trim();
|
||||
$envVarName = str($value)->before('=')->trim();
|
||||
// Only include if it's a direct declaration (not a reference like ${VAR})
|
||||
// Direct declarations look like: SERVICE_URL_APP or SERVICE_URL_APP_3000
|
||||
// References look like: NEXT_PUBLIC_URL=${SERVICE_URL_APP}
|
||||
if ($envVarName->startsWith('SERVICE_FQDN_') || $envVarName->startsWith('SERVICE_URL_')) {
|
||||
$templateVariableNames[] = $envVarName->value();
|
||||
}
|
||||
} elseif (is_string($key)) {
|
||||
// Map-style: "SERVICE_URL_APP_3000: value" or "SERVICE_FQDN_DB: localhost"
|
||||
$envVarName = str($key);
|
||||
if ($envVarName->startsWith('SERVICE_FQDN_') || $envVarName->startsWith('SERVICE_URL_')) {
|
||||
$templateVariableNames[] = $envVarName->value();
|
||||
}
|
||||
}
|
||||
// DO NOT extract variables that are only referenced with ${VAR_NAME} syntax
|
||||
// Those belong to other services and will be updated when THOSE services are updated
|
||||
|
|
|
|||
|
|
@ -399,3 +399,165 @@
|
|||
// 5. SERVICE_URL_API_8080
|
||||
// 6. SERVICE_FQDN_API_8080
|
||||
});
|
||||
|
||||
it('detects SERVICE_URL variables in map-style environment format', function () {
|
||||
$yaml = <<<'YAML'
|
||||
services:
|
||||
trigger:
|
||||
environment:
|
||||
SERVICE_URL_TRIGGER_3000: ""
|
||||
SERVICE_FQDN_DB: localhost
|
||||
OTHER_VAR: value
|
||||
YAML;
|
||||
|
||||
$dockerCompose = Yaml::parse($yaml);
|
||||
$serviceConfig = data_get($dockerCompose, 'services.trigger');
|
||||
$environment = data_get($serviceConfig, 'environment', []);
|
||||
|
||||
$templateVariableNames = [];
|
||||
foreach ($environment as $key => $value) {
|
||||
if (is_int($key) && is_string($value)) {
|
||||
// List-style
|
||||
$envVarName = str($value)->before('=')->trim();
|
||||
if ($envVarName->startsWith('SERVICE_FQDN_') || $envVarName->startsWith('SERVICE_URL_')) {
|
||||
$templateVariableNames[] = $envVarName->value();
|
||||
}
|
||||
} elseif (is_string($key)) {
|
||||
// Map-style
|
||||
$envVarName = str($key);
|
||||
if ($envVarName->startsWith('SERVICE_FQDN_') || $envVarName->startsWith('SERVICE_URL_')) {
|
||||
$templateVariableNames[] = $envVarName->value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect($templateVariableNames)->toHaveCount(2);
|
||||
expect($templateVariableNames)->toContain('SERVICE_URL_TRIGGER_3000');
|
||||
expect($templateVariableNames)->toContain('SERVICE_FQDN_DB');
|
||||
expect($templateVariableNames)->not->toContain('OTHER_VAR');
|
||||
});
|
||||
|
||||
it('handles multiple map-style SERVICE_URL and SERVICE_FQDN variables', function () {
|
||||
$yaml = <<<'YAML'
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
SERVICE_URL_APP_3000: ""
|
||||
SERVICE_FQDN_API: api.local
|
||||
SERVICE_URL_WEB: ""
|
||||
OTHER_VAR: value
|
||||
YAML;
|
||||
|
||||
$dockerCompose = Yaml::parse($yaml);
|
||||
$serviceConfig = data_get($dockerCompose, 'services.app');
|
||||
$environment = data_get($serviceConfig, 'environment', []);
|
||||
|
||||
$templateVariableNames = [];
|
||||
foreach ($environment as $key => $value) {
|
||||
if (is_int($key) && is_string($value)) {
|
||||
// List-style
|
||||
$envVarName = str($value)->before('=')->trim();
|
||||
if ($envVarName->startsWith('SERVICE_FQDN_') || $envVarName->startsWith('SERVICE_URL_')) {
|
||||
$templateVariableNames[] = $envVarName->value();
|
||||
}
|
||||
} elseif (is_string($key)) {
|
||||
// Map-style
|
||||
$envVarName = str($key);
|
||||
if ($envVarName->startsWith('SERVICE_FQDN_') || $envVarName->startsWith('SERVICE_URL_')) {
|
||||
$templateVariableNames[] = $envVarName->value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect($templateVariableNames)->toHaveCount(3);
|
||||
expect($templateVariableNames)->toContain('SERVICE_URL_APP_3000');
|
||||
expect($templateVariableNames)->toContain('SERVICE_FQDN_API');
|
||||
expect($templateVariableNames)->toContain('SERVICE_URL_WEB');
|
||||
expect($templateVariableNames)->not->toContain('OTHER_VAR');
|
||||
});
|
||||
|
||||
it('does not detect SERVICE_URL references in map-style values', function () {
|
||||
$yaml = <<<'YAML'
|
||||
services:
|
||||
app:
|
||||
environment:
|
||||
SERVICE_URL_APP_3000: ""
|
||||
NEXT_PUBLIC_URL: ${SERVICE_URL_APP}
|
||||
API_ENDPOINT: ${SERVICE_URL_API}
|
||||
YAML;
|
||||
|
||||
$dockerCompose = Yaml::parse($yaml);
|
||||
$serviceConfig = data_get($dockerCompose, 'services.app');
|
||||
$environment = data_get($serviceConfig, 'environment', []);
|
||||
|
||||
$templateVariableNames = [];
|
||||
foreach ($environment as $key => $value) {
|
||||
if (is_int($key) && is_string($value)) {
|
||||
// List-style
|
||||
$envVarName = str($value)->before('=')->trim();
|
||||
if ($envVarName->startsWith('SERVICE_FQDN_') || $envVarName->startsWith('SERVICE_URL_')) {
|
||||
$templateVariableNames[] = $envVarName->value();
|
||||
}
|
||||
} elseif (is_string($key)) {
|
||||
// Map-style
|
||||
$envVarName = str($key);
|
||||
if ($envVarName->startsWith('SERVICE_FQDN_') || $envVarName->startsWith('SERVICE_URL_')) {
|
||||
$templateVariableNames[] = $envVarName->value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Should only detect the direct declaration, not references in values
|
||||
expect($templateVariableNames)->toHaveCount(1);
|
||||
expect($templateVariableNames)->toContain('SERVICE_URL_APP_3000');
|
||||
expect($templateVariableNames)->not->toContain('SERVICE_URL_APP');
|
||||
expect($templateVariableNames)->not->toContain('SERVICE_URL_API');
|
||||
expect($templateVariableNames)->not->toContain('NEXT_PUBLIC_URL');
|
||||
expect($templateVariableNames)->not->toContain('API_ENDPOINT');
|
||||
});
|
||||
|
||||
it('handles map-style with abbreviated service names', function () {
|
||||
// Simulating the langfuse.yaml case with map-style
|
||||
$yaml = <<<'YAML'
|
||||
services:
|
||||
langfuse:
|
||||
environment:
|
||||
SERVICE_URL_LANGFUSE_3000: ${SERVICE_URL_LANGFUSE_3000}
|
||||
DATABASE_URL: postgres://...
|
||||
YAML;
|
||||
|
||||
$dockerCompose = Yaml::parse($yaml);
|
||||
$serviceConfig = data_get($dockerCompose, 'services.langfuse');
|
||||
$environment = data_get($serviceConfig, 'environment', []);
|
||||
|
||||
$templateVariableNames = [];
|
||||
foreach ($environment as $key => $value) {
|
||||
if (is_int($key) && is_string($value)) {
|
||||
// List-style
|
||||
$envVarName = str($value)->before('=')->trim();
|
||||
if ($envVarName->startsWith('SERVICE_FQDN_') || $envVarName->startsWith('SERVICE_URL_')) {
|
||||
$templateVariableNames[] = $envVarName->value();
|
||||
}
|
||||
} elseif (is_string($key)) {
|
||||
// Map-style
|
||||
$envVarName = str($key);
|
||||
if ($envVarName->startsWith('SERVICE_FQDN_') || $envVarName->startsWith('SERVICE_URL_')) {
|
||||
$templateVariableNames[] = $envVarName->value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect($templateVariableNames)->toHaveCount(1);
|
||||
expect($templateVariableNames)->toContain('SERVICE_URL_LANGFUSE_3000');
|
||||
expect($templateVariableNames)->not->toContain('DATABASE_URL');
|
||||
});
|
||||
|
||||
it('verifies updateCompose helper has dual-format handling', function () {
|
||||
$servicesFile = file_get_contents(__DIR__.'/../../bootstrap/helpers/services.php');
|
||||
|
||||
// Check that both formats are handled
|
||||
expect($servicesFile)->toContain('is_int($key) && is_string($value)');
|
||||
expect($servicesFile)->toContain('List-style');
|
||||
expect($servicesFile)->toContain('elseif (is_string($key))');
|
||||
expect($servicesFile)->toContain('Map-style');
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue