Skip to content
58 changes: 58 additions & 0 deletions app/Livewire/Project/Service/FileStorage.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Attributes\Validate;
use Livewire\Component;
use Symfony\Component\Yaml\Yaml;

class FileStorage extends Component
{
Expand All @@ -30,6 +31,8 @@ class FileStorage extends Component

public ?string $workdir = null;

public ?string $resolvedFromEnvVar = null;

public bool $permanently_delete = true;

public bool $isReadOnly = false;
Expand Down Expand Up @@ -64,6 +67,7 @@ public function mount()
}

$this->isReadOnly = $this->fileStorage->shouldBeReadOnlyInUI();
$this->resolvedFromEnvVar = $this->detectEnvVarSource();
$this->syncData();
}

Expand All @@ -86,6 +90,60 @@ public function syncData(bool $toModel = false): void
}
}

/**
* Check the raw docker-compose for env var references in this volume's source.
* Returns the variable name if found, null otherwise.
*/
private function detectEnvVarSource(): ?string
{
try {
$resource = $this->resource;
$dockerComposeRaw = $resource->service?->docker_compose_raw
?? $resource->docker_compose_raw
?? null;

if (! $dockerComposeRaw) {
return null;
}

$compose = Yaml::parse($dockerComposeRaw);

if (! isset($compose['services'])) {
return null;
}

$mountPath = $this->fileStorage->mount_path;

// Search all services in the compose for a volume matching this mount path
foreach ($compose['services'] as $serviceVolumes) {
$volumes = data_get($serviceVolumes, 'volumes', []);
foreach ($volumes as $volume) {
$source = null;
$target = null;

if (is_string($volume)) {
$parts = explode(':', $volume);
if (count($parts) >= 2) {
$source = $parts[0];
$target = $parts[1];
}
} elseif (is_array($volume)) {
$source = data_get($volume, 'source');
$target = data_get($volume, 'target');
}

if ($target === $mountPath && $source && preg_match('/\$\{([a-zA-Z_][a-zA-Z0-9_]*)/', $source, $matches)) {
return $matches[1];
}
}
}
} catch (\Throwable $e) {
// Silently fail — this is just a UI hint
}

return null;
}
Comment thread
andrasbacsai marked this conversation as resolved.

public function convertToDirectory()
{
try {
Expand Down
3 changes: 2 additions & 1 deletion app/Livewire/Project/Shared/EnvironmentVariable/All.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Livewire\Project\Shared\EnvironmentVariable;

use App\Models\Application;
use App\Models\EnvironmentVariable;
use App\Traits\EnvironmentVariableProtection;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
Expand Down Expand Up @@ -38,7 +39,7 @@ public function mount()
$this->is_env_sorting_enabled = data_get($this->resource, 'settings.is_env_sorting_enabled', false);
$this->use_build_secrets = data_get($this->resource, 'settings.use_build_secrets', false);
$this->resourceClass = get_class($this->resource);
$resourceWithPreviews = [\App\Models\Application::class];
$resourceWithPreviews = [Application::class];
$simpleDockerfile = filled(data_get($this->resource, 'dockerfile'));
if (str($this->resourceClass)->contains($resourceWithPreviews) && ! $simpleDockerfile) {
$this->showPreview = true;
Expand Down
32 changes: 22 additions & 10 deletions app/Livewire/Project/Shared/EnvironmentVariable/Show.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@

namespace App\Livewire\Project\Shared\EnvironmentVariable;

use App\Models\Application;
use App\Models\Environment;
use App\Models\EnvironmentVariable as ModelsEnvironmentVariable;
use App\Models\LocalFileVolume;
use App\Models\Project;
use App\Models\Server;
use App\Models\Service;
use App\Models\SharedEnvironmentVariable;
use App\Traits\EnvironmentVariableAnalyzer;
use App\Traits\EnvironmentVariableProtection;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Attributes\Computed;
use Livewire\Component;
Expand Down Expand Up @@ -80,7 +85,7 @@ class Show extends Component
public function mount()
{
$this->syncData();
if ($this->env->getMorphClass() === \App\Models\SharedEnvironmentVariable::class) {
if ($this->env->getMorphClass() === SharedEnvironmentVariable::class) {
$this->isSharedVariable = true;
}
$this->parameters = get_route_parameters();
Expand Down Expand Up @@ -203,6 +208,13 @@ public function submit()
$this->serialize();
$this->syncData(true);
$this->syncData(false);

// Re-resolve volume paths that reference env vars (e.g., ${VAR:-./path})
$resourceable = $this->env->resourceable ?? null;
if ($resourceable) {
LocalFileVolume::reResolveVolumePaths($resourceable);
}

$this->dispatch('success', 'Environment variable updated.');
$this->dispatch('envsUpdated');
$this->dispatch('configurationChanged');
Expand Down Expand Up @@ -233,7 +245,7 @@ public function availableSharedVariables(): array
$result['team'] = $team->environment_variables()
->pluck('key')
->toArray();
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
} catch (AuthorizationException $e) {
// User not authorized to view team variables
}

Expand Down Expand Up @@ -264,12 +276,12 @@ public function availableSharedVariables(): array
$result['environment'] = $environment->environment_variables()
->pluck('key')
->toArray();
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
} catch (AuthorizationException $e) {
// User not authorized to view environment variables
}
}
}
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
} catch (AuthorizationException $e) {
// User not authorized to view project variables
}
}
Expand All @@ -279,7 +291,7 @@ public function availableSharedVariables(): array
$serverUuid = data_get($this->parameters, 'server_uuid');
if ($serverUuid) {
// If we have a specific server_uuid, show variables for that server
$server = \App\Models\Server::where('team_id', $team->id)
$server = Server::where('team_id', $team->id)
->where('uuid', $serverUuid)
->first();

Expand All @@ -289,15 +301,15 @@ public function availableSharedVariables(): array
$result['server'] = $server->environment_variables()
->pluck('key')
->toArray();
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
} catch (AuthorizationException $e) {
// User not authorized to view server variables
}
}
} else {
// For application environment variables, try to use the application's destination server
$applicationUuid = data_get($this->parameters, 'application_uuid');
if ($applicationUuid) {
$application = \App\Models\Application::whereRelation('environment.project.team', 'id', $team->id)
$application = Application::whereRelation('environment.project.team', 'id', $team->id)
->where('uuid', $applicationUuid)
->with('destination.server')
->first();
Expand All @@ -308,15 +320,15 @@ public function availableSharedVariables(): array
$result['server'] = $application->destination->server->environment_variables()
->pluck('key')
->toArray();
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
} catch (AuthorizationException $e) {
// User not authorized to view server variables
}
}
} else {
// For service environment variables, try to use the service's server
$serviceUuid = data_get($this->parameters, 'service_uuid');
if ($serviceUuid) {
$service = \App\Models\Service::whereRelation('environment.project.team', 'id', $team->id)
$service = Service::whereRelation('environment.project.team', 'id', $team->id)
->where('uuid', $serviceUuid)
->with('server')
->first();
Expand All @@ -327,7 +339,7 @@ public function availableSharedVariables(): array
$result['server'] = $service->server->environment_variables()
->pluck('key')
->toArray();
} catch (\Illuminate\Auth\Access\AuthorizationException $e) {
} catch (AuthorizationException $e) {
// User not authorized to view server variables
}
}
Expand Down
Loading
Loading