refactor(desktop): share shell settings path resolution

This commit is contained in:
winewill committed 2026-10-29 17:57:09 +08:00
1 parent a7c8c3ac25
commit acf3f66325
3 files changed
+29 -6

No files matched your search

+12 -4
View File
@@ -3,7 +3,7 @@
* Ownership: .agents/notes/implemented/architecture/2026-10-08-desktop-shell-configuration.md.
*/
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
import { dirname, join } from 'node:path'
/** Resolved shell preferences; absent fields use the defaults without rewriting existing files. */
export interface DesktopSettings {
@@ -16,20 +16,28 @@ function record(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value)
}
/**
* Resolve the shared document used by shell preferences and update channels.
* @param userData - Electron's userData directory, independent of DSH_HOME.
* @returns Absolute settings path when userData is absolute; no filesystem changes.
*/
export function desktopSettingsPath(userData: string): string {
return join(userData, 'desktop', 'settings.json')
}
/**
* Read shell settings, creating the default document exclusively when missing.
* @param userData - Electron's userData directory, independent of DSH_HOME.
* @returns Validated settings, or defaults when reading, creating or validating fails; changes require a restart.
*/
export function readDesktopSettings(userData: string): DesktopSettings {
const directory = join(userData, 'desktop')
const path = join(directory, 'settings.json')
const path = desktopSettingsPath(userData)
const defaults: DesktopSettings = { updates: { allowTestAuthPopupWindow: false } }
try {
let raw: string
try { raw = readFileSync(path, 'utf8') } catch (error) {
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
mkdirSync(directory, { recursive: true, mode: 0o700 })
mkdirSync(dirname(path), { recursive: true, mode: 0o700 })
try { writeFileSync(path, `${JSON.stringify(defaults, null, 2)}\n`, { flag: 'wx', mode: 0o600 }) } catch (error) {
// An existing document belongs to the user, including one created during initialization.
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error
+3 -2
View File
@@ -1,7 +1,8 @@
/** Startup-only file reads and explicit UI writes for shell-owned update channels. */
import { readFile, mkdir } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { dirname } from 'node:path'
import { withFileLock, writeFileAtomic } from '@deepseek-ai/dsh-atomic-write'
import { desktopSettingsPath } from './settings.ts'
import type { DesktopUpdateChannel } from './update-channel.ts'
/** A settings operation failed without applying a new runtime channel. */
@@ -43,7 +44,7 @@ export class DesktopUpdateSettings {
* @param packagedChannel - Default selected by the installer build input.
*/
constructor(userData: string, private readonly packagedChannel: DesktopUpdateChannel) {
this.path = join(userData, 'desktop', 'settings.json')
this.path = desktopSettingsPath(userData)
}
/** @returns The startup channel, or a failure that must suspend ordinary updates. */
@@ -2,6 +2,7 @@ import { mkdtemp, mkdir, readFile, writeFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { expect, it, onTestFinished } from 'vitest'
import { readDesktopSettings } from '../src/settings.ts'
import { DesktopUpdateSettings, DesktopUpdateSettingsError } from '../src/update-settings.ts'
async function fixture() {
@@ -48,3 +49,16 @@ it('reports unreadable configuration instead of silently choosing a default', as
await mkdir(settings.path)
expect(await settings.load()).toMatchObject({ kind: 'error', error: { kind: 'read' } })
})
it('updates the document initialized by shell settings without losing the popup preference', async () => {
const root = await mkdtemp(join(tmpdir(), 'desktop-shared-settings-'))
onTestFinished(() => rm(root, { recursive: true, force: true }))
expect(readDesktopSettings(root).updates.allowTestAuthPopupWindow).toBe(false)
const settings = new DesktopUpdateSettings(root, 'latest')
await settings.save('nightly')
expect(JSON.parse(await readFile(settings.path, 'utf8'))).toEqual({
updates: { allowTestAuthPopupWindow: false, channel: 'nightly' },
})
expect(readDesktopSettings(root).updates.allowTestAuthPopupWindow).toBe(false)
expect(await settings.load()).toEqual({ kind: 'ready', channel: 'nightly' })
})