fix(plugin): handled pinned git source replacements

- Removed the stale pinned dependency edge before invoking Bun for same-repository git source replacements so Bun does not construct a dependency loop.

- Added a regression test for the pinned-to-unpinned GitHub plugin replacement path.

Fixes #4960
This commit is contained in:
roboomp
2026-07-09 18:35:14 +00:00
parent dd67447a03
commit 63adfeece5
4 changed files with 103 additions and 5 deletions
@@ -228,6 +228,79 @@ describe("PluginManager.install with git sources", () => {
]);
});
test("removes an existing pinned git dependency before installing the unpinned source (#4960)", async () => {
await Bun.write(
pluginsPkgJson,
JSON.stringify(
{
name: "omp-plugins",
private: true,
dependencies: { "replaced-plugin": "github:foo/bar#v1.0.0" },
},
null,
2,
),
);
const seedDir = path.join(pluginsNodeModules, "replaced-plugin");
await fs.mkdir(seedDir, { recursive: true });
await Bun.write(
path.join(seedDir, "package.json"),
JSON.stringify({ name: "replaced-plugin", version: "1.0.0" }, null, 2),
);
const spawnedCommands: string[][] = [];
vi.spyOn(Bun, "spawn").mockImplementation(((cmd: string[]) => {
spawnedCommands.push([...cmd]);
if (cmd[1] === "install") {
expect(cmd).toEqual(["bun", "install", "github:foo/bar"]);
const prepare = (async () => {
const packageJson = await Bun.file(pluginsPkgJson).json();
expect(packageJson.dependencies?.["replaced-plugin"]).toBeUndefined();
await Bun.write(
pluginsPkgJson,
JSON.stringify(
{
name: "omp-plugins",
private: true,
dependencies: { "replaced-plugin": "github:foo/bar" },
},
null,
2,
),
);
await Bun.write(
path.join(seedDir, "package.json"),
JSON.stringify({ name: "replaced-plugin", version: "1.1.0" }, null, 2),
);
})();
return {
pid: 1,
stdout: emptyStream(),
stderr: emptyStream(),
exited: prepare.then(() => 0),
} as Subprocess;
}
expect(cmd).toEqual(["bun", "update", "replaced-plugin"]);
return {
pid: 2,
stdout: emptyStream(),
stderr: emptyStream(),
exited: Promise.resolve(0),
} as Subprocess;
}) as typeof Bun.spawn);
const mgr = new PluginManager(tmpRoot);
const result = await mgr.install("github:foo/bar");
expect(result.name).toBe("replaced-plugin");
expect(result.version).toBe("1.1.0");
expect(spawnedCommands[0]).toEqual(["bun", "install", "github:foo/bar"]);
const packageJson = await Bun.file(pluginsPkgJson).json();
expect(packageJson.dependencies).toEqual({ "replaced-plugin": "github:foo/bar" });
});
test("first-time github install does NOT run `bun update` (no existing pin to refresh)", async () => {
await Bun.write(
pluginsPkgJson,