mirror of
https://github.com/OpenRouterTeam/spawn.git
synced 2026-05-19 16:39:50 +00:00
fix: prevent uninstall from truncating RC files with missing end marker (#2927)
If the end marker (# <<< spawn <<<) is missing from .bashrc/.zshrc, cleanRcFile dropped all content after the start marker. Now detects unclosed blocks and skips the file with a warning instead of writing a truncated version. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: L <6723574+louisgv@users.noreply.github.com>
This commit is contained in:
parent
9651e029df
commit
42df6f753a
2 changed files with 60 additions and 0 deletions
|
|
@ -391,6 +391,58 @@ describe("cmdUninstall", () => {
|
|||
expect(clack.logSuccess).toHaveBeenCalledWith("Removed:");
|
||||
});
|
||||
|
||||
it("preserves RC file when end marker is missing (unclosed block)", async () => {
|
||||
const binaryPath = join(home, ".local", "bin", "spawn");
|
||||
fs.mkdirSync(join(home, ".local", "bin"), {
|
||||
recursive: true,
|
||||
});
|
||||
fs.writeFileSync(binaryPath, "#!/bin/bash\necho spawn");
|
||||
|
||||
// Remove optional dirs
|
||||
const spawnDir = join(home, ".spawn");
|
||||
const configDir = join(home, ".config", "spawn");
|
||||
if (fs.existsSync(spawnDir)) {
|
||||
fs.rmSync(spawnDir, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
if (fs.existsSync(configDir)) {
|
||||
fs.rmSync(configDir, {
|
||||
recursive: true,
|
||||
force: true,
|
||||
});
|
||||
}
|
||||
|
||||
// Write an RC file with start marker but NO end marker
|
||||
const rcPath = join(home, ".bashrc");
|
||||
const rcContent = [
|
||||
"# existing config",
|
||||
"alias ll='ls -la'",
|
||||
"",
|
||||
RC_MARKER_START,
|
||||
'export PATH="$HOME/.local/bin:$PATH"',
|
||||
"",
|
||||
"# user aliases that would be lost",
|
||||
"alias gs='git status'",
|
||||
].join("\n");
|
||||
fs.writeFileSync(rcPath, rcContent);
|
||||
|
||||
clack.confirm.mockResolvedValue(true);
|
||||
|
||||
await cmdUninstall();
|
||||
|
||||
// File should be unchanged — unclosed block means no write
|
||||
const after = fs.readFileSync(rcPath, "utf-8");
|
||||
expect(after).toBe(rcContent);
|
||||
expect(after).toContain("# user aliases that would be lost");
|
||||
expect(after).toContain("alias gs='git status'");
|
||||
|
||||
// Should have warned the user
|
||||
const warnCalls = clack.logWarn.mock.calls.map((c: unknown[]) => String(c[0]));
|
||||
expect(warnCalls.some((msg: string) => msg.includes("missing end marker"))).toBe(true);
|
||||
});
|
||||
|
||||
it("shows shell RC hint when RC files were cleaned", async () => {
|
||||
const binaryPath = join(home, ".local", "bin", "spawn");
|
||||
fs.mkdirSync(join(home, ".local", "bin"), {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,14 @@ function cleanRcFile(rcPath: string): boolean {
|
|||
cleaned.push(line);
|
||||
}
|
||||
|
||||
// Safety: if insideBlock is still true, the end marker is missing.
|
||||
// Abort to avoid truncating the user's shell config.
|
||||
if (insideBlock) {
|
||||
p.log.warn(`Spawn block in ${rcPath} is missing end marker — skipping to avoid data loss.`);
|
||||
p.log.warn(`Manually remove the line "${RC_MARKER_START}" and the spawn PATH export from ${rcPath}.`);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
fs.writeFileSync(rcPath, cleaned.join("\n"));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue