diff --git a/src/storage/verified-file.ts b/src/storage/verified-file.ts index 38332f5f..38407abd 100644 --- a/src/storage/verified-file.ts +++ b/src/storage/verified-file.ts @@ -140,8 +140,20 @@ export function readableFileRange( }); } -/** Syncs a directory entry after an atomic installation or replacement. */ -export async function syncDirectory(directory: string): Promise { +/** + * Syncs a directory entry after an atomic installation or replacement. + * + * A directory fsync is a POSIX durability barrier. Windows cannot open a + * directory as a file handle, so the open or sync call throws EPERM there and + * no equivalent barrier exists. On Windows the rename itself is the strongest + * available guarantee, so the sync is skipped rather than failing every + * publication. + */ +export async function syncDirectory( + directory: string, + platform: NodeJS.Platform = process.platform, +): Promise { + if (platform === "win32") return; const handle = await open(directory, "r"); try { await handle.sync(); diff --git a/tests/storage/verified-file-directory-sync.test.ts b/tests/storage/verified-file-directory-sync.test.ts new file mode 100644 index 00000000..9264abe3 --- /dev/null +++ b/tests/storage/verified-file-directory-sync.test.ts @@ -0,0 +1,39 @@ +import {mkdtemp, rm} from "node:fs/promises"; +import {tmpdir} from "node:os"; +import {join} from "node:path"; + +import {afterEach, beforeEach, describe, expect, it} from "vitest"; + +import {syncDirectory} from "../../src/storage/verified-file.js"; + +describe("syncDirectory platform behavior", () => { + let directory = ""; + + beforeEach(async () => { + directory = await mkdtemp(join(tmpdir(), "artifact-server-sync-")); + }); + + afterEach(async () => { + await rm(directory, {force: true, recursive: true}); + }); + + it("syncs a real directory on POSIX platforms", async () => { + await expect(syncDirectory(directory, "linux")).resolves.toBeUndefined(); + }); + + it("skips the directory barrier on Windows instead of failing publication", async () => { + await expect(syncDirectory(directory, "win32")).resolves.toBeUndefined(); + }); + + it("still reports a missing directory on POSIX platforms", async () => { + await expect( + syncDirectory(join(directory, "absent"), "linux"), + ).rejects.toThrowError(/ENOENT/u); + }); + + it("does not touch the filesystem on Windows", async () => { + await expect( + syncDirectory(join(directory, "absent"), "win32"), + ).resolves.toBeUndefined(); + }); +});