Skip to content

Commit be537ce

Browse files
authored
Optimize builtin copilot extension (#7757)
1 parent cc8f0b7 commit be537ce

3 files changed

Lines changed: 165 additions & 1 deletion

File tree

ci/build/build-vscode.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ EOF
108108
) > product.json
109109

110110

111-
VSCODE_QUALITY=stable npm run gulp compile-copilot-extension-build
111+
VSCODE_QUALITY=stable npm run gulp compile-copilot-extension-full-build
112112

113113
npm run gulp core-ci
114114
npm run gulp "vscode-reh-web-$VSCODE_TARGET${MINIFY:+-min}-ci"

patches/copilot.diff

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
Index: code-server/lib/vscode/build/gulpfile.extensions.ts
2+
===================================================================
3+
--- code-server.orig/lib/vscode/build/gulpfile.extensions.ts
4+
+++ code-server/lib/vscode/build/gulpfile.extensions.ts
5+
@@ -287,6 +287,29 @@ export const compileCopilotExtensionBuil
6+
gulp.task(compileCopilotExtensionBuildTask);
7+
8+
/**
9+
+ * Compiles the built-in copilot extension with proper `.vscodeignore` filtering
10+
+ * and materializes native dependency shims (`node-pty`, `ripgrep`).
11+
+ * Produces output equivalent to what CI ships from the pre-built VSIX.
12+
+ *
13+
+ * The result is placed in `.build/extensions/copilot/` and can be copied
14+
+ * directly into a VS Code Insiders installation at:
15+
+ * `<insiders>/resources/app/extensions/copilot/`
16+
+ */
17+
+export const compileCopilotExtensionFullBuildTask = task.define('compile-copilot-extension-full-build', task.series(
18+
+ // Step 1: Clean previous copilot build output
19+
+ task.define('clean-copilot-build', util.rimraf('.build/extensions/copilot')),
20+
+ // Step 2: Build and package with proper `.vscodeignore` filtering
21+
+ task.define('package-copilot-extension-full', () => ext.packageCopilotExtensionFullStream().pipe(gulp.dest('.build'))),
22+
+ // Step 3: Materialize native dependency shims (`node-pty`, `ripgrep`)
23+
+ task.define('copilot-extension-native-shims', () => {
24+
+ const copilotExtDir = path.join(root, '.build', 'extensions', 'copilot');
25+
+ ext.prepareCopilotExtensionNativeShims(copilotExtDir);
26+
+ return Promise.resolve();
27+
+ })
28+
+));
29+
+gulp.task(compileCopilotExtensionFullBuildTask);
30+
+
31+
+/**
32+
* Compiles the extensions for the build.
33+
* This is essentially a helper task that combines {@link cleanExtensionsBuildTask}, {@link compileNonNativeExtensionsBuildTask} and {@link compileNativeExtensionsBuildTask}
34+
*/
35+
Index: code-server/lib/vscode/build/lib/extensions.ts
36+
===================================================================
37+
--- code-server.orig/lib/vscode/build/lib/extensions.ts
38+
+++ code-server/lib/vscode/build/lib/extensions.ts
39+
@@ -24,6 +24,7 @@ import { getProductionDependencies } fro
40+
import { type IExtensionDefinition, getExtensionStream } from './builtInExtensions.ts';
41+
import { fetchUrls, fetchGithub } from './fetch.ts';
42+
import { createTsgoStream, spawnTsgo } from './tsgo.ts';
43+
+import { prepareBuiltInCopilotExtensionShims } from './copilot.ts';
44+
import vzip from 'gulp-vinyl-zip';
45+
46+
import { createRequire } from 'module';
47+
@@ -482,6 +483,116 @@ export function packageCopilotExtensionS
48+
).pipe(util2.setExecutableBit(['**/*.sh']));
49+
}
50+
51+
+/**
52+
+ * Package the built-in copilot extension as a properly filtered VSIX-equivalent.
53+
+ * Unlike {@link packageCopilotExtensionStream}, this uses vsce.listFiles with
54+
+ * PackageManager.Npm so that .vscodeignore is respected for dependency filtering,
55+
+ * producing output equivalent to what CI ships from the pre-built VSIX.
56+
+ */
57+
+export function packageCopilotExtensionFullStream(): Stream {
58+
+ const vsce = require('@vscode/vsce') as typeof import('@vscode/vsce');
59+
+ const extensionPath = path.join(root, 'extensions', 'copilot');
60+
+ if (!fs.existsSync(extensionPath)) {
61+
+ return es.readArray([]);
62+
+ }
63+
+
64+
+ const esbuildConfigFileName = '.esbuild.ts';
65+
+ const esbuildScript = path.join(extensionPath, esbuildConfigFileName);
66+
+ if (!fs.existsSync(esbuildScript)) {
67+
+ throw new Error(`Copilot esbuild script not found at ${esbuildScript}`);
68+
+ }
69+
+
70+
+ const result = es.through();
71+
+
72+
+ // Step 1: Run esbuild to compile the extension
73+
+ new Promise<void>((resolve, reject) => {
74+
+ const proc = cp.execFile(process.argv[0], [esbuildScript], { cwd: extensionPath }, (error, _stdout, stderr) => {
75+
+ if (error) {
76+
+ return reject(error);
77+
+ }
78+
+ const matches = (stderr || '').match(/\> (.+): error: (.+)?/g);
79+
+ fancyLog(`Bundled extension: ${ansiColors.yellow(path.join('copilot', esbuildConfigFileName))} with ${matches ? matches.length : 0} errors.`);
80+
+ for (const match of matches || []) {
81+
+ fancyLog.error(match);
82+
+ }
83+
+ return resolve();
84+
+ });
85+
+ proc.stdout!.on('data', (data) => {
86+
+ fancyLog(`${ansiColors.green('esbuilding copilot')}: ${data.toString('utf8')}`);
87+
+ });
88+
+ }).then(() => {
89+
+ // Step 2: Use `vsce.listFiles` with Npm package manager so `.vscodeignore`
90+
+ // is applied to both source files AND `node_modules` dependencies.
91+
+ // This is the key difference from `packageCopilotExtensionStream` which
92+
+ // uses `PackageManager.None` and then blindly merges all production deps.
93+
+ return vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Npm });
94+
+ }).then(fileNames => {
95+
+ const files = fileNames
96+
+ .map(fileName => path.join(extensionPath, fileName))
97+
+ .map(filePath => new File({
98+
+ path: filePath,
99+
+ stat: fs.statSync(filePath),
100+
+ base: extensionPath,
101+
+ contents: fs.createReadStream(filePath)
102+
+ }));
103+
+
104+
+ es.readArray(files).pipe(result);
105+
+ }).catch(err => {
106+
+ console.error('Failed to package copilot extension:', err);
107+
+ result.emit('error', err);
108+
+ });
109+
+
110+
+ // Apply the same package.json cleanup as bundled extensions get
111+
+ const cleaned = updateExtensionPackageJSON(
112+
+ result.pipe(rename(p => p.dirname = `extensions/copilot/${p.dirname}`)),
113+
+ (data: any) => {
114+
+ delete data.scripts;
115+
+ delete data.dependencies;
116+
+ delete data.devDependencies;
117+
+ if (data.main) {
118+
+ data.main = data.main.replace('/out/', '/dist/');
119+
+ }
120+
+ return data;
121+
+ }
122+
+ );
123+
+
124+
+ return minifyExtensionResources(cleaned)
125+
+ .pipe(util2.setExecutableBit(['**/*.sh']));
126+
+}
127+
+
128+
+/**
129+
+ * Materializes native dependency shims (`node-pty`, `ripgrep`) into the copilot
130+
+ * extension output at {@link outputDir}. Uses the root `node_modules` as the
131+
+ * source for native binaries, targeting the current platform/arch.
132+
+ *
133+
+ * This is the equivalent of what {@link copyCopilotNativeDepsTask} in
134+
+ * `gulpfile.vscode.ts` does during a full product build, but scoped to
135+
+ * just the standalone copilot extension output.
136+
+ *
137+
+ * Failures are logged as warnings rather than throwing, since the copilot
138+
+ * extension can still create shims at runtime if they are missing.
139+
+ */
140+
+export function prepareCopilotExtensionNativeShims(outputDir: string): void {
141+
+ const platform = process.platform;
142+
+ const arch = process.arch;
143+
+ const appNodeModulesDir = path.join(root, 'node_modules');
144+
+
145+
+ if (!fs.existsSync(outputDir)) {
146+
+ fancyLog.warn('[prepareCopilotExtensionNativeShims] Copilot extension not found at', outputDir, '- skipping shims');
147+
+ return;
148+
+ }
149+
+
150+
+ try {
151+
+ prepareBuiltInCopilotExtensionShims(platform, arch, outputDir, appNodeModulesDir);
152+
+ fancyLog(`[prepareCopilotExtensionNativeShims] Materialized native shims for ${platform}-${arch}`);
153+
+ } catch (err) {
154+
+ // Downgrade to a warning for local builds since the extension
155+
+ // can still function without shims (it creates them at runtime).
156+
+ fancyLog.warn(`[prepareCopilotExtensionNativeShims] Failed to materialize shims: ${err}`);
157+
+ fancyLog.warn('[prepareCopilotExtensionNativeShims] The extension will still work but will create shims at runtime.');
158+
+ }
159+
+}
160+
+
161+
export function packageMarketplaceExtensionsStream(forWeb: boolean): Stream {
162+
const marketplaceExtensionsDescriptions = [
163+
...builtInExtensions.filter(({ name }) => (forWeb ? !marketplaceWebExtensionsExclude.has(name) : true)),

patches/series

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ clipboard.diff
2222
display-language.diff
2323
trusted-domains.diff
2424
signature-verification.diff
25+
copilot.diff

0 commit comments

Comments
 (0)