Skip to content

Commit dfc4012

Browse files
newbe36524Copilot
andcommitted
Fix pruneSourceNativeArtifacts to detect vendor platform containers generically
The previous prebuilds-name-only approach missed vendor/audio-capture/ which uses the same platform-subdir layout but without a 'prebuilds' dir name. New heuristic: a directory is a 'platform container' when ALL its direct subdirectories have compound platform-arch names (e.g. darwin-arm64, linux-x64, arm64-linux, win32-x64) AND there are at least 2 subdirs (prevents false-positives on single-platform optional deps). looksLikeNativePlatformDir() requires both a platform component AND an arch component in the name, so bare 'x64' or 'arm64' don't match, and neither does '@swc/core-win32-x64-msvc' (starts with 'core-' not a platform token). This safely handles the @swc/ scope case that the earlier implementation accidentally broke. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent fd1a76a commit dfc4012

2 files changed

Lines changed: 105 additions & 22 deletions

File tree

packages/code-server/scripts/build-artifacts.mjs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -163,45 +163,56 @@ export async function pruneSourceNativeArtifacts(sourceRoot = codeServerRoot) {
163163
let removedAny = false
164164

165165
async function walkSource(dirPath) {
166-
let entries
166+
let dirents
167167
try {
168-
entries = await readdir(dirPath)
168+
dirents = await readdir(dirPath, { withFileTypes: true })
169169
} catch {
170170
return
171171
}
172172

173-
for (const entry of entries) {
174-
const entryPath = path.join(dirPath, entry)
173+
const subDirNames = dirents.filter((d) => d.isDirectory()).map((d) => d.name)
175174

176-
// Skip vscode-reh-web output directories at lib/ level
177-
if (dirPath === libDir && entry.startsWith("vscode-reh-web-")) {
178-
continue
179-
}
175+
// Skip vscode-reh-web output directories at lib/ level — those are handled separately
176+
const toVisit = subDirNames.filter(
177+
(name) => !(dirPath === libDir && name.startsWith("vscode-reh-web-")),
178+
)
180179

181-
const entryStat = await stat(entryPath).catch(() => null)
182-
if (!entryStat?.isDirectory()) continue
183-
184-
if (entry === "prebuilds") {
185-
// Prune non-Windows platform subdirs from this prebuilds directory
186-
const platformDirs = await readdir(entryPath).catch(() => [])
187-
for (const platformDir of platformDirs) {
188-
if (!shouldKeepWindowsNativeArtifact(platformDir)) {
189-
await rm(path.join(entryPath, platformDir), { recursive: true, force: true })
190-
removedAny = true
191-
}
180+
// A directory is a "platform container" when ALL its subdirectories have compound
181+
// platform-arch names (e.g. darwin-arm64, linux-x64, win32-x64, arm64-linux).
182+
// Require at least 2 to avoid false-positives on single-platform optional deps.
183+
if (toVisit.length >= 2 && toVisit.every(looksLikeNativePlatformDir)) {
184+
for (const name of toVisit) {
185+
if (!shouldKeepWindowsNativeArtifact(name)) {
186+
await rm(path.join(dirPath, name), { recursive: true, force: true })
187+
removedAny = true
192188
}
193-
// Don't recurse into prebuilds
194-
continue
195189
}
190+
return
191+
}
196192

197-
await walkSource(entryPath)
193+
for (const name of toVisit) {
194+
await walkSource(path.join(dirPath, name))
198195
}
199196
}
200197

201198
await walkSource(sourceRoot)
202199
return removedAny
203200
}
204201

202+
// Returns true for compound platform-arch directory names used by prebuild-install,
203+
// node-pre-gyp, and similar tools (e.g. darwin-arm64, linux-x64, arm64-linux, win32-x64).
204+
// Requires both a platform and an arch component to avoid false-positives on names like
205+
// 'x64' or 'arm64' which are used by packages that are NOT native prebuilds containers.
206+
export function looksLikeNativePlatformDir(name) {
207+
return (
208+
/^(darwin|linux|android|freebsd|openbsd|sunos|win32|windows)[-_](arm64|arm|x64|ia32|x86|s390x|ppc64|riscv64|loong64)/.test(
209+
name,
210+
) ||
211+
/^(arm64|arm|x64|ia32|x86)[-_](linux|darwin|win32|windows|musl)/.test(name)
212+
)
213+
}
214+
215+
205216
export async function pruneWindowsNativeArtifacts(
206217
runtimeRoot = path.join(codeServerRoot, `lib/vscode-reh-web-win32-${upstreamArch}`),
207218
) {

packages/code-server/scripts/build-artifacts.test.mjs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import path from "node:path"
66

77
import {
88
copyPackageTemplates,
9+
looksLikeNativePlatformDir,
910
patchBuildVscodeScript,
1011
pruneSourceNativeArtifacts,
1112
pruneWindowsNativeArtifacts,
@@ -228,3 +229,74 @@ test("pruneSourceNativeArtifacts removes non-Windows prebuilds from source tree
228229
await access(path.join(outputPrebuilds, "win32-x64", "computer.node"))
229230
})
230231

232+
test("pruneSourceNativeArtifacts handles vendor/audio-capture style platform containers", async () => {
233+
const sourceRoot = await mkdtemp(path.join(os.tmpdir(), "code-server-source-prune-vendor-"))
234+
const libDir = path.join(sourceRoot, "lib")
235+
236+
// Simulate @anthropic-ai/claude-agent-sdk/vendor/audio-capture platform dirs
237+
const audioCaptureDir = path.join(
238+
sourceRoot,
239+
"lib",
240+
"vscode",
241+
"extensions",
242+
"copilot",
243+
"node_modules",
244+
"@anthropic-ai",
245+
"claude-agent-sdk",
246+
"vendor",
247+
"audio-capture",
248+
)
249+
await mkdir(path.join(audioCaptureDir, "arm64-linux"), { recursive: true })
250+
await mkdir(path.join(audioCaptureDir, "darwin-arm64"), { recursive: true })
251+
await mkdir(path.join(audioCaptureDir, "win32-x64"), { recursive: true })
252+
for (const plat of ["arm64-linux", "darwin-arm64", "win32-x64"]) {
253+
await writeFile(path.join(audioCaptureDir, plat, "audio-capture.node"), `${plat}\n`)
254+
}
255+
256+
// Simulate @swc/ scope — should NOT be treated as a platform container
257+
const swcDir = path.join(
258+
sourceRoot,
259+
"lib",
260+
"vscode",
261+
"extensions",
262+
"copilot",
263+
"node_modules",
264+
"@swc",
265+
)
266+
await mkdir(path.join(swcDir, "core"), { recursive: true })
267+
await mkdir(path.join(swcDir, "helpers"), { recursive: true })
268+
await mkdir(path.join(swcDir, "core-win32-x64-msvc"), { recursive: true })
269+
await writeFile(path.join(swcDir, "core-win32-x64-msvc", "swc.win32.node"), "native\n")
270+
await writeFile(path.join(swcDir, "helpers", "index.js"), "// helpers\n")
271+
272+
const changed = await pruneSourceNativeArtifacts(sourceRoot)
273+
274+
assert.equal(changed, true)
275+
276+
// audio-capture: Windows kept, non-Windows removed
277+
await access(path.join(audioCaptureDir, "win32-x64", "audio-capture.node"))
278+
await assert.rejects(access(path.join(audioCaptureDir, "arm64-linux", "audio-capture.node")))
279+
await assert.rejects(access(path.join(audioCaptureDir, "darwin-arm64", "audio-capture.node")))
280+
281+
// @swc scope: untouched — core, helpers, and core-win32-x64-msvc all preserved
282+
await access(path.join(swcDir, "helpers", "index.js"))
283+
await access(path.join(swcDir, "core-win32-x64-msvc", "swc.win32.node"))
284+
})
285+
286+
test("looksLikeNativePlatformDir identifies compound platform-arch names", () => {
287+
assert.equal(looksLikeNativePlatformDir("darwin-arm64"), true)
288+
assert.equal(looksLikeNativePlatformDir("darwin-x64"), true)
289+
assert.equal(looksLikeNativePlatformDir("linux-x64"), true)
290+
assert.equal(looksLikeNativePlatformDir("win32-x64"), true)
291+
assert.equal(looksLikeNativePlatformDir("arm64-linux"), true)
292+
assert.equal(looksLikeNativePlatformDir("windows-arm64"), true)
293+
294+
// Single-component names must NOT match
295+
assert.equal(looksLikeNativePlatformDir("x64"), false)
296+
assert.equal(looksLikeNativePlatformDir("arm64"), false)
297+
assert.equal(looksLikeNativePlatformDir("core"), false)
298+
assert.equal(looksLikeNativePlatformDir("helpers"), false)
299+
assert.equal(looksLikeNativePlatformDir("Release"), false)
300+
assert.equal(looksLikeNativePlatformDir("core-win32-x64-msvc"), false)
301+
})
302+

0 commit comments

Comments
 (0)