fix(tui): publish welcome acknowledgement atomically

This commit is contained in:
NI0317
2026-07-30 23:18:51 +08:00
parent 605136d18a
commit 05b84f4458
5 changed files with 17 additions and 24 deletions

View File

@@ -7,8 +7,9 @@
* @module @deepseek-ai/dsh/tui-first-run-welcome
*/
import { lstat, mkdir, open, rm } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { randomUUID } from 'node:crypto'
import { lstat, mkdir, open, rename, rm } from 'node:fs/promises'
import { basename, dirname, join } from 'node:path'
import type { Context } from 'cordis'
import {
Key,
@@ -97,9 +98,9 @@ export async function hasTuiFirstRunWelcomeAcknowledgement(
}
/**
* Persist one version acknowledgement as an immutable exclusive marker.
* Concurrent launches race only on file creation: the winner syncs the marker,
* and every loser accepts the same already-published regular file.
* Persist one version acknowledgement by syncing a random same-directory file
* before atomically replacing the immutable marker. Concurrent launches publish
* the same fact, so same-value last-writer-wins replacement loses no state.
* @param dshHome - Resolved Harness home.
* @param version - Copy version being acknowledged.
*/
@@ -109,32 +110,24 @@ export async function acknowledgeTuiFirstRunWelcome(
): Promise<void> {
const path = tuiFirstRunWelcomeAcknowledgementPath(dshHome, version)
const directory = dirname(path)
const temp = join(directory, `.${basename(path)}.${randomUUID()}.tmp`)
await mkdir(directory, { recursive: true, mode: 0o700 })
await syncDirectory(dirname(directory))
let handle: Awaited<ReturnType<typeof open>> | undefined
try {
handle = await open(path, 'wx', 0o600)
} catch (error) {
/* v8 ignore else -- the only expected race is another creator publishing this exact marker */
if ((error as NodeJS.ErrnoException | null)?.code === 'EEXIST') {
/* v8 ignore else -- EEXIST is accepted only after the winner is verified as a regular marker */
if (await hasTuiFirstRunWelcomeAcknowledgement(dshHome, version)) return
}
/* v8 ignore next -- unexpected filesystem failures pass through unchanged */
throw error
}
try {
handle = await open(temp, 'wx', 0o600)
await handle.sync()
const created = handle
handle = undefined
await created.close()
await rename(temp, path)
await syncDirectory(directory)
} catch (error) {
/* v8 ignore start -- fault-injected UI coverage proves failed acknowledgements stay uncommitted and retryable */
try {
await handle?.close()
} finally {
await rm(path, { force: true })
await rm(temp, { force: true })
}
throw error
/* v8 ignore stop */

View File

@@ -101,7 +101,7 @@ describe('TUI first-run welcome acknowledgement', () => {
recursive: true,
})
await expect(hasTuiFirstRunWelcomeAcknowledgement(home)).rejects.toThrow('is not a file')
await expect(acknowledgeTuiFirstRunWelcome(home)).rejects.toThrow('is not a file')
await expect(acknowledgeTuiFirstRunWelcome(home)).rejects.toThrow()
})
it('detects only explicit ASCII-only terminal environments', () => {