mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-09 20:51:22 +00:00
fix(baoyu-image-gen): tolerate Bun-on-Windows EEXIST from mkdir(recursive)
On Windows, Bun throws EEXIST for mkdir(dir, { recursive: true }) when the
directory already exists, contradicting Node's documented contract (it should
resolve silently). Image generation then succeeds but the file save fails
whenever the output directory already exists (e.g. the Desktop):
EEXIST: file already exists, mkdir 'C:\Users\...\Desktop'
Add an ensureDir() helper that tolerates EEXIST only when the path really is a
directory (rethrowing otherwise, so a genuine EEXIST against an existing file
is not swallowed), and route writeImage() and migrateLegacyExtendConfig()
through it. Covered by a cross-platform unit test.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ import type { CliArgs, ExtendConfig } from "./types.ts";
|
||||
import {
|
||||
createTaskArgs,
|
||||
detectProvider,
|
||||
ensureDir,
|
||||
getConfiguredMaxWorkers,
|
||||
getConfiguredProviderRateLimits,
|
||||
getWorkerCount,
|
||||
@@ -201,6 +202,26 @@ batch:
|
||||
});
|
||||
});
|
||||
|
||||
test("ensureDir creates nested dirs, is idempotent on an existing dir, and rethrows for a non-directory", async (t: TestContext) => {
|
||||
const root = await makeTempDir("ensure-dir-");
|
||||
t.after(() => fs.rm(root, { recursive: true, force: true }));
|
||||
|
||||
const nested = path.join(root, "a", "b", "c");
|
||||
await ensureDir(nested);
|
||||
assert.equal((await fs.stat(nested)).isDirectory(), true);
|
||||
|
||||
// Idempotent: a second call on an existing directory must not throw. This is the
|
||||
// Bun-on-Windows regression the helper guards against (Bun wrongly throws EEXIST
|
||||
// for mkdir(existingDir, { recursive: true })).
|
||||
await ensureDir(nested);
|
||||
|
||||
// Rethrows when the path exists but is a file rather than a directory, so a real
|
||||
// EEXIST against a non-directory is not silently swallowed.
|
||||
const filePath = path.join(root, "not-a-dir");
|
||||
await fs.writeFile(filePath, "x");
|
||||
await assert.rejects(() => ensureDir(filePath));
|
||||
});
|
||||
|
||||
test("loadExtendConfig renames legacy EXTEND.md when the new path is missing", async () => {
|
||||
const root = await makeTempDir("baoyu-image-gen-extend-");
|
||||
const cwd = path.join(root, "project");
|
||||
|
||||
@@ -2,7 +2,7 @@ import path from "node:path";
|
||||
import process from "node:process";
|
||||
import { homedir } from "node:os";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { access, mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
||||
import { access, mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";
|
||||
import type {
|
||||
BatchFile,
|
||||
BatchTaskInput,
|
||||
@@ -563,11 +563,25 @@ async function exists(filePath: string): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function ensureDir(dir: string): Promise<void> {
|
||||
try {
|
||||
await mkdir(dir, { recursive: true });
|
||||
} catch (err) {
|
||||
// Bun on Windows incorrectly throws EEXIST for mkdir(dir, { recursive: true })
|
||||
// when the directory already exists, contradicting Node's documented contract
|
||||
// (mkdir with recursive: true resolves silently for an existing directory).
|
||||
// Tolerate EEXIST only when the path really is a directory; rethrow otherwise
|
||||
// (e.g. EEXIST raised because the path points at an existing file).
|
||||
if ((err as { code?: string }).code !== "EEXIST") throw err;
|
||||
if (!(await stat(dir)).isDirectory()) throw err;
|
||||
}
|
||||
}
|
||||
|
||||
async function migrateLegacyExtendConfig(cwd: string, home: string): Promise<void> {
|
||||
for (const { current, legacy } of getExtendConfigPathPairs(cwd, home)) {
|
||||
const [hasCurrent, hasLegacy] = await Promise.all([exists(current), exists(legacy)]);
|
||||
if (hasCurrent || !hasLegacy) continue;
|
||||
await mkdir(path.dirname(current), { recursive: true });
|
||||
await ensureDir(path.dirname(current));
|
||||
await rename(legacy, current);
|
||||
}
|
||||
}
|
||||
@@ -1049,7 +1063,7 @@ async function prepareBatchTasks(
|
||||
}
|
||||
|
||||
async function writeImage(outputPath: string, imageData: Uint8Array): Promise<void> {
|
||||
await mkdir(path.dirname(outputPath), { recursive: true });
|
||||
await ensureDir(path.dirname(outputPath));
|
||||
await writeFile(outputPath, imageData);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user