From 0ecd488d6a588f8847b1678f8638d24dbdbb2da9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Wed, 13 May 2026 10:04:17 +0200 Subject: [PATCH] fix(applications): refresh pending configuration changes Dispatch configuration change events after saving application source and advanced settings, and refresh the configuration checker before showing redeploy diffs. --- app/Livewire/Project/Application/Advanced.php | 3 ++ app/Livewire/Project/Application/Source.php | 2 + .../Project/Shared/ConfigurationChecker.php | 16 ++++++-- .../shared/configuration-checker.blade.php | 3 +- .../ApplicationSourceLocalhostKeyTest.php | 28 ++++++++++++- .../Livewire/ConfigurationCheckerTest.php | 39 +++++++++++++++++++ .../AdvancedStopGracePeriodTest.php | 10 +++++ 7 files changed, 95 insertions(+), 6 deletions(-) diff --git a/app/Livewire/Project/Application/Advanced.php b/app/Livewire/Project/Application/Advanced.php index 618958140..947368a0d 100644 --- a/app/Livewire/Project/Application/Advanced.php +++ b/app/Livewire/Project/Application/Advanced.php @@ -219,6 +219,7 @@ public function submit() } $this->syncData(true); $this->dispatch('success', 'Settings saved.'); + $this->dispatch('configurationChanged'); } catch (\Throwable $e) { return handleError($e, $this); } @@ -237,6 +238,7 @@ public function saveCustomName() if (is_null($this->customInternalName)) { $this->syncData(true); $this->dispatch('success', 'Custom name saved.'); + $this->dispatch('configurationChanged'); return; } @@ -256,6 +258,7 @@ public function saveCustomName() } $this->syncData(true); $this->dispatch('success', 'Custom name saved.'); + $this->dispatch('configurationChanged'); } catch (\Throwable $e) { return handleError($e, $this); } diff --git a/app/Livewire/Project/Application/Source.php b/app/Livewire/Project/Application/Source.php index 422dd6b28..3ef5ccf7c 100644 --- a/app/Livewire/Project/Application/Source.php +++ b/app/Livewire/Project/Application/Source.php @@ -109,6 +109,7 @@ public function setPrivateKey(int $privateKeyId) $this->application->refresh(); $this->privateKeyName = $this->application->private_key->name; $this->dispatch('success', 'Private key updated!'); + $this->dispatch('configurationChanged'); } catch (\Throwable $e) { return handleError($e, $this); } @@ -124,6 +125,7 @@ public function submit() } $this->syncData(true); $this->dispatch('success', 'Application source updated!'); + $this->dispatch('configurationChanged'); } catch (\Throwable $e) { return handleError($e, $this); } diff --git a/app/Livewire/Project/Shared/ConfigurationChecker.php b/app/Livewire/Project/Shared/ConfigurationChecker.php index 1bf2ecada..d583e74e6 100644 --- a/app/Livewire/Project/Shared/ConfigurationChecker.php +++ b/app/Livewire/Project/Shared/ConfigurationChecker.php @@ -12,6 +12,7 @@ use App\Models\StandaloneMysql; use App\Models\StandalonePostgresql; use App\Models\StandaloneRedis; +use Illuminate\Contracts\View\View; use Livewire\Component; class ConfigurationChecker extends Component @@ -24,7 +25,7 @@ class ConfigurationChecker extends Component public Application|Service|StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource; - public function getListeners() + public function getListeners(): array { $teamId = auth()->user()->currentTeam()->id; @@ -34,18 +35,25 @@ public function getListeners() ]; } - public function mount() + public function mount(): void { $this->configurationChanged(); } - public function render() + public function render(): View { return view('livewire.project.shared.configuration-checker'); } - public function configurationChanged() + public function refreshConfigurationChanges(): void { + $this->configurationChanged(); + } + + public function configurationChanged(): void + { + $this->resource->refresh(); + if ($this->resource instanceof Application) { $diff = $this->resource->pendingDeploymentConfigurationDiff(); $this->isConfigurationChanged = $diff->isChanged(); diff --git a/resources/views/livewire/project/shared/configuration-checker.blade.php b/resources/views/livewire/project/shared/configuration-checker.blade.php index 7d5ee70af..2c4440dfb 100644 --- a/resources/views/livewire/project/shared/configuration-checker.blade.php +++ b/resources/views/livewire/project/shared/configuration-checker.blade.php @@ -23,7 +23,8 @@ Please redeploy to apply the new configuration. @endif @else diff --git a/tests/Feature/ApplicationSourceLocalhostKeyTest.php b/tests/Feature/ApplicationSourceLocalhostKeyTest.php index 9b9b7b184..1a38bd26e 100644 --- a/tests/Feature/ApplicationSourceLocalhostKeyTest.php +++ b/tests/Feature/ApplicationSourceLocalhostKeyTest.php @@ -24,12 +24,23 @@ $this->environment = Environment::factory()->create(['project_id' => $this->project->id]); }); +function applicationSourceValidPrivateKey(): string +{ + return '-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk +hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA +AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV +uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== +-----END OPENSSH PRIVATE KEY-----'; +} + describe('Application Source with localhost key (id=0)', function () { test('renders deploy key section when private_key_id is 0', function () { $privateKey = PrivateKey::create([ 'id' => 0, 'name' => 'localhost', - 'private_key' => 'test-key-content', + 'private_key' => applicationSourceValidPrivateKey(), 'team_id' => $this->team->id, ]); @@ -56,4 +67,19 @@ ->assertDontSee('Deploy Key') ->assertSee('No source connected'); }); + + test('dispatches configuration changed when source settings are saved', function () { + $application = Application::factory()->create([ + 'environment_id' => $this->environment->id, + 'git_repository' => 'coollabsio/coolify', + 'git_branch' => 'main', + 'git_commit_sha' => 'HEAD', + ]); + + Livewire::test(Source::class, ['application' => $application]) + ->set('gitBranch', 'next') + ->call('submit') + ->assertHasNoErrors() + ->assertDispatched('configurationChanged'); + }); }); diff --git a/tests/Feature/Livewire/ConfigurationCheckerTest.php b/tests/Feature/Livewire/ConfigurationCheckerTest.php index 3dfa44712..edf8c5044 100644 --- a/tests/Feature/Livewire/ConfigurationCheckerTest.php +++ b/tests/Feature/Livewire/ConfigurationCheckerTest.php @@ -70,6 +70,45 @@ function markConfigurationCheckerApplicationDeployed(Application $application): ->assertSee('A rebuild is required.'); }); +it('refreshes configuration changes when the event is received', function () { + $application = configurationCheckerApplication($this->environment); + markConfigurationCheckerApplicationDeployed($application); + + $component = Livewire::test(ConfigurationChecker::class, ['resource' => $application->refresh()]) + ->assertSet('isConfigurationChanged', false) + ->assertDontSee('The latest configuration has not been applied'); + + $application->update(['build_command' => 'pnpm build']); + + $component + ->dispatch('configurationChanged') + ->assertSet('isConfigurationChanged', true) + ->assertSee('The latest configuration has not been applied') + ->assertSee('Build command'); +}); + +it('refreshes stale modal configuration diff before opening changes', function () { + $application = configurationCheckerApplication($this->environment); + markConfigurationCheckerApplicationDeployed($application); + + $application->update(['build_command' => 'pnpm build']); + + $component = Livewire::test(ConfigurationChecker::class, ['resource' => $application->refresh()]) + ->assertSee('Build command') + ->assertDontSee('Start command'); + + $application->update([ + 'build_command' => 'npm run build', + 'start_command' => 'node server.js', + ]); + + $component + ->call('refreshConfigurationChanges') + ->assertSet('isConfigurationChanged', true) + ->assertSee('Start command') + ->assertDontSee('Build command'); +}); + it('does not render environment variable secret values', function () { $application = configurationCheckerApplication($this->environment); EnvironmentVariable::create([ diff --git a/tests/Feature/Livewire/Project/Application/AdvancedStopGracePeriodTest.php b/tests/Feature/Livewire/Project/Application/AdvancedStopGracePeriodTest.php index 8d8de1d47..1bc179502 100644 --- a/tests/Feature/Livewire/Project/Application/AdvancedStopGracePeriodTest.php +++ b/tests/Feature/Livewire/Project/Application/AdvancedStopGracePeriodTest.php @@ -48,6 +48,16 @@ function createApplicationForAdvancedStopGracePeriodTest(): Application expect($application->settings()->first()->stop_grace_period)->toBe(300); }); +it('dispatches configuration changed when advanced settings are saved', function () { + $application = createApplicationForAdvancedStopGracePeriodTest(); + + Livewire::test(Advanced::class, ['application' => $application]) + ->set('includeSourceCommitInBuild', true) + ->call('submit') + ->assertHasNoErrors() + ->assertDispatched('configurationChanged'); +}); + it('clears the stop grace period when submitted empty', function () { $application = createApplicationForAdvancedStopGracePeriodTest(); $application->settings->update(['stop_grace_period' => 300]);