Merge pull request #3718 from ismetanin/fix/cached-version-per-plugin-sandbox
fix(auto-update-checker): read loaded plugin's package.json, not the legacy flat install
This commit is contained in:
@@ -4,7 +4,10 @@ import { tmpdir } from "node:os"
|
|||||||
import { join } from "node:path"
|
import { join } from "node:path"
|
||||||
|
|
||||||
// Hold mutable mock state so beforeEach can swap the cache root for each test.
|
// Hold mutable mock state so beforeEach can swap the cache root for each test.
|
||||||
const mockState: { candidates: string[] } = { candidates: [] }
|
const mockState: { candidates: string[]; walkUpResult: string | null } = {
|
||||||
|
candidates: [],
|
||||||
|
walkUpResult: null,
|
||||||
|
}
|
||||||
|
|
||||||
mock.module("../constants", () => ({
|
mock.module("../constants", () => ({
|
||||||
INSTALLED_PACKAGE_JSON_CANDIDATES: new Proxy([], {
|
INSTALLED_PACKAGE_JSON_CANDIDATES: new Proxy([], {
|
||||||
@@ -22,7 +25,7 @@ mock.module("../constants", () => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
mock.module("./package-json-locator", () => ({
|
mock.module("./package-json-locator", () => ({
|
||||||
findPackageJsonUp: () => null,
|
findPackageJsonUp: () => mockState.walkUpResult,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
import { getCachedVersion } from "./cached-version"
|
import { getCachedVersion } from "./cached-version"
|
||||||
@@ -36,11 +39,13 @@ describe("getCachedVersion (GH-3257)", () => {
|
|||||||
join(cacheRoot, "node_modules", "oh-my-opencode", "package.json"),
|
join(cacheRoot, "node_modules", "oh-my-opencode", "package.json"),
|
||||||
join(cacheRoot, "node_modules", "oh-my-openagent", "package.json"),
|
join(cacheRoot, "node_modules", "oh-my-openagent", "package.json"),
|
||||||
]
|
]
|
||||||
|
mockState.walkUpResult = null
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
rmSync(cacheRoot, { recursive: true, force: true })
|
rmSync(cacheRoot, { recursive: true, force: true })
|
||||||
mockState.candidates = []
|
mockState.candidates = []
|
||||||
|
mockState.walkUpResult = null
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns the version when the package is installed under oh-my-opencode", () => {
|
it("returns the version when the package is installed under oh-my-opencode", () => {
|
||||||
@@ -77,4 +82,23 @@ describe("getCachedVersion (GH-3257)", () => {
|
|||||||
it("returns null when neither candidate exists and fallbacks find nothing", () => {
|
it("returns null when neither candidate exists and fallbacks find nothing", () => {
|
||||||
expect(getCachedVersion()).toBeNull()
|
expect(getCachedVersion()).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("prefers the loaded module's package.json over flat-install candidates", () => {
|
||||||
|
// OpenCode loads plugins from a per-plugin sandbox at
|
||||||
|
// <CACHE_DIR>/<plugin-entry>/node_modules/<pkg>/, while a parallel flat
|
||||||
|
// install at <CACHE_DIR>/node_modules/<pkg>/ can drift independently when
|
||||||
|
// bun re-resolves "latest". The flat install must NOT take precedence,
|
||||||
|
// because that's the path the user is actually running.
|
||||||
|
const sandboxDir = join(cacheRoot, "oh-my-openagent@latest", "node_modules", "oh-my-openagent")
|
||||||
|
mkdirSync(sandboxDir, { recursive: true })
|
||||||
|
const sandboxPkgJson = join(sandboxDir, "package.json")
|
||||||
|
writeFileSync(sandboxPkgJson, JSON.stringify({ name: "oh-my-openagent", version: "3.17.5" }))
|
||||||
|
mockState.walkUpResult = sandboxPkgJson
|
||||||
|
|
||||||
|
const flatDir = join(cacheRoot, "node_modules", "oh-my-opencode")
|
||||||
|
mkdirSync(flatDir, { recursive: true })
|
||||||
|
writeFileSync(join(flatDir, "package.json"), JSON.stringify({ name: "oh-my-opencode", version: "3.17.6" }))
|
||||||
|
|
||||||
|
expect(getCachedVersion()).toBe("3.17.5")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,16 +13,12 @@ function readPackageVersion(packageJsonPath: string): string | null {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getCachedVersion(): string | null {
|
export function getCachedVersion(): string | null {
|
||||||
for (const candidate of INSTALLED_PACKAGE_JSON_CANDIDATES) {
|
// Walk up from the loaded module first. OpenCode loads plugins from a
|
||||||
try {
|
// per-plugin sandbox at <CACHE_DIR>/<plugin-entry>/node_modules/<pkg>/, while
|
||||||
if (fs.existsSync(candidate)) {
|
// a parallel flat install at <CACHE_DIR>/node_modules/<pkg>/ can drift
|
||||||
return readPackageVersion(candidate)
|
// independently when bun re-resolves "latest". Reading the flat install
|
||||||
}
|
// first means the toast can announce a version the runtime isn't running.
|
||||||
} catch {
|
// The module-relative walk-up always reflects what is actually loaded.
|
||||||
// ignore; try next candidate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const currentDir = path.dirname(fileURLToPath(import.meta.url))
|
const currentDir = path.dirname(fileURLToPath(import.meta.url))
|
||||||
const pkgPath = findPackageJsonUp(currentDir)
|
const pkgPath = findPackageJsonUp(currentDir)
|
||||||
@@ -33,6 +29,16 @@ export function getCachedVersion(): string | null {
|
|||||||
log("[auto-update-checker] Failed to resolve version from current directory:", err)
|
log("[auto-update-checker] Failed to resolve version from current directory:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const candidate of INSTALLED_PACKAGE_JSON_CANDIDATES) {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(candidate)) {
|
||||||
|
return readPackageVersion(candidate)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore; try next candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const execDir = path.dirname(fs.realpathSync(process.execPath))
|
const execDir = path.dirname(fs.realpathSync(process.execPath))
|
||||||
const pkgPath = findPackageJsonUp(execDir)
|
const pkgPath = findPackageJsonUp(execDir)
|
||||||
|
|||||||
Reference in New Issue
Block a user