fix(auto-update-checker): use OpenCode cache paths for updates
Align version lookup, invalidation, and bun install with OpenCode's cache directory so updates target the loaded plugin location. Keep dependency declarations intact during invalidation so auto-update can reinstall instead of converging to uninstall.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, spyOn } from "bun:test"
|
||||
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"
|
||||
import { join } from "node:path"
|
||||
import * as dataPath from "../../shared/data-path"
|
||||
import * as opencodeConfigDir from "../../shared/opencode-config-dir"
|
||||
|
||||
const TEST_CACHE_DIR = join(import.meta.dir, "__test-cache__")
|
||||
const TEST_OPENCODE_CACHE_DIR = join(TEST_CACHE_DIR, "opencode")
|
||||
|
||||
function resetTestCache(): void {
|
||||
if (existsSync(TEST_CACHE_DIR)) {
|
||||
rmSync(TEST_CACHE_DIR, { recursive: true, force: true })
|
||||
}
|
||||
|
||||
mkdirSync(join(TEST_OPENCODE_CACHE_DIR, "node_modules", "oh-my-opencode"), { recursive: true })
|
||||
writeFileSync(
|
||||
join(TEST_OPENCODE_CACHE_DIR, "package.json"),
|
||||
JSON.stringify({ dependencies: { "oh-my-opencode": "latest", other: "1.0.0" } }, null, 2)
|
||||
)
|
||||
writeFileSync(
|
||||
join(TEST_OPENCODE_CACHE_DIR, "bun.lock"),
|
||||
JSON.stringify(
|
||||
{
|
||||
workspaces: {
|
||||
"": {
|
||||
dependencies: { "oh-my-opencode": "latest", other: "1.0.0" },
|
||||
},
|
||||
},
|
||||
packages: {
|
||||
"oh-my-opencode": {},
|
||||
other: {},
|
||||
},
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
)
|
||||
writeFileSync(
|
||||
join(TEST_OPENCODE_CACHE_DIR, "node_modules", "oh-my-opencode", "package.json"),
|
||||
'{"name":"oh-my-opencode"}'
|
||||
)
|
||||
}
|
||||
|
||||
describe("invalidatePackage", () => {
|
||||
let getOpenCodeCacheDirSpy: ReturnType<typeof spyOn>
|
||||
let getOpenCodeConfigDirSpy: ReturnType<typeof spyOn>
|
||||
|
||||
beforeEach(() => {
|
||||
getOpenCodeCacheDirSpy = spyOn(dataPath, "getOpenCodeCacheDir").mockReturnValue(TEST_OPENCODE_CACHE_DIR)
|
||||
getOpenCodeConfigDirSpy = spyOn(opencodeConfigDir, "getOpenCodeConfigDir").mockReturnValue("/tmp/opencode-config")
|
||||
resetTestCache()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
getOpenCodeCacheDirSpy.mockRestore()
|
||||
getOpenCodeConfigDirSpy.mockRestore()
|
||||
if (existsSync(TEST_CACHE_DIR)) {
|
||||
rmSync(TEST_CACHE_DIR, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it("invalidates the installed package from the OpenCode cache directory", async () => {
|
||||
const { invalidatePackage } = await import(`./cache?test=${Date.now()}`)
|
||||
|
||||
const result = invalidatePackage()
|
||||
|
||||
expect(result).toBe(true)
|
||||
expect(existsSync(join(TEST_OPENCODE_CACHE_DIR, "node_modules", "oh-my-opencode"))).toBe(false)
|
||||
|
||||
const packageJson = JSON.parse(readFileSync(join(TEST_OPENCODE_CACHE_DIR, "package.json"), "utf-8")) as {
|
||||
dependencies?: Record<string, string>
|
||||
}
|
||||
expect(packageJson.dependencies?.["oh-my-opencode"]).toBe("latest")
|
||||
expect(packageJson.dependencies?.other).toBe("1.0.0")
|
||||
|
||||
const bunLock = JSON.parse(readFileSync(join(TEST_OPENCODE_CACHE_DIR, "bun.lock"), "utf-8")) as {
|
||||
workspaces?: { ""?: { dependencies?: Record<string, string> } }
|
||||
packages?: Record<string, unknown>
|
||||
}
|
||||
expect(bunLock.workspaces?.[""]?.dependencies?.["oh-my-opencode"]).toBe("latest")
|
||||
expect(bunLock.workspaces?.[""]?.dependencies?.other).toBe("1.0.0")
|
||||
expect(bunLock.packages?.["oh-my-opencode"]).toBeUndefined()
|
||||
expect(bunLock.packages?.other).toEqual({})
|
||||
})
|
||||
})
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as fs from "node:fs"
|
||||
import * as path from "node:path"
|
||||
import { PACKAGE_NAME, USER_CONFIG_DIR } from "./constants"
|
||||
import { CACHE_DIR, PACKAGE_NAME } from "./constants"
|
||||
import { log } from "../../shared/logger"
|
||||
|
||||
interface BunLockfile {
|
||||
@@ -17,18 +17,13 @@ function stripTrailingCommas(json: string): string {
|
||||
}
|
||||
|
||||
function removeFromBunLock(packageName: string): boolean {
|
||||
const lockPath = path.join(USER_CONFIG_DIR, "bun.lock")
|
||||
const lockPath = path.join(CACHE_DIR, "bun.lock")
|
||||
if (!fs.existsSync(lockPath)) return false
|
||||
|
||||
try {
|
||||
const content = fs.readFileSync(lockPath, "utf-8")
|
||||
const lock = JSON.parse(stripTrailingCommas(content)) as BunLockfile
|
||||
let modified = false
|
||||
|
||||
if (lock.workspaces?.[""]?.dependencies?.[packageName]) {
|
||||
delete lock.workspaces[""].dependencies[packageName]
|
||||
modified = true
|
||||
}
|
||||
let modified = false
|
||||
|
||||
if (lock.packages?.[packageName]) {
|
||||
delete lock.packages[packageName]
|
||||
@@ -48,11 +43,9 @@ function removeFromBunLock(packageName: string): boolean {
|
||||
|
||||
export function invalidatePackage(packageName: string = PACKAGE_NAME): boolean {
|
||||
try {
|
||||
const pkgDir = path.join(USER_CONFIG_DIR, "node_modules", packageName)
|
||||
const pkgJsonPath = path.join(USER_CONFIG_DIR, "package.json")
|
||||
const pkgDir = path.join(CACHE_DIR, "node_modules", packageName)
|
||||
|
||||
let packageRemoved = false
|
||||
let dependencyRemoved = false
|
||||
let lockRemoved = false
|
||||
|
||||
if (fs.existsSync(pkgDir)) {
|
||||
@@ -61,20 +54,9 @@ export function invalidatePackage(packageName: string = PACKAGE_NAME): boolean {
|
||||
packageRemoved = true
|
||||
}
|
||||
|
||||
if (fs.existsSync(pkgJsonPath)) {
|
||||
const content = fs.readFileSync(pkgJsonPath, "utf-8")
|
||||
const pkgJson = JSON.parse(content)
|
||||
if (pkgJson.dependencies?.[packageName]) {
|
||||
delete pkgJson.dependencies[packageName]
|
||||
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2))
|
||||
log(`[auto-update-checker] Dependency removed from package.json: ${packageName}`)
|
||||
dependencyRemoved = true
|
||||
}
|
||||
}
|
||||
|
||||
lockRemoved = removeFromBunLock(packageName)
|
||||
|
||||
if (!packageRemoved && !dependencyRemoved && !lockRemoved) {
|
||||
if (!packageRemoved && !lockRemoved) {
|
||||
log(`[auto-update-checker] Package not found, nothing to invalidate: ${packageName}`)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { describe, expect, it } from "bun:test"
|
||||
import { join } from "node:path"
|
||||
import { getOpenCodeCacheDir } from "../../shared/data-path"
|
||||
|
||||
describe("auto-update-checker constants", () => {
|
||||
it("uses the OpenCode cache directory for installed package metadata", async () => {
|
||||
const { CACHE_DIR, INSTALLED_PACKAGE_JSON, PACKAGE_NAME } = await import(`./constants?test=${Date.now()}`)
|
||||
|
||||
expect(CACHE_DIR).toBe(getOpenCodeCacheDir())
|
||||
expect(INSTALLED_PACKAGE_JSON).toBe(
|
||||
join(getOpenCodeCacheDir(), "node_modules", PACKAGE_NAME, "package.json")
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -1,19 +1,13 @@
|
||||
import * as path from "node:path"
|
||||
import * as os from "node:os"
|
||||
import { getOpenCodeConfigDir } from "../../shared"
|
||||
import { getOpenCodeCacheDir } from "../../shared/data-path"
|
||||
import { getOpenCodeConfigDir } from "../../shared/opencode-config-dir"
|
||||
|
||||
export const PACKAGE_NAME = "oh-my-opencode"
|
||||
export const NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`
|
||||
export const NPM_FETCH_TIMEOUT = 5000
|
||||
|
||||
function getCacheDir(): string {
|
||||
if (process.platform === "win32") {
|
||||
return path.join(process.env.LOCALAPPDATA ?? os.homedir(), "opencode")
|
||||
}
|
||||
return path.join(os.homedir(), ".cache", "opencode")
|
||||
}
|
||||
|
||||
export const CACHE_DIR = getCacheDir()
|
||||
export const CACHE_DIR = getOpenCodeCacheDir()
|
||||
export const VERSION_FILE = path.join(CACHE_DIR, "version")
|
||||
|
||||
export function getWindowsAppdataDir(): string | null {
|
||||
@@ -26,7 +20,7 @@ export const USER_OPENCODE_CONFIG = path.join(USER_CONFIG_DIR, "opencode.json")
|
||||
export const USER_OPENCODE_CONFIG_JSONC = path.join(USER_CONFIG_DIR, "opencode.jsonc")
|
||||
|
||||
export const INSTALLED_PACKAGE_JSON = path.join(
|
||||
USER_CONFIG_DIR,
|
||||
CACHE_DIR,
|
||||
"node_modules",
|
||||
PACKAGE_NAME,
|
||||
"package.json"
|
||||
|
||||
Reference in New Issue
Block a user