Skip to content

Commit 85e66dc

Browse files
committed
fix: skip desktop restore when version is unchanged
1 parent 1611628 commit 85e66dc

3 files changed

Lines changed: 78 additions & 6 deletions

File tree

apps/dsa-desktop/main.js

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,7 @@ function restorePackagedRuntimeStateFromBackup() {
477477
backupRoot: null,
478478
restored: [],
479479
failed: [],
480+
skipped: [],
480481
};
481482

482483
if (!isWindowsNsisInstalledApp()) {
@@ -488,9 +489,26 @@ function restorePackagedRuntimeStateFromBackup() {
488489
return result;
489490
}
490491

491-
const appDir = resolveAppDir();
492492
const backupRoot = resolveUpdateBackupRoot();
493493
result.backupRoot = backupRoot;
494+
const backupAppVersion = normalizeVersionString(manifest.appVersion);
495+
const currentAppVersion = normalizeVersionString(resolveDesktopVersion());
496+
const versionComparison = backupAppVersion && currentAppVersion
497+
? compareVersions(backupAppVersion, currentAppVersion)
498+
: null;
499+
const isSameAppVersion = Boolean(
500+
backupAppVersion &&
501+
currentAppVersion &&
502+
(versionComparison === 0 || (versionComparison === null && backupAppVersion === currentAppVersion))
503+
);
504+
if (isSameAppVersion) {
505+
const reason = `manifest (app version ${currentAppVersion} did not change after update attempt)`;
506+
result.skipped.push(reason);
507+
logLine(`[update] skipped runtime restore because app version did not change after update attempt: ${currentAppVersion}`);
508+
return result;
509+
}
510+
511+
const appDir = resolveAppDir();
494512
const runtimeEntries = resolveRuntimeFileEntries(appDir);
495513
const relativeFiles = normalizeBackupFileList(manifest);
496514
const failedRelativeFiles = [];
@@ -535,6 +553,9 @@ function restorePackagedRuntimeStateFromBackup() {
535553
if (result.failed.length) {
536554
logLine(`[update] skipped runtime restore files after copy failure: ${result.failed.join(', ')}`);
537555
}
556+
if (result.skipped.length) {
557+
logLine(`[update] skipped runtime restore: ${result.skipped.join(', ')}`);
558+
}
538559

539560
return result;
540561
}
@@ -1375,14 +1396,17 @@ ipcMain.handle('desktop:open-release-page', async (_event, releaseUrl) => {
13751396
async function createWindow() {
13761397
const restoreResult = isWindowsNsisInstalledApp() ? restorePackagedRuntimeStateFromBackup() : null;
13771398
initLogging();
1378-
const restoreFailed = Boolean(restoreResult && restoreResult.failed.length);
1379-
const restoreErrorMessage = restoreFailed
1380-
? `上次更新安装后恢复运行时文件失败,已保留备份目录 ${restoreResult.backupRoot},请确认后手动恢复并重启应用。失败明细:${restoreResult.failed.join(';')}`
1399+
const restoreNeedsAttention = Boolean(restoreResult && (restoreResult.failed.length || restoreResult.skipped.length));
1400+
const restoreIssueDetails = restoreResult
1401+
? restoreResult.failed.concat(restoreResult.skipped).join(';')
1402+
: '';
1403+
const restoreErrorMessage = restoreNeedsAttention
1404+
? `上次更新安装未完成或恢复运行时文件失败,已保留备份目录 ${restoreResult.backupRoot},请确认后手动恢复并重启应用。明细:${restoreIssueDetails}`
13811405
: '';
13821406
setDesktopUpdateState({
1383-
status: restoreFailed ? UPDATE_STATUS.ERROR : UPDATE_STATUS.IDLE,
1407+
status: restoreNeedsAttention ? UPDATE_STATUS.ERROR : UPDATE_STATUS.IDLE,
13841408
currentVersion: resolveDesktopVersion(),
1385-
updateMode: restoreFailed ? UPDATE_MODE.MANUAL : UPDATE_MODE.AUTO,
1409+
updateMode: restoreNeedsAttention ? UPDATE_MODE.MANUAL : UPDATE_MODE.AUTO,
13861410
message: restoreErrorMessage,
13871411
});
13881412
const startupStartedAt = Date.now();

apps/dsa-desktop/tests/main.test.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,53 @@ test('restorePackagedRuntimeStateFromBackup removes restored files from pending
437437
assert.deepEqual(JSON.parse(fs.readFileSync(manifestPath, 'utf-8')).files, [dbRelativePath]);
438438
});
439439

440+
test('restorePackagedRuntimeStateFromBackup skips backup when app version did not change', (t) => {
441+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'dsa-desktop-same-version-restore-'));
442+
const appDir = path.join(tempRoot, 'app');
443+
const userDataDir = path.join(tempRoot, 'userData');
444+
const backupRoot = path.join(userDataDir, '.dsa-desktop-update-backup');
445+
const backupEnvPath = path.join(backupRoot, '.env');
446+
const targetEnvPath = path.join(appDir, '.env');
447+
const manifestPath = path.join(backupRoot, 'runtime-state.json');
448+
449+
fs.mkdirSync(backupRoot, { recursive: true });
450+
fs.mkdirSync(appDir, { recursive: true });
451+
fs.writeFileSync(path.join(appDir, 'Uninstall Daily Stock Analysis.exe'), '');
452+
fs.writeFileSync(backupEnvPath, 'pre-update-env\n', 'utf-8');
453+
fs.writeFileSync(targetEnvPath, 'user-change-after-aborted-install\n', 'utf-8');
454+
fs.writeFileSync(
455+
manifestPath,
456+
JSON.stringify({ appVersion: 'v3.12.0', files: ['.env'] }),
457+
'utf-8'
458+
);
459+
460+
const mainModule = loadMainModule(t, {
461+
platform: 'win32',
462+
app: {
463+
isPackaged: true,
464+
getPath: (name) => {
465+
if (name === 'exe') {
466+
return path.join(appDir, 'Daily Stock Analysis.exe');
467+
}
468+
return userDataDir;
469+
},
470+
},
471+
});
472+
473+
t.after(() => {
474+
fs.rmSync(tempRoot, { recursive: true, force: true });
475+
});
476+
477+
const restoreResult = mainModule.restorePackagedRuntimeStateFromBackup();
478+
assert.deepEqual(restoreResult.restored, []);
479+
assert.deepEqual(restoreResult.failed, []);
480+
assert.equal(restoreResult.skipped.length, 1);
481+
assert.match(restoreResult.skipped[0], /version 3\.12\.0 did not change/);
482+
assert.equal(fs.readFileSync(targetEnvPath, 'utf-8'), 'user-change-after-aborted-install\n');
483+
assert.equal(fs.existsSync(backupRoot), true);
484+
assert.deepEqual(JSON.parse(fs.readFileSync(manifestPath, 'utf-8')).files, ['.env']);
485+
});
486+
440487
test('stopBackend waits for backend process exit', async (t) => {
441488
const mainModule = loadMainModule(t);
442489
const killSignals = [];

docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
1717
- [文档] 强化桌面打包文档:补充 `latest.yml` / `*.blockmap``desktop-release` tag/version 一致性核验清单,明确非 Windows 环境下需在平台限制里补充说明。
1818
- [修复] 为 Windows NSIS 安装版自动更新加入安装目录运行时文件(`.env``data/stock_analysis.db``data/stock_analysis.db-wal``data/stock_analysis.db-shm``logs/desktop.log`)备份与首次启动恢复链路,并在 `quitAndInstall` 前等待后端退出,降低升级时配置与数据库丢失风险。
1919
- [修复] Windows NSIS 自动更新在运行时文件部分恢复失败时只保留失败项待重试,避免已恢复成功的配置或数据库文件在后续启动时被旧备份重复覆盖。
20+
- [修复] Windows NSIS 自动更新在安装尝试未切换桌面端版本时跳过自动恢复,避免失败或取消安装后误回滚用户运行时数据。
2021
- [修复] 清理提交中的临时探测文件(`node_modules_exists.txt``node_modules_ls_check.txt`),避免污染桌面/前端改动范围。
2122

2223
- [新功能] Web 系统设置页开放 `.env` 配置备份导入/导出,复用键级覆盖、配置版本冲突保护和重载链路;Web 端在 `ADMIN_AUTH_ENABLED=false` 时该入口为禁用状态。

0 commit comments

Comments
 (0)