Skip to content

Commit c9326fc

Browse files
thdxrgreptile-apps[bot]Brendonovichrekram1-node
authored
refactor: replace BunProc with Npm module using @npmcli/arborist (#18308)
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> Co-authored-by: Brendan Allan <git@brendonovich.dev> Co-authored-by: Aiden Cline <aidenpcline@gmail.com>
1 parent d7481f4 commit c9326fc

14 files changed

Lines changed: 901 additions & 1114 deletions

File tree

bun.lock

Lines changed: 618 additions & 464 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/opencode/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"@types/bun": "catalog:",
5454
"@types/cross-spawn": "6.0.6",
5555
"@types/mime-types": "3.0.1",
56+
"@types/npmcli__arborist": "6.3.3",
5657
"@types/semver": "^7.5.8",
5758
"@types/turndown": "5.0.5",
5859
"@types/which": "3.0.4",
@@ -94,6 +95,7 @@
9495
"@hono/standard-validator": "0.1.5",
9596
"@hono/zod-validator": "catalog:",
9697
"@modelcontextprotocol/sdk": "1.27.1",
98+
"@npmcli/arborist": "9.4.0",
9799
"@octokit/graphql": "9.0.2",
98100
"@octokit/rest": "catalog:",
99101
"@openauthjs/openauth": "catalog:",

packages/opencode/src/bun/index.ts

Lines changed: 0 additions & 129 deletions
This file was deleted.

packages/opencode/src/bun/registry.ts

Lines changed: 0 additions & 50 deletions
This file was deleted.

packages/opencode/src/config/config.ts

Lines changed: 4 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,25 @@ import {
2020
} from "jsonc-parser"
2121
import { Instance, type InstanceContext } from "../project/instance"
2222
import { LSPServer } from "../lsp/server"
23-
import { BunProc } from "@/bun"
2423
import { Installation } from "@/installation"
2524
import { ConfigMarkdown } from "./markdown"
2625
import { constants, existsSync } from "fs"
2726
import { Bus } from "@/bus"
2827
import { GlobalBus } from "@/bus/global"
2928
import { Event } from "../server/event"
3029
import { Glob } from "../util/glob"
31-
import { PackageRegistry } from "@/bun/registry"
32-
import { online, proxied } from "@/util/network"
3330
import { iife } from "@/util/iife"
3431
import { Account } from "@/account"
3532
import { isRecord } from "@/util/record"
3633
import { ConfigPaths } from "./paths"
3734
import { Filesystem } from "@/util/filesystem"
38-
import { Process } from "@/util/process"
3935
import { AppFileSystem } from "@/filesystem"
4036
import { InstanceState } from "@/effect/instance-state"
4137
import { makeRuntime } from "@/effect/run-service"
4238
import { Duration, Effect, Layer, Option, ServiceMap } from "effect"
4339
import { Flock } from "@/util/flock"
4440
import { isPathPluginSpec, parsePluginSpecifier, resolvePathPluginTarget } from "@/plugin/shared"
41+
import { Npm } from "@/npm"
4542

4643
export namespace Config {
4744
const ModelId = z.string().meta({ $ref: "https://models.dev/model-schema.json#/$defs/Model" })
@@ -90,8 +87,7 @@ export namespace Config {
9087
}
9188

9289
export async function installDependencies(dir: string, input?: InstallInput) {
93-
if (!(await needsInstall(dir))) return
94-
90+
if (!(await isWritable(dir))) return
9591
await using _ = await Flock.acquire(`config-install:${Filesystem.resolve(dir)}`, {
9692
signal: input?.signal,
9793
onWait: (tick) =>
@@ -102,13 +98,10 @@ export namespace Config {
10298
waited: tick.waited,
10399
}),
104100
})
105-
106101
input?.signal?.throwIfAborted()
107-
if (!(await needsInstall(dir))) return
108102

109103
const pkg = path.join(dir, "package.json")
110104
const target = Installation.isLocal() ? "*" : Installation.VERSION
111-
112105
const json = await Filesystem.readJson<{ dependencies?: Record<string, string> }>(pkg).catch(() => ({
113106
dependencies: {},
114107
}))
@@ -126,49 +119,7 @@ export namespace Config {
126119
["node_modules", "package.json", "package-lock.json", "bun.lock", ".gitignore"].join("\n"),
127120
)
128121
}
129-
130-
// Bun can race cache writes on Windows when installs run in parallel across dirs.
131-
// Serialize installs globally on win32, but keep parallel installs on other platforms.
132-
await using __ =
133-
process.platform === "win32"
134-
? await Flock.acquire("config-install:bun", {
135-
signal: input?.signal,
136-
})
137-
: undefined
138-
139-
await BunProc.run(
140-
[
141-
"install",
142-
// TODO: get rid of this case (see: https://github.com/oven-sh/bun/issues/19936)
143-
...(proxied() || process.env.CI ? ["--no-cache"] : []),
144-
],
145-
{
146-
cwd: dir,
147-
abort: input?.signal,
148-
},
149-
).catch((err) => {
150-
if (err instanceof Process.RunFailedError) {
151-
const detail = {
152-
dir,
153-
cmd: err.cmd,
154-
code: err.code,
155-
stdout: err.stdout.toString(),
156-
stderr: err.stderr.toString(),
157-
}
158-
if (Flag.OPENCODE_STRICT_CONFIG_DEPS) {
159-
log.error("failed to install dependencies", detail)
160-
throw err
161-
}
162-
log.warn("failed to install dependencies", detail)
163-
return
164-
}
165-
166-
if (Flag.OPENCODE_STRICT_CONFIG_DEPS) {
167-
log.error("failed to install dependencies", { dir, error: err })
168-
throw err
169-
}
170-
log.warn("failed to install dependencies", { dir, error: err })
171-
})
122+
await Npm.install(dir)
172123
}
173124

174125
async function isWritable(dir: string) {
@@ -180,42 +131,6 @@ export namespace Config {
180131
}
181132
}
182133

183-
export async function needsInstall(dir: string) {
184-
// Some config dirs may be read-only.
185-
// Installing deps there will fail; skip installation in that case.
186-
const writable = await isWritable(dir)
187-
if (!writable) {
188-
log.debug("config dir is not writable, skipping dependency install", { dir })
189-
return false
190-
}
191-
192-
const mod = path.join(dir, "node_modules", "@opencode-ai", "plugin")
193-
if (!existsSync(mod)) return true
194-
195-
const pkg = path.join(dir, "package.json")
196-
const pkgExists = await Filesystem.exists(pkg)
197-
if (!pkgExists) return true
198-
199-
const parsed = await Filesystem.readJson<{ dependencies?: Record<string, string> }>(pkg).catch(() => null)
200-
const dependencies = parsed?.dependencies ?? {}
201-
const depVersion = dependencies["@opencode-ai/plugin"]
202-
if (!depVersion) return true
203-
204-
const targetVersion = Installation.isLocal() ? "latest" : Installation.VERSION
205-
if (targetVersion === "latest") {
206-
if (!online()) return false
207-
const stale = await PackageRegistry.isOutdated("@opencode-ai/plugin", depVersion, dir)
208-
if (!stale) return false
209-
log.info("Cached version is outdated, proceeding with install", {
210-
pkg: "@opencode-ai/plugin",
211-
cachedVersion: depVersion,
212-
})
213-
return true
214-
}
215-
if (depVersion === targetVersion) return false
216-
return true
217-
}
218-
219134
function rel(item: string, patterns: string[]) {
220135
const normalizedItem = item.replaceAll("\\", "/")
221136
for (const pattern of patterns) {
@@ -1355,8 +1270,7 @@ export namespace Config {
13551270
}
13561271

13571272
const dep = iife(async () => {
1358-
const stale = await needsInstall(dir)
1359-
if (stale) await installDependencies(dir)
1273+
await installDependencies(dir)
13601274
})
13611275
void dep.catch((err) => {
13621276
log.warn("background dependency install failed", { dir, error: err })

packages/opencode/src/format/formatter.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { text } from "node:stream/consumers"
2-
import { BunProc } from "../bun"
32
import { Instance } from "../project/instance"
43
import { Filesystem } from "../util/filesystem"
54
import { Process } from "../util/process"
@@ -34,7 +33,7 @@ export const mix: Info = {
3433

3534
export const prettier: Info = {
3635
name: "prettier",
37-
command: [BunProc.which(), "x", "prettier", "--write", "$FILE"],
36+
command: ["bun", "x", "prettier", "--write", "$FILE"],
3837
environment: {
3938
BUN_BE_BUN: "1",
4039
},
@@ -82,7 +81,7 @@ export const prettier: Info = {
8281

8382
export const oxfmt: Info = {
8483
name: "oxfmt",
85-
command: [BunProc.which(), "x", "oxfmt", "$FILE"],
84+
command: ["bun", "x", "oxfmt", "$FILE"],
8685
environment: {
8786
BUN_BE_BUN: "1",
8887
},
@@ -104,7 +103,7 @@ export const oxfmt: Info = {
104103

105104
export const biome: Info = {
106105
name: "biome",
107-
command: [BunProc.which(), "x", "@biomejs/biome", "check", "--write", "$FILE"],
106+
command: ["bun", "x", "@biomejs/biome", "check", "--write", "$FILE"],
108107
environment: {
109108
BUN_BE_BUN: "1",
110109
},

0 commit comments

Comments
 (0)