mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-04-28 11:59:29 +00:00
Restructure the repo as a Bun workspace monorepo: - Move cli/ → packages/cli/ - Create packages/shared/ (@openrouter/spawn-shared) with type-guards and parse utilities - Add root package.json with workspace configuration - Update all CLI imports to use @openrouter/spawn-shared - Deduplicate toRecord/toObjectArray helpers from 4 cloud modules - Update SPA (slack-bot) to use shared package instead of local toObj() - Update 48 agent shell scripts for new packages/cli/ path - Update install.sh, install.ps1, e2e, and test scripts - Update all GitHub workflows, .gitignore, pre-commit hooks - Update CLAUDE.md, README.md, and skill prompt references - Pin all dependency versions (no ^ ranges) - Bump CLI version 0.9.1 → 0.10.0 All 1908 tests pass. Lint clean. All 8 cloud bundles build. Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
/**
|
|
* Test preload script — filesystem isolation for CLI tests.
|
|
*
|
|
* Loaded before every test file via bunfig.toml `preload`.
|
|
* Redirects HOME and XDG dirs to a temp directory so no test
|
|
* can accidentally write to the real user's home directory
|
|
* (e.g. ~/.claude/settings.json, ~/.zshrc, ~/.ssh/id_rsa).
|
|
*
|
|
* This prevents the class of bugs where a test (or the code under test)
|
|
* overwrites real config files on the developer's machine.
|
|
*
|
|
* SANDBOXING STRATEGY:
|
|
* 1. Creates a unique temp directory for each test run
|
|
* 2. Sets process.env.HOME and all XDG_* variables to temp paths
|
|
* 3. Mocks os.homedir() to return the sandboxed HOME
|
|
* 4. Pre-creates common directories (~/.config, ~/.ssh, ~/.claude, etc.)
|
|
* 5. Cleans up the temp directory on process exit
|
|
*
|
|
* This ensures that:
|
|
* - Direct filesystem writes (fs.writeFileSync("~/.config/...")) are safe
|
|
* - Environment variable reads (process.env.HOME) point to temp
|
|
* - Node.js API calls (os.homedir()) return the sandboxed path
|
|
* - Subprocesses (execSync, spawnSync) inherit the sandboxed environment
|
|
*/
|
|
|
|
import { mkdirSync, readdirSync, rmSync, mkdtempSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
|
|
// ── Stray test file cleanup ──────────────────────────────────────────────────
|
|
//
|
|
// Automated refactor/discovery agents occasionally run tests from outside the
|
|
// cli/ directory, where bunfig.toml is not loaded and this preload never runs.
|
|
// In those cases HOME remains the real home directory (/root on CI), so any
|
|
// test that writes "$HOME/subprocess-test-*.txt" leaves files there.
|
|
//
|
|
// We clean up before and after every test run to keep the working tree tidy.
|
|
|
|
const REAL_HOME = process.env.HOME ?? "";
|
|
|
|
function cleanupStrayTestFiles(): void {
|
|
if (!REAL_HOME) {
|
|
return;
|
|
}
|
|
try {
|
|
for (const f of readdirSync(REAL_HOME)) {
|
|
if (f.startsWith("subprocess-test-") && f.endsWith(".txt")) {
|
|
rmSync(join(REAL_HOME, f), {
|
|
force: true,
|
|
});
|
|
}
|
|
}
|
|
} catch {
|
|
// Best-effort
|
|
}
|
|
}
|
|
|
|
cleanupStrayTestFiles();
|
|
|
|
// ── Create isolated HOME ────────────────────────────────────────────────────
|
|
|
|
const TEST_HOME = mkdtempSync(join(tmpdir(), "spawn-test-home-"));
|
|
|
|
// Redirect all user-directory env vars to the isolated temp
|
|
process.env.HOME = TEST_HOME;
|
|
process.env.XDG_CACHE_HOME = join(TEST_HOME, ".cache");
|
|
process.env.XDG_CONFIG_HOME = join(TEST_HOME, ".config");
|
|
process.env.XDG_DATA_HOME = join(TEST_HOME, ".local", "share");
|
|
|
|
// Pre-create common directories tests might expect
|
|
mkdirSync(join(TEST_HOME, ".cache"), {
|
|
recursive: true,
|
|
});
|
|
mkdirSync(join(TEST_HOME, ".config"), {
|
|
recursive: true,
|
|
});
|
|
mkdirSync(join(TEST_HOME, ".claude"), {
|
|
recursive: true,
|
|
});
|
|
mkdirSync(join(TEST_HOME, ".ssh"), {
|
|
recursive: true,
|
|
});
|
|
mkdirSync(join(TEST_HOME, ".local", "share"), {
|
|
recursive: true,
|
|
});
|
|
|
|
// ── Cleanup on exit ─────────────────────────────────────────────────────────
|
|
|
|
process.on("exit", () => {
|
|
try {
|
|
rmSync(TEST_HOME, {
|
|
recursive: true,
|
|
force: true,
|
|
});
|
|
} catch {
|
|
// Best-effort cleanup
|
|
}
|
|
cleanupStrayTestFiles();
|
|
});
|