Merge pull request #2707 from MoerAI/fix/windows-symlink-config

fix(windows): resolve symlinked config paths and plugin name parsing (fixes #2271)
This commit is contained in:
YeonGyu-Kim
2026-03-26 08:54:45 +09:00
committed by GitHub
5 changed files with 192 additions and 14 deletions
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, it } from "bun:test"
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"
import { mkdirSync, mkdtempSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs"
import { tmpdir } from "node:os"
import { dirname, join } from "node:path"
@@ -104,6 +104,31 @@ describe("system loaded version", () => {
expect(loadedVersion.expectedVersion).toBe("2.3.4")
expect(loadedVersion.loadedVersion).toBe("2.3.4")
})
it("resolves symlinked config directories before selecting install path", () => {
//#given
const realConfigDir = createTemporaryDirectory("omo-real-config-")
const symlinkBaseDir = createTemporaryDirectory("omo-symlink-base-")
const symlinkConfigDir = join(symlinkBaseDir, "config-link")
symlinkSync(realConfigDir, symlinkConfigDir, process.platform === "win32" ? "junction" : "dir")
process.env.OPENCODE_CONFIG_DIR = symlinkConfigDir
writeJson(join(realConfigDir, "package.json"), {
dependencies: { [PACKAGE_NAME]: "4.5.6" },
})
writeJson(join(realConfigDir, "node_modules", PACKAGE_NAME, "package.json"), {
version: "4.5.6",
})
//#when
const loadedVersion = getLoadedPluginVersion()
//#then
expect(loadedVersion.cacheDir).toBe(realpathSync(symlinkConfigDir))
expect(loadedVersion.expectedVersion).toBe("4.5.6")
expect(loadedVersion.loadedVersion).toBe("4.5.6")
})
})
describe("getSuggestedInstallTag", () => {
+16 -5
View File
@@ -1,4 +1,4 @@
import { existsSync, readFileSync } from "node:fs"
import { existsSync, readFileSync, realpathSync } from "node:fs"
import { homedir } from "node:os"
import { join } from "node:path"
@@ -36,6 +36,16 @@ function resolveOpenCodeCacheDir(): string {
return platformDefault
}
function resolveExistingDir(dirPath: string): string {
if (!existsSync(dirPath)) return dirPath
try {
return realpathSync(dirPath)
} catch {
return dirPath
}
}
function readPackageJson(filePath: string): PackageJsonShape | null {
if (!existsSync(filePath)) return null
@@ -55,12 +65,13 @@ function normalizeVersion(value: string | undefined): string | null {
export function getLoadedPluginVersion(): LoadedVersionInfo {
const configPaths = getOpenCodeConfigPaths({ binary: "opencode" })
const cacheDir = resolveOpenCodeCacheDir()
const configDir = resolveExistingDir(configPaths.configDir)
const cacheDir = resolveExistingDir(resolveOpenCodeCacheDir())
const candidates = [
{
cacheDir: configPaths.configDir,
cachePackagePath: configPaths.packageJson,
installedPackagePath: join(configPaths.configDir, "node_modules", PACKAGE_NAME, "package.json"),
cacheDir: configDir,
cachePackagePath: join(configDir, "package.json"),
installedPackagePath: join(configDir, "node_modules", PACKAGE_NAME, "package.json"),
},
{
cacheDir,