mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-02 00:09:30 +01:00
Also handle copilot native deps in remote server build (#302603)
* Remote server copilot build fixes * Don't strip copilot from stable builds * Handle all platforms for copilot
This commit is contained in:
@@ -34,6 +34,7 @@ import * as cp from 'child_process';
|
||||
import log from 'fancy-log';
|
||||
import buildfile from './buildfile.ts';
|
||||
import { fetchUrls, fetchGithub } from './lib/fetch.ts';
|
||||
import { getCopilotExcludeFilter, copyCopilotNativeDeps } from './lib/copilot.ts';
|
||||
import jsonEditor from 'gulp-json-editor';
|
||||
|
||||
|
||||
@@ -343,6 +344,7 @@ function packageTask(type: string, platform: string, arch: string, sourceFolderN
|
||||
.pipe(filter(['**', '!**/package-lock.json', '!**/*.{js,css}.map']))
|
||||
.pipe(util.cleanNodeModules(path.join(import.meta.dirname, '.moduleignore')))
|
||||
.pipe(util.cleanNodeModules(path.join(import.meta.dirname, `.moduleignore.${process.platform}`)))
|
||||
.pipe(filter(getCopilotExcludeFilter(platform, arch)))
|
||||
.pipe(jsFilter)
|
||||
.pipe(util.stripSourceMappingURL())
|
||||
.pipe(jsFilter.restore);
|
||||
@@ -461,6 +463,13 @@ function patchWin32DependenciesTask(destinationFolderName: string) {
|
||||
};
|
||||
}
|
||||
|
||||
function copyCopilotNativeDepsTaskREH(platform: string, arch: string, destinationFolderName: string) {
|
||||
return async () => {
|
||||
const nodeModulesDir = path.join(BUILD_ROOT, destinationFolderName, 'node_modules');
|
||||
copyCopilotNativeDeps(platform, arch, nodeModulesDir);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param product The parsed product.json file contents
|
||||
*/
|
||||
@@ -509,7 +518,8 @@ function tweakProductForServerWeb(product: typeof import('../product.json')) {
|
||||
compileNativeExtensionsBuildTask,
|
||||
gulp.task(`node-${platform}-${arch}`) as task.Task,
|
||||
util.rimraf(path.join(BUILD_ROOT, destinationFolderName)),
|
||||
packageTask(type, platform, arch, sourceFolderName, destinationFolderName)
|
||||
packageTask(type, platform, arch, sourceFolderName, destinationFolderName),
|
||||
copyCopilotNativeDepsTaskREH(platform, arch, destinationFolderName)
|
||||
];
|
||||
|
||||
if (platform === 'win32') {
|
||||
|
||||
@@ -31,6 +31,7 @@ import minimist from 'minimist';
|
||||
import { compileBuildWithoutManglingTask, compileBuildWithManglingTask } from './gulpfile.compile.ts';
|
||||
import { compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileAllExtensionsBuildTask, compileExtensionMediaBuildTask, cleanExtensionsBuildTask } from './gulpfile.extensions.ts';
|
||||
import { copyCodiconsTask } from './lib/compilation.ts';
|
||||
import { getCopilotExcludeFilter, copyCopilotNativeDeps } from './lib/copilot.ts';
|
||||
import type { EmbeddedProductInfo } from './lib/embeddedType.ts';
|
||||
import { useEsbuildTranspile } from './buildConfig.ts';
|
||||
import { promisify } from 'util';
|
||||
@@ -318,38 +319,6 @@ function computeChecksum(filename: string): string {
|
||||
return hash;
|
||||
}
|
||||
|
||||
const copilotPlatforms = [
|
||||
'darwin-arm64', 'darwin-x64',
|
||||
'linux-arm64', 'linux-x64',
|
||||
'win32-arm64', 'win32-x64',
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns a glob filter that strips @github/copilot platform packages and
|
||||
* prebuilt native modules for architectures other than the build target.
|
||||
* On stable builds, all copilot SDK dependencies are stripped entirely.
|
||||
*/
|
||||
function getCopilotExcludeFilter(platform: string, arch: string, quality: string | undefined): string[] {
|
||||
const targetPlatformArch = `${platform}-${arch}`;
|
||||
const nonTargetPlatforms = copilotPlatforms.filter(p => p !== targetPlatformArch);
|
||||
|
||||
// Strip wrong-architecture @github/copilot-{platform} packages.
|
||||
// All copilot prebuilds are stripped by .moduleignore; VS Code's own
|
||||
// node-pty is copied into the prebuilds location by a post-packaging task.
|
||||
const excludes = nonTargetPlatforms.map(p => `!**/node_modules/@github/copilot-${p}/**`);
|
||||
|
||||
// Strip agent host SDK dependencies entirely from stable builds
|
||||
if (quality === 'stable') {
|
||||
excludes.push(
|
||||
'!**/node_modules/@github/copilot/**',
|
||||
'!**/node_modules/@github/copilot-sdk/**',
|
||||
'!**/node_modules/@github/copilot-*/**',
|
||||
);
|
||||
}
|
||||
|
||||
return ['**', ...excludes];
|
||||
}
|
||||
|
||||
function packageTask(platform: string, arch: string, sourceFolderName: string, destinationFolderName: string, _opts?: { stats?: boolean }) {
|
||||
const destination = path.join(path.dirname(root), destinationFolderName);
|
||||
platform = platform || process.platform;
|
||||
@@ -469,7 +438,7 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d
|
||||
.pipe(filter(depFilterPattern))
|
||||
.pipe(util.cleanNodeModules(path.join(import.meta.dirname, '.moduleignore')))
|
||||
.pipe(util.cleanNodeModules(path.join(import.meta.dirname, `.moduleignore.${process.platform}`)))
|
||||
.pipe(filter(getCopilotExcludeFilter(platform, arch, quality)))
|
||||
.pipe(filter(getCopilotExcludeFilter(platform, arch)))
|
||||
.pipe(jsFilter)
|
||||
.pipe(util.rewriteSourceMappingURL(sourceMappingURLBase))
|
||||
.pipe(jsFilter.restore)
|
||||
@@ -713,27 +682,10 @@ function patchWin32DependenciesTask(destinationFolderName: string) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies VS Code's own node-pty binaries into the copilot SDK's
|
||||
* expected locations so the copilot CLI subprocess can find them at runtime.
|
||||
* The copilot-bundled prebuilds are stripped by .moduleignore;
|
||||
* this replaces them with the same binaries VS Code already ships, avoiding
|
||||
* new system dependency requirements.
|
||||
*
|
||||
* node-pty: `prebuilds/{platform}-{arch}/` (pty.node + spawn-helper)
|
||||
*/
|
||||
function copyCopilotNativeDepsTask(platform: string, arch: string, destinationFolderName: string) {
|
||||
const outputDir = path.join(path.dirname(root), destinationFolderName);
|
||||
|
||||
return async () => {
|
||||
const quality = (product as { quality?: string }).quality;
|
||||
|
||||
// On stable builds the copilot SDK is stripped entirely -- nothing to copy into.
|
||||
if (quality === 'stable') {
|
||||
console.log(`[copyCopilotNativeDeps] Skipping -- stable build`);
|
||||
return;
|
||||
}
|
||||
|
||||
// On Windows with win32VersionedUpdate, app resources live under a
|
||||
// commit-hash prefix: {output}/{commitHash}/resources/app/
|
||||
const versionedResourcesFolder = util.getVersionedResourcesFolder(platform, commit!);
|
||||
@@ -741,24 +693,7 @@ function copyCopilotNativeDepsTask(platform: string, arch: string, destinationFo
|
||||
? path.join(outputDir, `${product.nameLong}.app`, 'Contents', 'Resources', 'app')
|
||||
: path.join(outputDir, versionedResourcesFolder, 'resources', 'app');
|
||||
|
||||
// Source and destination are both in node_modules/, which exists as a real
|
||||
// directory on disk on all platforms after packaging.
|
||||
const nodeModulesDir = path.join(appBase, 'node_modules');
|
||||
const copilotBase = path.join(nodeModulesDir, '@github', 'copilot');
|
||||
const platformArch = `${platform === 'win32' ? 'win32' : platform}-${arch}`;
|
||||
|
||||
const nodePtySource = path.join(nodeModulesDir, 'node-pty', 'build', 'Release');
|
||||
|
||||
// Fail-fast: source binaries must exist on non-stable builds.
|
||||
if (!fs.existsSync(nodePtySource)) {
|
||||
throw new Error(`[copyCopilotNativeDeps] node-pty source not found at ${nodePtySource}`);
|
||||
}
|
||||
|
||||
// Copy node-pty (pty.node + spawn-helper) into copilot prebuilds
|
||||
const copilotPrebuildsDir = path.join(copilotBase, 'prebuilds', platformArch);
|
||||
fs.mkdirSync(copilotPrebuildsDir, { recursive: true });
|
||||
fs.cpSync(nodePtySource, copilotPrebuildsDir, { recursive: true });
|
||||
console.log(`[copyCopilotNativeDeps] Copied node-pty from ${nodePtySource} to ${copilotPrebuildsDir}`);
|
||||
copyCopilotNativeDeps(platform, arch, path.join(appBase, 'node_modules'));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
110
build/lib/copilot.ts
Normal file
110
build/lib/copilot.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
/**
|
||||
* The platforms that @github/copilot ships platform-specific packages for.
|
||||
* These are the `@github/copilot-{platform}` optional dependency packages.
|
||||
*/
|
||||
export const copilotPlatforms = [
|
||||
'darwin-arm64', 'darwin-x64',
|
||||
'linux-arm64', 'linux-x64',
|
||||
'win32-arm64', 'win32-x64',
|
||||
];
|
||||
|
||||
/**
|
||||
* Converts VS Code build platform/arch to the values that Node.js reports
|
||||
* at runtime via `process.platform` and `process.arch`.
|
||||
*
|
||||
* The copilot SDK's `loadNativeModule` looks up native binaries under
|
||||
* `prebuilds/${process.platform}-${process.arch}/`, so the directory names
|
||||
* must match these runtime values exactly.
|
||||
*/
|
||||
function toNodePlatformArch(platform: string, arch: string): { nodePlatform: string; nodeArch: string } {
|
||||
// alpine is musl-linux; Node still reports process.platform === 'linux'
|
||||
let nodePlatform = platform === 'alpine' ? 'linux' : platform;
|
||||
let nodeArch = arch;
|
||||
|
||||
if (arch === 'armhf') {
|
||||
// VS Code build uses 'armhf'; Node reports process.arch === 'arm'
|
||||
nodeArch = 'arm';
|
||||
} else if (arch === 'alpine') {
|
||||
// Legacy: { platform: 'linux', arch: 'alpine' } means alpine-x64
|
||||
nodePlatform = 'linux';
|
||||
nodeArch = 'x64';
|
||||
}
|
||||
|
||||
return { nodePlatform, nodeArch };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a glob filter that strips @github/copilot platform packages
|
||||
* for architectures other than the build target.
|
||||
*
|
||||
* For platforms the copilot SDK doesn't natively support (e.g. alpine, armhf),
|
||||
* ALL platform packages are stripped - that's fine because the SDK doesn't ship
|
||||
* binaries for those platforms anyway, and we replace them with VS Code's own.
|
||||
*/
|
||||
export function getCopilotExcludeFilter(platform: string, arch: string): string[] {
|
||||
const { nodePlatform, nodeArch } = toNodePlatformArch(platform, arch);
|
||||
const targetPlatformArch = `${nodePlatform}-${nodeArch}`;
|
||||
const nonTargetPlatforms = copilotPlatforms.filter(p => p !== targetPlatformArch);
|
||||
|
||||
// Strip wrong-architecture @github/copilot-{platform} packages.
|
||||
// All copilot prebuilds are stripped by .moduleignore; VS Code's own
|
||||
// node-pty is copied into the prebuilds location by a post-packaging task.
|
||||
const excludes = nonTargetPlatforms.map(p => `!**/node_modules/@github/copilot-${p}/**`);
|
||||
|
||||
return ['**', ...excludes];
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies VS Code's own node-pty binaries into the copilot SDK's
|
||||
* expected locations so the copilot CLI subprocess can find them at runtime.
|
||||
* The copilot-bundled prebuilds are stripped by .moduleignore;
|
||||
* this replaces them with the same binaries VS Code already ships, avoiding
|
||||
* new system dependency requirements.
|
||||
*
|
||||
* This works even for platforms the copilot SDK doesn't natively support
|
||||
* (e.g. alpine, armhf) because the SDK's native module loader simply
|
||||
* looks for `prebuilds/{process.platform}-{process.arch}/pty.node` - it
|
||||
* doesn't validate the platform against a supported list.
|
||||
*
|
||||
* Failures are logged but do not throw, to avoid breaking the build on
|
||||
* platforms where something unexpected happens.
|
||||
*
|
||||
* @param nodeModulesDir Absolute path to the node_modules directory that
|
||||
* contains both the source binaries (node-pty) and the copilot SDK
|
||||
* target directories.
|
||||
*/
|
||||
export function copyCopilotNativeDeps(platform: string, arch: string, nodeModulesDir: string): void {
|
||||
const { nodePlatform, nodeArch } = toNodePlatformArch(platform, arch);
|
||||
const platformArch = `${nodePlatform}-${nodeArch}`;
|
||||
|
||||
const copilotBase = path.join(nodeModulesDir, '@github', 'copilot');
|
||||
if (!fs.existsSync(copilotBase)) {
|
||||
console.warn(`[copyCopilotNativeDeps] @github/copilot not found at ${copilotBase}, skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
const nodePtySource = path.join(nodeModulesDir, 'node-pty', 'build', 'Release');
|
||||
if (!fs.existsSync(nodePtySource)) {
|
||||
console.warn(`[copyCopilotNativeDeps] node-pty source not found at ${nodePtySource}, skipping`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Copy node-pty (pty.node + spawn-helper on Unix, conpty.node + conpty/ on Windows)
|
||||
// into copilot prebuilds so the SDK finds them via loadNativeModule.
|
||||
const copilotPrebuildsDir = path.join(copilotBase, 'prebuilds', platformArch);
|
||||
fs.mkdirSync(copilotPrebuildsDir, { recursive: true });
|
||||
fs.cpSync(nodePtySource, copilotPrebuildsDir, { recursive: true });
|
||||
console.log(`[copyCopilotNativeDeps] Copied node-pty from ${nodePtySource} to ${copilotPrebuildsDir}`);
|
||||
} catch (err) {
|
||||
console.warn(`[copyCopilotNativeDeps] Failed to copy node-pty for ${platformArch}: ${err}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user