Merge remote-tracking branch 'origin/next' into pr-7764-shadow/add-shared-server-env

This commit is contained in:
Andras Bacsai
2026-03-31 10:52:31 +02:00
767 changed files with 61894 additions and 15664 deletions
@@ -31,6 +31,8 @@ class Add extends Component
public bool $is_buildtime = true;
public ?string $comment = null;
public array $problematicVariables = [];
protected $listeners = ['clearAddEnv' => 'clear'];
@@ -42,6 +44,7 @@ class Add extends Component
'is_literal' => 'required|boolean',
'is_runtime' => 'required|boolean',
'is_buildtime' => 'required|boolean',
'comment' => 'nullable|string|max:256',
];
protected $validationAttributes = [
@@ -51,6 +54,7 @@ class Add extends Component
'is_literal' => 'literal',
'is_runtime' => 'runtime',
'is_buildtime' => 'buildtime',
'comment' => 'comment',
];
public function mount()
@@ -197,6 +201,7 @@ class Add extends Component
'is_runtime' => $this->is_runtime,
'is_buildtime' => $this->is_buildtime,
'is_preview' => $this->is_preview,
'comment' => $this->comment,
]);
$this->clear();
}
@@ -209,5 +214,6 @@ class Add extends Component
$this->is_literal = false;
$this->is_runtime = true;
$this->is_buildtime = true;
$this->comment = null;
}
}
@@ -89,6 +89,62 @@ class All extends Component
return $query->get();
}
public function getHardcodedEnvironmentVariablesProperty()
{
return $this->getHardcodedVariables(false);
}
public function getHardcodedEnvironmentVariablesPreviewProperty()
{
return $this->getHardcodedVariables(true);
}
protected function getHardcodedVariables(bool $isPreview)
{
// Only for services and docker-compose applications
if ($this->resource->type() !== 'service' &&
($this->resourceClass !== 'App\Models\Application' ||
($this->resourceClass === 'App\Models\Application' && $this->resource->build_pack !== 'dockercompose'))) {
return collect([]);
}
$dockerComposeRaw = $this->resource->docker_compose_raw ?? $this->resource->docker_compose;
if (blank($dockerComposeRaw)) {
return collect([]);
}
// Extract all hard-coded variables
$hardcodedVars = extractHardcodedEnvironmentVariables($dockerComposeRaw);
// Filter out magic variables (SERVICE_FQDN_*, SERVICE_URL_*, SERVICE_NAME_*)
$hardcodedVars = $hardcodedVars->filter(function ($var) {
$key = $var['key'];
return ! str($key)->startsWith(['SERVICE_FQDN_', 'SERVICE_URL_', 'SERVICE_NAME_']);
});
// Filter out variables that exist in database (user has overridden/managed them)
// For preview, check against preview variables; for production, check against production variables
if ($isPreview) {
$managedKeys = $this->resource->environment_variables_preview()->pluck('key')->toArray();
} else {
$managedKeys = $this->resource->environment_variables()->where('is_preview', false)->pluck('key')->toArray();
}
$hardcodedVars = $hardcodedVars->filter(function ($var) use ($managedKeys) {
return ! in_array($var['key'], $managedKeys);
});
// Apply sorting based on is_env_sorting_enabled
if ($this->is_env_sorting_enabled) {
$hardcodedVars = $hardcodedVars->sortBy('key')->values();
}
// Otherwise keep order from docker-compose file
return $hardcodedVars;
}
public function getDevView()
{
$this->variables = $this->formatEnvironmentVariables($this->environmentVariables);
@@ -240,6 +296,7 @@ class All extends Component
$environment->is_runtime = $data['is_runtime'] ?? true;
$environment->is_buildtime = $data['is_buildtime'] ?? true;
$environment->is_preview = $data['is_preview'] ?? false;
$environment->comment = $data['comment'] ?? null;
$environment->resourceable_id = $this->resource->id;
$environment->resourceable_type = $this->resource->getMorphClass();
@@ -280,18 +337,37 @@ class All extends Component
private function updateOrCreateVariables($isPreview, $variables)
{
$count = 0;
foreach ($variables as $key => $value) {
foreach ($variables as $key => $data) {
if (str($key)->startsWith('SERVICE_FQDN') || str($key)->startsWith('SERVICE_URL') || str($key)->startsWith('SERVICE_NAME')) {
continue;
}
// Extract value and comment from parsed data
// Handle both array format ['value' => ..., 'comment' => ...] and plain string values
$value = is_array($data) ? ($data['value'] ?? '') : $data;
$comment = is_array($data) ? ($data['comment'] ?? null) : null;
$method = $isPreview ? 'environment_variables_preview' : 'environment_variables';
$found = $this->resource->$method()->where('key', $key)->first();
if ($found) {
if (! $found->is_shown_once && ! $found->is_multiline) {
// Only count as a change if the value actually changed
$changed = false;
// Update value if it changed
if ($found->value !== $value) {
$found->value = $value;
$changed = true;
}
// Only update comment from inline comment if one is provided (overwrites existing)
// If $comment is null, don't touch existing comment field to preserve it
if ($comment !== null && $found->comment !== $comment) {
$found->comment = $comment;
$changed = true;
}
if ($changed) {
$found->save();
$count++;
}
@@ -300,6 +376,7 @@ class All extends Component
$environment = new EnvironmentVariable;
$environment->key = $key;
$environment->value = $value;
$environment->comment = $comment; // Set comment from inline comment
$environment->is_multiline = false;
$environment->is_preview = $isPreview;
$environment->resourceable_id = $this->resource->id;
@@ -24,6 +24,8 @@ class Show extends Component
public bool $isLocked = false;
public bool $isMagicVariable = false;
public bool $isSharedVariable = false;
public string $type;
@@ -34,6 +36,8 @@ class Show extends Component
public ?string $real_value = null;
public ?string $comment = null;
public bool $is_shared = false;
public bool $is_multiline = false;
@@ -63,6 +67,7 @@ class Show extends Component
protected $rules = [
'key' => 'required|string',
'value' => 'nullable',
'comment' => 'nullable|string|max:256',
'is_multiline' => 'required|boolean',
'is_literal' => 'required|boolean',
'is_shown_once' => 'required|boolean',
@@ -93,6 +98,9 @@ class Show extends Component
public function refresh()
{
if (! $this->env->exists || ! $this->env->fresh()) {
return;
}
$this->syncData();
$this->checkEnvs();
}
@@ -104,6 +112,7 @@ class Show extends Component
$this->validate([
'key' => 'required|string',
'value' => 'nullable',
'comment' => 'nullable|string|max:256',
'is_multiline' => 'required|boolean',
'is_literal' => 'required|boolean',
'is_shown_once' => 'required|boolean',
@@ -118,6 +127,7 @@ class Show extends Component
}
$this->env->key = $this->key;
$this->env->value = $this->value;
$this->env->comment = $this->comment;
$this->env->is_multiline = $this->is_multiline;
$this->env->is_literal = $this->is_literal;
$this->env->is_shown_once = $this->is_shown_once;
@@ -125,6 +135,7 @@ class Show extends Component
} else {
$this->key = $this->env->key;
$this->value = $this->env->value;
$this->comment = $this->env->comment;
$this->is_multiline = $this->env->is_multiline;
$this->is_literal = $this->env->is_literal;
$this->is_shown_once = $this->env->is_shown_once;
@@ -140,9 +151,13 @@ class Show extends Component
public function checkEnvs()
{
$this->isDisabled = false;
$this->isMagicVariable = false;
if (str($this->env->key)->startsWith('SERVICE_FQDN') || str($this->env->key)->startsWith('SERVICE_URL') || str($this->env->key)->startsWith('SERVICE_NAME')) {
$this->isDisabled = true;
$this->isMagicVariable = true;
}
if ($this->env->is_shown_once) {
$this->isLocked = true;
}
@@ -0,0 +1,31 @@
<?php
namespace App\Livewire\Project\Shared\EnvironmentVariable;
use Livewire\Component;
class ShowHardcoded extends Component
{
public array $env;
public string $key;
public ?string $value = null;
public ?string $comment = null;
public ?string $serviceName = null;
public function mount()
{
$this->key = $this->env['key'];
$this->value = $this->env['value'] ?? null;
$this->comment = $this->env['comment'] ?? null;
$this->serviceName = $this->env['service_name'] ?? null;
}
public function render()
{
return view('livewire.project.shared.environment-variable.show-hardcoded');
}
}