From c516825a6c66e4bbf2698a461d4b835817d60b22 Mon Sep 17 00:00:00 2001 From: Savage Date: Fri, 20 Mar 2026 03:38:55 +0200 Subject: [PATCH] fix(cli): apply --files startup seed in virtual shell --- src/cli/shell-files.test.ts | 48 +++++++++++++++++++++++++++++++++++++ src/cli/shell-files.ts | 15 ++++++++++++ src/cli/shell.ts | 2 ++ 3 files changed, 65 insertions(+) create mode 100644 src/cli/shell-files.test.ts create mode 100644 src/cli/shell-files.ts diff --git a/src/cli/shell-files.test.ts b/src/cli/shell-files.test.ts new file mode 100644 index 00000000..850c2f03 --- /dev/null +++ b/src/cli/shell-files.test.ts @@ -0,0 +1,48 @@ +import * as fs from "node:fs"; +import * as os from "node:os"; +import * as path from "node:path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { Bash } from "../Bash.js"; +import { OverlayFs } from "../fs/overlay-fs/overlay-fs.js"; +import { seedOverlayFiles } from "./shell-files.js"; + +describe("seedOverlayFiles", () => { + let tempDir: string; + + beforeEach(() => { + tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "just-bash-shell-files-")); + fs.writeFileSync(path.join(tempDir, "real.txt"), "from-disk"); + }); + + afterEach(() => { + fs.rmSync(tempDir, { recursive: true, force: true }); + }); + + it("seeds overlay-only files for shell startup", async () => { + const overlayFs = new OverlayFs({ root: tempDir, mountPoint: "/" }); + seedOverlayFiles(overlayFs, { + "/seeded.txt": "from-seed", + "nested/seeded2.txt": "from-seed-2", + }); + + const env = new Bash({ fs: overlayFs, cwd: "/" }); + const one = await env.exec("cat /seeded.txt"); + const two = await env.exec("cat /nested/seeded2.txt"); + + expect(one.exitCode).toBe(0); + expect(one.stdout).toBe("from-seed"); + expect(two.exitCode).toBe(0); + expect(two.stdout).toBe("from-seed-2"); + }); + + it("does nothing when files are omitted", async () => { + const overlayFs = new OverlayFs({ root: tempDir, mountPoint: "/" }); + seedOverlayFiles(overlayFs, undefined); + + const env = new Bash({ fs: overlayFs, cwd: "/" }); + const result = await env.exec("cat /seeded.txt"); + + expect(result.exitCode).toBe(1); + expect(result.stderr).toContain("No such file or directory"); + }); +}); diff --git a/src/cli/shell-files.ts b/src/cli/shell-files.ts new file mode 100644 index 00000000..54c49a00 --- /dev/null +++ b/src/cli/shell-files.ts @@ -0,0 +1,15 @@ +import type { OverlayFs } from "../fs/overlay-fs/overlay-fs.js"; + +/** + * Seed in-memory overlay files for shell startup fixtures. + * Paths are virtual shell paths (e.g. "/tmp/data.txt"). + */ +export function seedOverlayFiles( + overlayFs: OverlayFs, + files?: Record, +): void { + if (!files) return; + for (const [path, content] of Object.entries(files)) { + overlayFs.writeFileSync(path, content); + } +} diff --git a/src/cli/shell.ts b/src/cli/shell.ts index 1a6b5c05..d60ff8a2 100644 --- a/src/cli/shell.ts +++ b/src/cli/shell.ts @@ -12,6 +12,7 @@ import * as readline from "node:readline"; import { Bash } from "../Bash.js"; import { OverlayFs } from "../fs/overlay-fs/overlay-fs.js"; import { getErrorMessage } from "../interpreter/helpers/errors.js"; +import { seedOverlayFiles } from "./shell-files.js"; // ANSI colors const colors = { @@ -47,6 +48,7 @@ class VirtualShell { root, mountPoint: "/", }); + seedOverlayFiles(overlayFs, options.files); this.env = new Bash({ fs: overlayFs,