fix(update): persist update_channel via settings.json instead of hasGit

After b464fdc3 (preserve .git on release switch), the hasGit-based
channel detection is unreliable — .git now exists for both release
and nightly installs, so page refresh always reset the channel.

- Add _resolveChannelFromSettings() with migration heuristic:
  !hasGit → release (ZIP), detached HEAD → release (on tag),
  on branch → nightly. Uses gitInfo.branch from check-updates.
- Persist resolved channel to settings.json on first load
  (one-time migration) and on explicit switchChannel.
- Add update_channel validation (release|nightly) in backend
  update_settings handler.
- Remove hasGit-based guessing from initialize(); defer to
  checkForUpdates where full gitInfo is available.
- Channel resolution runs before checkForUpdates early-returns
  to avoid null channelMode on reload-within-interval.

Tests: 361 passed.
This commit is contained in:
Will Miao
2026-07-31 13:23:54 +08:00
parent b47dde87e4
commit dd6bdbf297
3 changed files with 78 additions and 12 deletions

View File

@@ -1,12 +1,26 @@
import { describe, beforeEach, afterEach, expect, it, vi } from 'vitest';
import { UpdateService } from '../../../static/js/managers/UpdateService.js';
import { state } from '../../../static/js/state/index.js';
function createFetchResponse(payload) {
return {
json: vi.fn().mockResolvedValue(payload)
json: vi.fn().mockResolvedValue(payload),
ok: true,
};
}
function stubSettingsUpdateChannel(channel) {
state.global = state.global || {};
state.global.settings = state.global.settings || {};
state.global.settings.update_channel = channel;
}
function clearSettingsUpdateChannel() {
if (state.global?.settings) {
delete state.global.settings.update_channel;
}
}
describe('UpdateService passive checks', () => {
let service;
let fetchMock;
@@ -16,10 +30,13 @@ describe('UpdateService passive checks', () => {
success: true,
current_version: 'v1.0.0',
latest_version: 'v1.0.0',
git_info: { short_hash: 'abc123' }
git_info: { short_hash: 'abc123' },
has_git: true,
}));
global.fetch = fetchMock;
stubSettingsUpdateChannel('release');
service = new UpdateService();
service.updateNotificationsEnabled = false;
service.lastCheckTime = 0;
@@ -28,6 +45,7 @@ describe('UpdateService passive checks', () => {
afterEach(() => {
delete global.fetch;
clearSettingsUpdateChannel();
});
it('skips passive update checks when notifications are disabled', async () => {