refactor(desktop): share shell settings path resolution
This commit is contained in:
1 parent
a7c8c3ac25
commit
acf3f66325
3 files changed
+29
-6
No files matched your search
@@ -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
|
||||
|
||||
@@ -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' })
|
||||
})
|
||||
Reference in new issue
Block a user