From 50b589aeff3820d000b741335e46e437c985efb7 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Fri, 26 Dec 2025 12:02:24 +0100 Subject: [PATCH 1/3] fix(ui): Initialize latestVersion in Upgrade component mount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upgrade modal was displaying "0.0.0" as the target version because $latestVersion was not initialized during component mount. The Blade template rendered before Alpine's x-init could populate the value. Fixed by calling get_latest_version_of_coolify() in mount() to ensure the version is available at initial render time. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 --- app/Livewire/Upgrade.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/Livewire/Upgrade.php b/app/Livewire/Upgrade.php index 7948ad6a9..25cedc71b 100644 --- a/app/Livewire/Upgrade.php +++ b/app/Livewire/Upgrade.php @@ -24,6 +24,7 @@ class Upgrade extends Component public function mount() { $this->currentVersion = config('constants.coolify.version'); + $this->latestVersion = get_latest_version_of_coolify(); $this->devMode = isDev(); } From c2a3be152e2ef462e2dce187872dd54a0a6d92fd Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 31 Mar 2026 17:28:24 +0200 Subject: [PATCH 2/3] test(upgrade): add mount tests for cached and fallback versions Covers Upgrade Livewire component mount behavior for: - initializing latest version from cached versions data - falling back to 0.0.0 when versions cache is unavailable --- tests/Feature/UpgradeComponentTest.php | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/Feature/UpgradeComponentTest.php diff --git a/tests/Feature/UpgradeComponentTest.php b/tests/Feature/UpgradeComponentTest.php new file mode 100644 index 000000000..612e989ae --- /dev/null +++ b/tests/Feature/UpgradeComponentTest.php @@ -0,0 +1,37 @@ + '4.0.0-beta.998']); + + Cache::shouldReceive('remember') + ->once() + ->with('coolify:versions:all', 3600, Mockery::type(\Closure::class)) + ->andReturn([ + 'coolify' => [ + 'v4' => [ + 'version' => '4.0.0-beta.999', + ], + ], + ]); + + Livewire::test(Upgrade::class) + ->assertSet('currentVersion', '4.0.0-beta.998') + ->assertSet('latestVersion', '4.0.0-beta.999') + ->set('isUpgradeAvailable', true) + ->assertSee('4.0.0-beta.998') + ->assertSee('4.0.0-beta.999'); +}); + +it('falls back to 0.0.0 during mount when cached versions data is unavailable', function () { + Cache::shouldReceive('remember') + ->once() + ->with('coolify:versions:all', 3600, Mockery::type(\Closure::class)) + ->andReturn(null); + + Livewire::test(Upgrade::class) + ->assertSet('latestVersion', '0.0.0'); +}); From dbd2b68a08f2b7a782381491dc9ca77e8a2b87fc Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:31:12 +0200 Subject: [PATCH 3/3] fix(upgrade): clear stale upgrade flag when version is already current Refactor upgrade state initialization into a shared `refreshUpgradeState()` method used by both `mount()` and `checkUpdate()`. The method now uses `version_compare` to validate upgrade availability and clears the `new_version_available` flag in InstanceSettings when the current version is already equal to or newer than the latest version, preventing stale upgrade notifications from persisting after a successful update. --- app/Livewire/Upgrade.php | 35 ++++++++++---- tests/Feature/UpgradeComponentTest.php | 65 +++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 10 deletions(-) diff --git a/app/Livewire/Upgrade.php b/app/Livewire/Upgrade.php index 25cedc71b..1b8701d94 100644 --- a/app/Livewire/Upgrade.php +++ b/app/Livewire/Upgrade.php @@ -23,25 +23,42 @@ class Upgrade extends Component public function mount() { - $this->currentVersion = config('constants.coolify.version'); - $this->latestVersion = get_latest_version_of_coolify(); - $this->devMode = isDev(); + $this->refreshUpgradeState(); } public function checkUpdate() { try { - $this->latestVersion = get_latest_version_of_coolify(); - $this->currentVersion = config('constants.coolify.version'); - $this->isUpgradeAvailable = data_get(InstanceSettings::get(), 'new_version_available', false); - if (isDev()) { - $this->isUpgradeAvailable = true; - } + $this->refreshUpgradeState(); } catch (\Throwable $e) { return handleError($e, $this); } } + protected function refreshUpgradeState(): void + { + $this->currentVersion = config('constants.coolify.version'); + $this->latestVersion = get_latest_version_of_coolify(); + $this->devMode = isDev(); + + if ($this->devMode) { + $this->isUpgradeAvailable = true; + + return; + } + + $settings = InstanceSettings::find(0); + $hasNewerVersion = version_compare($this->latestVersion, $this->currentVersion, '>'); + $newVersionAvailable = (bool) data_get($settings, 'new_version_available', false); + + if ($settings && $newVersionAvailable && ! $hasNewerVersion) { + $settings->update(['new_version_available' => false]); + $newVersionAvailable = false; + } + + $this->isUpgradeAvailable = $hasNewerVersion && $newVersionAvailable; + } + public function upgrade() { try { diff --git a/tests/Feature/UpgradeComponentTest.php b/tests/Feature/UpgradeComponentTest.php index 612e989ae..896e0fa3b 100644 --- a/tests/Feature/UpgradeComponentTest.php +++ b/tests/Feature/UpgradeComponentTest.php @@ -1,11 +1,19 @@ '4.0.0-beta.998']); + InstanceSettings::create([ + 'id' => 0, + 'new_version_available' => true, + ]); Cache::shouldReceive('remember') ->once() @@ -21,12 +29,17 @@ Livewire::test(Upgrade::class) ->assertSet('currentVersion', '4.0.0-beta.998') ->assertSet('latestVersion', '4.0.0-beta.999') - ->set('isUpgradeAvailable', true) + ->assertSet('isUpgradeAvailable', true) ->assertSee('4.0.0-beta.998') ->assertSee('4.0.0-beta.999'); }); it('falls back to 0.0.0 during mount when cached versions data is unavailable', function () { + InstanceSettings::create([ + 'id' => 0, + 'new_version_available' => false, + ]); + Cache::shouldReceive('remember') ->once() ->with('coolify:versions:all', 3600, Mockery::type(\Closure::class)) @@ -35,3 +48,53 @@ Livewire::test(Upgrade::class) ->assertSet('latestVersion', '0.0.0'); }); + +it('clears stale upgrade availability when current version already matches latest version', function () { + config(['constants.coolify.version' => '4.0.0-beta.999']); + InstanceSettings::create([ + 'id' => 0, + 'new_version_available' => true, + ]); + + Cache::shouldReceive('remember') + ->once() + ->with('coolify:versions:all', 3600, Mockery::type(\Closure::class)) + ->andReturn([ + 'coolify' => [ + 'v4' => [ + 'version' => '4.0.0-beta.999', + ], + ], + ]); + + Livewire::test(Upgrade::class) + ->assertSet('latestVersion', '4.0.0-beta.999') + ->assertSet('isUpgradeAvailable', false); + + expect(InstanceSettings::findOrFail(0)->new_version_available)->toBeFalse(); +}); + +it('clears stale upgrade availability when current version is newer than cached latest version', function () { + config(['constants.coolify.version' => '4.0.0-beta.1000']); + InstanceSettings::create([ + 'id' => 0, + 'new_version_available' => true, + ]); + + Cache::shouldReceive('remember') + ->once() + ->with('coolify:versions:all', 3600, Mockery::type(\Closure::class)) + ->andReturn([ + 'coolify' => [ + 'v4' => [ + 'version' => '4.0.0-beta.999', + ], + ], + ]); + + Livewire::test(Upgrade::class) + ->assertSet('latestVersion', '4.0.0-beta.999') + ->assertSet('isUpgradeAvailable', false); + + expect(InstanceSettings::findOrFail(0)->new_version_available)->toBeFalse(); +});