* feat(update): update Bun global installations `rea update` only recognized npm global installations and looked up releases with `npm view`, so a `bun add --global rea-agents` install on a machine without npm failed with `spawn npm ENOENT`. Detect the running package inside Bun's global directory (BUN_INSTALL_GLOBAL_DIR, $BUN_INSTALL/install/global, $XDG_CACHE_HOME/.bun/install/global, ~/.bun/install/global), resolve the latest release with `bun info`, install with `bun add --global --ignore-scripts`, then verify the installed entry point and plan integration maintenance exactly as for npm. Bun commands run inside the owning global directory with BUN_INSTALL_GLOBAL_DIR pinned, so the caller's current project is never selected. A global directory without package.json is not treated as an owned installation: Bun then installs into the nearest ancestor project. npm installations keep their existing commands and messages. * fix(update): gate Bun self-updates on supported CLI versions --------- Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
90 lines
2.7 KiB
TypeScript
90 lines
2.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { err, ok } from "../domain/result.js";
|
|
import { safeParseJson } from "../domain/safeJson.js";
|
|
import { PRODUCT_IDENTITY } from "../identity.js";
|
|
import {
|
|
npmLatestVersionCommand,
|
|
npmReleaseLookup,
|
|
parseNpmLatestVersion,
|
|
} from "./UpdateRuntime.js";
|
|
|
|
const installation = {
|
|
manager: "npm",
|
|
prefix: "/opt/rea",
|
|
packageRoot: "/opt/rea/lib/node_modules/rea-agents",
|
|
} as const;
|
|
|
|
describe("npm latest release metadata", () => {
|
|
it("accepts npm 11's scalar JSON response", () => {
|
|
expect(parseNpmLatestVersion('"6.0.0"')).toEqual(ok("6.0.0"));
|
|
});
|
|
|
|
it("accepts npm 12's singleton-array JSON response", () => {
|
|
expect(parseNpmLatestVersion('[\n "6.0.0"\n]\n')).toEqual(ok("6.0.0"));
|
|
});
|
|
|
|
it.each([
|
|
["empty array", "[]"],
|
|
["multiple versions", '["6.0.0", "6.0.1"]'],
|
|
["empty version", '[""]'],
|
|
["number", "6"],
|
|
["object", '{"version":"6.0.0"}'],
|
|
["null", "null"],
|
|
])("rejects %s response data", (_description, metadata) => {
|
|
expect(parseNpmLatestVersion(metadata)).toEqual(
|
|
err(
|
|
"Invalid npm release metadata: expected a version string or a single-element version array.",
|
|
),
|
|
);
|
|
});
|
|
|
|
it("preserves malformed JSON separately from a wrong metadata shape", () => {
|
|
const metadata = '"6.0.0';
|
|
const decoded = safeParseJson(metadata);
|
|
expect(decoded.ok).toBe(false);
|
|
if (decoded.ok) return;
|
|
expect(parseNpmLatestVersion(metadata)).toEqual(err(decoded.error));
|
|
expect(decoded.error).not.toContain("single-element version array");
|
|
});
|
|
|
|
it("keeps command failures distinct from rejected metadata", () => {
|
|
expect(npmReleaseLookup(err("npm registry returned HTTP 503"))).toEqual(
|
|
err({ kind: "unavailable", detail: "npm registry returned HTTP 503" }),
|
|
);
|
|
expect(npmReleaseLookup(ok("[]"))).toEqual(
|
|
err({
|
|
kind: "invalid-metadata",
|
|
detail:
|
|
"Invalid npm release metadata: expected a version string or a single-element version array.",
|
|
}),
|
|
);
|
|
expect(npmReleaseLookup(ok('["6.0.0"]'))).toEqual(ok("6.0.0"));
|
|
});
|
|
|
|
it("keeps the global registry query and adds the owning prefix", () => {
|
|
expect(npmLatestVersionCommand(installation)).toEqual([
|
|
"npm",
|
|
"view",
|
|
"--global",
|
|
"--prefix",
|
|
installation.prefix,
|
|
PRODUCT_IDENTITY.packageName,
|
|
"dist-tags.latest",
|
|
"--json",
|
|
"--fetch-retries=0",
|
|
"--fetch-timeout=10000",
|
|
]);
|
|
expect(npmLatestVersionCommand(undefined)).toEqual([
|
|
"npm",
|
|
"view",
|
|
"--global",
|
|
PRODUCT_IDENTITY.packageName,
|
|
"dist-tags.latest",
|
|
"--json",
|
|
"--fetch-retries=0",
|
|
"--fetch-timeout=10000",
|
|
]);
|
|
});
|
|
});
|