mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-17 15:24:40 +01:00
* agent host init * Agent host: Copilot SDK integration with chat UI * Agent host: direct MessagePort, logging, SDK wrapper, env fix * Refactoring and cleanup * Copilot-authored message: Agent-host tool rendering, protocol, and session fixes Tool invocation rendering: - Emit tool_start/tool_complete as ChatToolInvocation (not progressMessage) - Shell tools (bash/powershell) render as terminal command blocks with IChatTerminalToolInvocationData, output, and exit codes - Non-shell tools render via invocationMessage/pastTenseMessage (markdown) - Filter out report_intent (hidden internal tool) Agent-agnostic protocol: - IPC events carry display-ready fields (displayName, invocationMessage, pastTenseMessage, toolInput, toolOutput, toolKind, language) - All Copilot CLI-specific logic in copilotToolDisplay.ts with typed interfaces for known tools (CopilotToolName enum, parameter types) - Renderer never references specific SDK tool names Session fixes: - Resumed sessions show tool invocations in history (getSessionMessages now returns tool events alongside messages) - Fixed 'already has a pending request' on resumed sessions by conditionally providing interruptActiveResponseCallback - Fixed event filtering for resumed sessions (sessionId override in _trackSession) Documentation: - Split parity.md into design.md (decisions) and backlog.md (tasks) - Updated architecture.md, sessions.md with cross-references - Added maintenance notes to all docs * Copilot-authored message: Model picker, session class, DI and test cleanup * Cleanups * stuff * add diagram * Add claude agent * Clean up * Copy some build script changes from #295817 * Simplify * Update docs * Register agent-host via chatSessions contribution API, reduce peripheral diff * Cleanup * Don't ship stuff in stable * Dynamic agent discovery via listAgents() IPC Replace hardcoded per-provider contributions with a single AgentHostContribution that discovers available agents from the agent host process at startup. Each IAgent backend now exposes an IAgentDescriptor with display metadata and auth requirements. - Add IAgentDescriptor interface and listAgents() to IPC contract - CopilotAgent/ClaudeAgent return descriptors via getDescriptor() - Single AgentHostContribution discovers + registers dynamically - Remove agentHostConstants.ts (no more hardcoded session types) - AgentHostSessionListController/LMProvider take params instead - Rename AgentSessionProviders.AgentHost -> AgentHostCopilot - Update architecture.md, sessions.md, backlog.md (Written by Copilot) * Fix review findings: proxy, disposal, filtering, tests - Add listAgents() forwarding to AgentHostServiceClient - Guard async discovery against disposal race - Add provider field to IAgentModelInfo for per-provider filtering - Filter models and sessions by provider in LM provider and list controller - Update tests for new dynamic API and agent-host-copilot scheme (Written by Copilot) * Use DI for AgentHostLanguageModelProvider (Written by Copilot) * Strip @img/sharp native binaries from builds sharp is a transitive dependency of the Claude Agent SDK used for image processing. Its native .node binaries cause dpkg-shlibdeps errors during Debian packaging due to $ORIGIN RPATH references. Strip all @img/sharp-* platform packages since the agent host doesn't need image processing at runtime. (Written by Copilot) * Strip Claude SDK vendored ripgrep binaries The Claude Agent SDK bundles ripgrep binaries for all platforms under vendor/ripgrep/. Wrong-architecture binaries cause macOS Mach-O verification to fail. Strip them entirely via .moduleignore (VS Code has its own ripgrep) and add to verify-macho skip list. (Written by Copilot) * Add tests for AgentSession, AgentService dispatcher, and workbench agent host components (Written by Copilot) * Add trace logging, IPC output channel, tool permissions, and attachment context - Add Agent Host IPC output channel (only registered at trace log level) that logs all IPC method calls, results, and progress events with full JSON payloads - Add trace-level logging in AgentService dispatcher for all method calls - Add trace-level logging in session handler for all progress events and session resolution - Wire up onPermissionRequest handler on CopilotClient.createSession and resumeSession to auto-approve tool permission requests - Add IAgentAttachment type to IPC contract and thread attachments from chat variables (file, directory, selection) through sendMessage to the Copilot SDK (Written by Copilot) * Add tests for attachment context conversion and threading (Written by Copilot) * Add gap analysis docs for Copilot and Claude SDK implementations (Written by Copilot) * Sanitize env vars for Copilot CLI subprocess Strip VSCODE_*, ELECTRON_* (except ELECTRON_RUN_AS_NODE), NODE_OPTIONS, and other debug-related env vars that can interfere with the Node.js process the SDK spawns. Matches the env sanitization from the extension implementation. Also set useStdio and autoStart for proper CLI communication. (Written by Copilot) * Add error, usage, and title_changed event types to IPC contract Add IAgentErrorEvent, IAgentUsageEvent, and IAgentTitleChangedEvent to the progress event union. Wire up session.error and assistant.usage events from the Copilot SDK to fire as IPC events instead of only logging. Handle error events in the renderer session handler by rendering the error message. Usage and title_changed events are logged at trace level. (Written by Copilot) * Add abortSession IPC method for proper cancellation Add abortSession(session) to the IPC contract, implemented across AgentService, CopilotAgent (calls session.abort()), ClaudeAgent (no-op, uses AbortController), and the renderer proxy. Wire up cancellation in the session handler to call abortSession before finishing, so the SDK actually stops processing. (Written by Copilot) * Address reviewer feedback: error finishes request, Claude abort, tests - Error events now call finish() so the request doesn't hang if the SDK doesn't send idle after an error - ClaudeAgent.abortSession calls ClaudeSession.abort() which signals the AbortController and creates a new one for future turns - Add test: cancellation calls abortSession on the agent host service - Add test: error event renders message and finishes the request - Remove stale TODO in interruptActiveResponseCallback - Use timeout() helper instead of raw setTimeout in test - Update gap docs to reflect completed work (Written by Copilot) * Add permission request IPC round-trip (Written by Copilot) * Remove Claude agent from agent-host process Strip the Claude Agent SDK integration from the agent-host utility process to focus on the Copilot SDK path. - Delete src/vs/platform/agent/node/claude/ (claudeAgent, claudeSession, claudeToolDisplay) - Remove @anthropic-ai/claude-agent-sdk from package.json - Remove AgentHostClaude enum member and all switch cases - Remove Claude command registration in electron-browser chat.contribution - Clean up build scripts (.moduleignore, verify-macho, gulpfile.vscode) - Narrow AgentProvider type to just 'copilot' - Update tests and documentation (Written by Copilot) * Wire up permission confirmation UI with ChatToolInvocation (Written by Copilot) * Fix reviewer feedback: safe permission serialization, deny on abort/dispose (Written by Copilot) * Forward reasoning events as thinking blocks (Written by Copilot) * Pass workspace folder as workingDirectory to Copilot SDK (Written by Copilot) * Store and pass workingDirectory on session resume, update gap docs (Written by Copilot) * Fix permission rendering, session-scoped permissions, and test gaps (Written by Copilot) * Auto-approve read permissions inside workspace folder (Written by Copilot) * Move read auto-approve into CopilotAgent where permission policy belongs (Written by Copilot) * Update gap docs (Written by Copilot) * Use log language for IPC output channel, add trace prefix (Written by Copilot) * Add tool rendering gaps to docs (Written by Copilot) * Stringify URIs in IPC output channel for readability (Written by Copilot) * Fix IPC output channel: use log languageId with non-log channel for proper append + syntax highlighting (Written by Copilot) * Fix build errors: add URI import, fix test mock types (Written by Copilot) * Don't localize agent host provider strings (Written by Copilot) * Remove claude-agent-sdk from eslint allowed imports (Written by Copilot) * fix test * initial thoughts * Rename folder to agentHost * Fix paths * Fixes * Fixes for copilot * Fix moduleignore * first working protocol version align more closely with protocol json rpc and some gaps * cleanup * Fix copilot pty.node packaging * Fix test * prebuild packaging * Agenthost server fixes * Update monaco.d.ts * Update docs * Fixes * Build fix * Fix build issues * reduce duplication in side effecting code * fix model switching not working * reduce mock duplication * Build fixes * Copy vscode's node.pty * And ripgrep * And thsi * Ripgrep goes to non-SDK * Skip copy for stable build * Remove outdated script * Build fixes for asar * fix * Add some logging * Fix for windows * Fix * Logs * build: add glob diagnostic for copyCopilotNativeDeps * build: check both node_modules/ and .asar.unpacked/ for source binaries * Fix * Remove excalidraw --------- Co-authored-by: Connor Peet <connor@peet.io> Co-authored-by: Connor Peet <copeet@microsoft.com>
906 lines
34 KiB
TypeScript
906 lines
34 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import gulp from 'gulp';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import es from 'event-stream';
|
|
import vfs from 'vinyl-fs';
|
|
import rename from 'gulp-rename';
|
|
import replace from 'gulp-replace';
|
|
import filter from 'gulp-filter';
|
|
import electron from '@vscode/gulp-electron';
|
|
import jsonEditor from 'gulp-json-editor';
|
|
import * as util from './lib/util.ts';
|
|
import { getVersion } from './lib/getVersion.ts';
|
|
import { readISODate, writeISODate } from './lib/date.ts';
|
|
import * as task from './lib/task.ts';
|
|
import buildfile from './buildfile.ts';
|
|
import * as optimize from './lib/optimize.ts';
|
|
import { inlineMeta } from './lib/inlineMeta.ts';
|
|
import packageJson from '../package.json' with { type: 'json' };
|
|
import product from '../product.json' with { type: 'json' };
|
|
import * as crypto from 'crypto';
|
|
import * as i18n from './lib/i18n.ts';
|
|
import { getProductionDependencies } from './lib/dependencies.ts';
|
|
import { config } from './lib/electron.ts';
|
|
import { createAsar } from './lib/asar.ts';
|
|
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 type { EmbeddedProductInfo } from './lib/embeddedType.ts';
|
|
import { useEsbuildTranspile } from './buildConfig.ts';
|
|
import { promisify } from 'util';
|
|
import globCallback from 'glob';
|
|
import rceditCallback from 'rcedit';
|
|
import * as cp from 'child_process';
|
|
|
|
|
|
const glob = promisify(globCallback);
|
|
const rcedit = promisify(rceditCallback);
|
|
const root = path.dirname(import.meta.dirname);
|
|
const commit = getVersion(root);
|
|
|
|
// Build
|
|
const vscodeEntryPoints = [
|
|
buildfile.workerEditor,
|
|
buildfile.workerExtensionHost,
|
|
buildfile.workerNotebook,
|
|
buildfile.workerLanguageDetection,
|
|
buildfile.workerLocalFileSearch,
|
|
buildfile.workerProfileAnalysis,
|
|
buildfile.workerOutputLinks,
|
|
buildfile.workerBackgroundTokenization,
|
|
buildfile.workbenchDesktop,
|
|
buildfile.code
|
|
].flat();
|
|
|
|
const vscodeResourceIncludes = [
|
|
|
|
// NLS
|
|
'out-build/nls.messages.json',
|
|
'out-build/nls.keys.json',
|
|
|
|
// Workbench
|
|
'out-build/vs/code/electron-browser/workbench/workbench.html',
|
|
'out-build/vs/sessions/electron-browser/sessions.html',
|
|
|
|
// Electron Preload
|
|
'out-build/vs/base/parts/sandbox/electron-browser/preload.js',
|
|
'out-build/vs/base/parts/sandbox/electron-browser/preload-aux.js',
|
|
'out-build/vs/platform/browserView/electron-browser/preload-browserView.js',
|
|
|
|
// Node Scripts
|
|
'out-build/vs/base/node/{terminateProcess.sh,cpuUsage.sh,ps.sh}',
|
|
|
|
// Touchbar
|
|
'out-build/vs/workbench/browser/parts/editor/media/*.png',
|
|
'out-build/vs/workbench/contrib/debug/browser/media/*.png',
|
|
|
|
// External Terminal
|
|
'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt',
|
|
|
|
// Terminal shell integration
|
|
'out-build/vs/workbench/contrib/terminal/common/scripts/*.fish',
|
|
'out-build/vs/workbench/contrib/terminal/common/scripts/*.ps1',
|
|
'out-build/vs/workbench/contrib/terminal/common/scripts/*.psm1',
|
|
'out-build/vs/workbench/contrib/terminal/common/scripts/*.sh',
|
|
'out-build/vs/workbench/contrib/terminal/common/scripts/*.zsh',
|
|
'out-build/vs/workbench/contrib/terminal/common/scripts/psreadline/**',
|
|
|
|
// Accessibility Signals
|
|
'out-build/vs/platform/accessibilitySignal/browser/media/*.mp3',
|
|
|
|
// Welcome
|
|
'out-build/vs/workbench/contrib/welcomeGettingStarted/common/media/**/*.{svg,png}',
|
|
|
|
// Sessions
|
|
'out-build/vs/sessions/contrib/chat/browser/media/*.svg',
|
|
'out-build/vs/sessions/prompts/*.prompt.md',
|
|
'out-build/vs/sessions/skills/**/SKILL.md',
|
|
|
|
// Extensions
|
|
'out-build/vs/workbench/contrib/extensions/browser/media/{theme-icon.png,language-icon.svg}',
|
|
'out-build/vs/workbench/services/extensionManagement/common/media/*.{svg,png}',
|
|
|
|
// Webview
|
|
'out-build/vs/workbench/contrib/webview/browser/pre/*.{js,html}',
|
|
|
|
// Extension Host Worker
|
|
'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html',
|
|
|
|
// Tree Sitter highlights
|
|
'out-build/vs/editor/common/languages/highlights/*.scm',
|
|
|
|
// Tree Sitter injection queries
|
|
'out-build/vs/editor/common/languages/injections/*.scm'
|
|
];
|
|
|
|
const vscodeResources = [
|
|
|
|
// Includes
|
|
...vscodeResourceIncludes,
|
|
|
|
// Excludes
|
|
'!out-build/vs/code/browser/**',
|
|
'!out-build/vs/editor/standalone/**',
|
|
'!out-build/vs/code/**/*-dev.html',
|
|
'!out-build/vs/workbench/contrib/issue/**/*-dev.html',
|
|
'!**/test/**'
|
|
];
|
|
|
|
const bootstrapEntryPoints = [
|
|
'out-build/main.js',
|
|
'out-build/cli.js',
|
|
'out-build/bootstrap-fork.js'
|
|
];
|
|
|
|
const bundleVSCodeTask = task.define('bundle-vscode', task.series(
|
|
util.rimraf('out-vscode'),
|
|
// Optimize: bundles source files automatically based on
|
|
// import statements based on the passed in entry points.
|
|
// In addition, concat window related bootstrap files into
|
|
// a single file.
|
|
optimize.bundleTask(
|
|
{
|
|
out: 'out-vscode',
|
|
esm: {
|
|
src: 'out-build',
|
|
entryPoints: [
|
|
...vscodeEntryPoints,
|
|
...bootstrapEntryPoints
|
|
],
|
|
resources: vscodeResources,
|
|
skipTSBoilerplateRemoval: entryPoint => entryPoint === 'vs/code/electron-browser/workbench/workbench' || entryPoint === 'vs/sessions/electron-browser/sessions'
|
|
}
|
|
}
|
|
)
|
|
));
|
|
gulp.task(bundleVSCodeTask);
|
|
|
|
// esbuild-based bundle tasks (drop-in replacement for bundle-vscode / minify-vscode)
|
|
function runEsbuildTranspile(outDir: string, excludeTests: boolean): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
const scriptPath = path.join(root, 'build/next/index.ts');
|
|
const args = [scriptPath, 'transpile', '--out', outDir];
|
|
if (excludeTests) {
|
|
args.push('--exclude-tests');
|
|
}
|
|
|
|
const proc = cp.spawn(process.execPath, args, {
|
|
cwd: root,
|
|
stdio: 'inherit'
|
|
});
|
|
|
|
proc.on('error', reject);
|
|
proc.on('close', code => {
|
|
if (code === 0) {
|
|
resolve();
|
|
} else {
|
|
reject(new Error(`esbuild transpile failed with exit code ${code} (outDir: ${outDir})`));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function runEsbuildBundle(outDir: string, minify: boolean, nls: boolean, target: 'desktop' | 'server' | 'server-web' = 'desktop', sourceMapBaseUrl?: string): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
// const tsxPath = path.join(root, 'build/node_modules/tsx/dist/cli.mjs');
|
|
const scriptPath = path.join(root, 'build/next/index.ts');
|
|
const args = [scriptPath, 'bundle', '--out', outDir, '--target', target];
|
|
if (minify) {
|
|
args.push('--minify');
|
|
args.push('--mangle-privates');
|
|
}
|
|
if (nls) {
|
|
args.push('--nls');
|
|
}
|
|
if (sourceMapBaseUrl) {
|
|
args.push('--source-map-base-url', sourceMapBaseUrl);
|
|
}
|
|
|
|
const proc = cp.spawn(process.execPath, args, {
|
|
cwd: root,
|
|
stdio: 'inherit'
|
|
});
|
|
|
|
proc.on('error', reject);
|
|
proc.on('close', code => {
|
|
if (code === 0) {
|
|
resolve();
|
|
} else {
|
|
reject(new Error(`esbuild bundle failed with exit code ${code} (outDir: ${outDir}, minify: ${minify}, nls: ${nls}, target: ${target})`));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function runTsGoTypeCheck(): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
const proc = cp.spawn('tsgo', ['--project', 'src/tsconfig.json', '--noEmit', '--skipLibCheck'], {
|
|
cwd: root,
|
|
stdio: 'inherit',
|
|
shell: true
|
|
});
|
|
|
|
proc.on('error', reject);
|
|
proc.on('close', code => {
|
|
if (code === 0) {
|
|
resolve();
|
|
} else {
|
|
reject(new Error(`tsgo typecheck failed with exit code ${code}`));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`;
|
|
const isCI = !!process.env['CI'] || !!process.env['BUILD_ARTIFACTSTAGINGDIRECTORY'] || !!process.env['GITHUB_WORKSPACE'];
|
|
const useCdnSourceMapsForPackagingTasks = isCI;
|
|
const stripSourceMapsInPackagingTasks = isCI;
|
|
const minifyVSCodeTask = task.define('minify-vscode', task.series(
|
|
bundleVSCodeTask,
|
|
util.rimraf('out-vscode-min'),
|
|
optimize.minifyTask('out-vscode', `${sourceMappingURLBase}/core`)
|
|
));
|
|
gulp.task(minifyVSCodeTask);
|
|
|
|
gulp.task(task.define('core-ci-old', task.series(
|
|
gulp.task('compile-build-with-mangling') as task.Task,
|
|
task.parallel(
|
|
gulp.task('minify-vscode') as task.Task,
|
|
gulp.task('minify-vscode-reh') as task.Task,
|
|
gulp.task('minify-vscode-reh-web') as task.Task,
|
|
)
|
|
)));
|
|
|
|
gulp.task(task.define('core-ci', task.series(
|
|
copyCodiconsTask,
|
|
compileNonNativeExtensionsBuildTask,
|
|
compileExtensionMediaBuildTask,
|
|
writeISODate('out-build'),
|
|
// Type-check with tsgo (no emit)
|
|
task.define('tsgo-typecheck', () => runTsGoTypeCheck()),
|
|
// Transpile individual files to out-build first (for unit tests)
|
|
task.define('esbuild-out-build', () => runEsbuildTranspile('out-build', false)),
|
|
// Then bundle for shipping (bundles also write NLS files to out-build)
|
|
task.parallel(
|
|
task.define('esbuild-vscode-min', () => runEsbuildBundle('out-vscode-min', true, true, 'desktop', `${sourceMappingURLBase}/core`)),
|
|
task.define('esbuild-vscode-reh-min', () => runEsbuildBundle('out-vscode-reh-min', true, true, 'server', `${sourceMappingURLBase}/core`)),
|
|
task.define('esbuild-vscode-reh-web-min', () => runEsbuildBundle('out-vscode-reh-web-min', true, true, 'server-web', `${sourceMappingURLBase}/core`)),
|
|
)
|
|
)));
|
|
|
|
const coreCIPR = task.define('core-ci-pr', task.series(
|
|
gulp.task('compile-build-without-mangling') as task.Task,
|
|
task.parallel(
|
|
gulp.task('minify-vscode') as task.Task,
|
|
gulp.task('minify-vscode-reh') as task.Task,
|
|
gulp.task('minify-vscode-reh-web') as task.Task,
|
|
)
|
|
));
|
|
gulp.task(coreCIPR);
|
|
|
|
/**
|
|
* Compute checksums for some files.
|
|
*
|
|
* @param out The out folder to read the file from.
|
|
* @param filenames The paths to compute a checksum for.
|
|
* @return A map of paths to checksums.
|
|
*/
|
|
function computeChecksums(out: string, filenames: string[]): Record<string, string> {
|
|
const result: Record<string, string> = {};
|
|
filenames.forEach(function (filename) {
|
|
const fullPath = path.join(process.cwd(), out, filename);
|
|
result[filename] = computeChecksum(fullPath);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Compute checksums for a file.
|
|
*
|
|
* @param filename The absolute path to a filename.
|
|
* @return The checksum for `filename`.
|
|
*/
|
|
function computeChecksum(filename: string): string {
|
|
const contents = fs.readFileSync(filename);
|
|
|
|
const hash = crypto
|
|
.createHash('sha256')
|
|
.update(contents)
|
|
.digest('base64')
|
|
.replace(/=+$/, '');
|
|
|
|
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;
|
|
|
|
const task = () => {
|
|
const out = sourceFolderName;
|
|
const versionedResourcesFolder = util.getVersionedResourcesFolder(platform, commit!);
|
|
|
|
const checksums = computeChecksums(out, [
|
|
'vs/base/parts/sandbox/electron-browser/preload.js',
|
|
'vs/workbench/workbench.desktop.main.js',
|
|
'vs/workbench/workbench.desktop.main.css',
|
|
'vs/workbench/api/node/extensionHostProcess.js',
|
|
'vs/code/electron-browser/workbench/workbench.html',
|
|
'vs/code/electron-browser/workbench/workbench.js',
|
|
'vs/sessions/sessions.desktop.main.js',
|
|
'vs/sessions/sessions.desktop.main.css',
|
|
'vs/sessions/electron-browser/sessions.html',
|
|
'vs/sessions/electron-browser/sessions.js'
|
|
]);
|
|
|
|
const src = gulp.src(out + '/**', { base: '.' })
|
|
.pipe(rename(function (path) { path.dirname = path.dirname!.replace(new RegExp('^' + out), 'out'); }))
|
|
.pipe(util.setExecutableBit(['**/*.sh']));
|
|
|
|
const platformSpecificBuiltInExtensionsExclusions = product.builtInExtensions.filter(ext => {
|
|
if (!(ext as { platforms?: string[] }).platforms) {
|
|
return false;
|
|
}
|
|
|
|
const set = new Set((ext as { platforms?: string[] }).platforms);
|
|
return !set.has(platform);
|
|
}).map(ext => `!.build/extensions/${ext.name}/**`);
|
|
|
|
const extensions = gulp.src(['.build/extensions/**', ...platformSpecificBuiltInExtensionsExclusions], { base: '.build', dot: true });
|
|
|
|
const sourceFilterPattern = stripSourceMapsInPackagingTasks
|
|
? ['**', '!**/*.{js,css}.map']
|
|
: ['**'];
|
|
const sources = es.merge(src, extensions)
|
|
.pipe(filter(sourceFilterPattern, { dot: true }));
|
|
|
|
let version = packageJson.version;
|
|
const quality = (product as { quality?: string }).quality;
|
|
|
|
if (quality && quality !== 'stable') {
|
|
version += '-' + quality;
|
|
}
|
|
|
|
const name = product.nameShort;
|
|
const packageJsonUpdates: Record<string, unknown> = { name, version };
|
|
|
|
if (platform === 'linux') {
|
|
packageJsonUpdates.desktopName = `${product.applicationName}.desktop`;
|
|
}
|
|
|
|
let packageJsonContents: string;
|
|
const packageJsonStream = gulp.src(['package.json'], { base: '.' })
|
|
.pipe(jsonEditor(packageJsonUpdates))
|
|
.pipe(es.through(function (file) {
|
|
packageJsonContents = file.contents.toString();
|
|
this.emit('data', file);
|
|
}));
|
|
|
|
let productJsonContents: string;
|
|
const productJsonStream = gulp.src(['product.json'], { base: '.' })
|
|
.pipe(jsonEditor({ commit, date: readISODate(out), checksums, version }))
|
|
.pipe(es.through(function (file) {
|
|
productJsonContents = file.contents.toString();
|
|
this.emit('data', file);
|
|
}));
|
|
|
|
|
|
const isInsiderOrExploration = quality === 'insider' || quality === 'exploration';
|
|
const embedded = isInsiderOrExploration
|
|
? (product as typeof product & { embedded?: EmbeddedProductInfo }).embedded
|
|
: undefined;
|
|
|
|
const packageSubJsonStream = isInsiderOrExploration
|
|
? gulp.src(['package.json'], { base: '.' })
|
|
.pipe(jsonEditor((json: Record<string, unknown>) => {
|
|
json.name = `sessions-${quality || 'oss-dev'}`;
|
|
return json;
|
|
}))
|
|
.pipe(rename('package.sub.json'))
|
|
: undefined;
|
|
|
|
const productSubJsonStream = embedded
|
|
? gulp.src(['product.json'], { base: '.' })
|
|
.pipe(jsonEditor((json: Record<string, unknown>) => {
|
|
Object.keys(embedded).forEach(key => {
|
|
json[key] = embedded[key as keyof EmbeddedProductInfo];
|
|
});
|
|
return json;
|
|
}))
|
|
.pipe(rename('product.sub.json'))
|
|
: undefined;
|
|
|
|
const license = gulp.src([product.licenseFileName, 'ThirdPartyNotices.txt', 'licenses/**'], { base: '.', allowEmpty: true });
|
|
|
|
// TODO the API should be copied to `out` during compile, not here
|
|
const api = gulp.src('src/vscode-dts/vscode.d.ts').pipe(rename('out/vscode-dts/vscode.d.ts'));
|
|
|
|
const telemetry = gulp.src('.build/telemetry/**', { base: '.build/telemetry', dot: true });
|
|
|
|
const jsFilter = util.filter(data => !data.isDirectory() && /\.js$/.test(data.path));
|
|
const root = path.resolve(path.join(import.meta.dirname, '..'));
|
|
const productionDependencies = getProductionDependencies(root);
|
|
const dependenciesSrc = productionDependencies.map(d => path.relative(root, d)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`]).flat().concat('!**/*.mk');
|
|
|
|
const depFilterPattern = ['**', `!**/${config.version}/**`, '!**/bin/darwin-arm64-87/**', '!**/package-lock.json', '!**/yarn.lock'];
|
|
if (stripSourceMapsInPackagingTasks) {
|
|
depFilterPattern.push('!**/*.{js,css}.map');
|
|
}
|
|
|
|
const deps = gulp.src(dependenciesSrc, { base: '.', dot: true })
|
|
.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(jsFilter)
|
|
.pipe(util.rewriteSourceMappingURL(sourceMappingURLBase))
|
|
.pipe(jsFilter.restore)
|
|
.pipe(createAsar(path.join(process.cwd(), 'node_modules'), [
|
|
'**/*.node',
|
|
'**/@vscode/ripgrep/bin/*',
|
|
'**/@github/copilot-*/**',
|
|
'**/node-pty/build/Release/*',
|
|
'**/node-pty/build/Release/conpty/*',
|
|
'**/node-pty/lib/worker/conoutSocketWorker.js',
|
|
'**/node-pty/lib/shared/conout.js',
|
|
'**/*.wasm',
|
|
'**/@vscode/vsce-sign/bin/*',
|
|
], [
|
|
'**/*.mk',
|
|
'!node_modules/vsda/**' // stay compatible with extensions that depend on us shipping `vsda` into ASAR
|
|
], [
|
|
'node_modules/vsda/**' // retain copy of `vsda` in node_modules for internal use
|
|
], 'node_modules.asar'));
|
|
|
|
const mergeStreams = [
|
|
packageJsonStream,
|
|
productJsonStream,
|
|
license,
|
|
api,
|
|
telemetry,
|
|
sources,
|
|
deps
|
|
];
|
|
if (packageSubJsonStream) {
|
|
mergeStreams.push(packageSubJsonStream);
|
|
}
|
|
if (productSubJsonStream) {
|
|
mergeStreams.push(productSubJsonStream);
|
|
}
|
|
let all = es.merge(...mergeStreams);
|
|
|
|
if (platform === 'win32') {
|
|
all = es.merge(all, gulp.src([
|
|
'resources/win32/bower.ico',
|
|
'resources/win32/c.ico',
|
|
'resources/win32/code.ico',
|
|
'resources/win32/config.ico',
|
|
'resources/win32/cpp.ico',
|
|
'resources/win32/csharp.ico',
|
|
'resources/win32/css.ico',
|
|
'resources/win32/default.ico',
|
|
'resources/win32/go.ico',
|
|
'resources/win32/html.ico',
|
|
'resources/win32/jade.ico',
|
|
'resources/win32/java.ico',
|
|
'resources/win32/javascript.ico',
|
|
'resources/win32/json.ico',
|
|
'resources/win32/less.ico',
|
|
'resources/win32/markdown.ico',
|
|
'resources/win32/php.ico',
|
|
'resources/win32/powershell.ico',
|
|
'resources/win32/python.ico',
|
|
'resources/win32/react.ico',
|
|
'resources/win32/ruby.ico',
|
|
'resources/win32/sass.ico',
|
|
'resources/win32/shell.ico',
|
|
'resources/win32/sql.ico',
|
|
'resources/win32/typescript.ico',
|
|
'resources/win32/vue.ico',
|
|
'resources/win32/xml.ico',
|
|
'resources/win32/yaml.ico',
|
|
'resources/win32/code_70x70.png',
|
|
'resources/win32/code_150x150.png'
|
|
], { base: '.' }));
|
|
if (embedded) {
|
|
all = es.merge(all, gulp.src('resources/win32/sessions.ico', { base: '.' }));
|
|
}
|
|
} else if (platform === 'linux') {
|
|
const policyDest = gulp.src('.build/policies/linux/**', { base: '.build/policies/linux' })
|
|
.pipe(rename(f => f.dirname = `policies/${f.dirname}`));
|
|
all = es.merge(all, gulp.src('resources/linux/code.png', { base: '.' }), policyDest);
|
|
} else if (platform === 'darwin') {
|
|
const shortcut = gulp.src('resources/darwin/bin/code.sh')
|
|
.pipe(replace('@@APPNAME@@', product.applicationName))
|
|
.pipe(replace('@@NAME@@', product.nameShort))
|
|
.pipe(rename('bin/code'));
|
|
const policyDest = gulp.src('.build/policies/darwin/**', { base: '.build/policies/darwin' })
|
|
.pipe(rename(f => f.dirname = `policies/${f.dirname}`));
|
|
all = es.merge(all, shortcut, policyDest);
|
|
}
|
|
|
|
const electronConfig = {
|
|
...config,
|
|
platform,
|
|
arch: arch === 'armhf' ? 'arm' : arch,
|
|
ffmpegChromium: false,
|
|
...(embedded ? {
|
|
darwinMiniAppName: embedded.nameShort,
|
|
darwinMiniAppBundleIdentifier: embedded.darwinBundleIdentifier,
|
|
darwinMiniAppIcon: 'resources/darwin/sessions.icns',
|
|
darwinMiniAppAssetsCar: 'resources/darwin/sessions.car',
|
|
darwinMiniAppBundleURLTypes: [{
|
|
role: 'Viewer',
|
|
name: embedded.nameLong,
|
|
urlSchemes: [embedded.urlProtocol]
|
|
}],
|
|
win32ProxyAppName: embedded.nameShort,
|
|
win32ProxyIcon: 'resources/win32/sessions.ico',
|
|
} : {})
|
|
};
|
|
|
|
let result: NodeJS.ReadWriteStream = all
|
|
.pipe(util.skipDirectories())
|
|
.pipe(util.fixWin32DirectoryPermissions())
|
|
.pipe(filter(['**', '!**/.github/**'], { dot: true })) // https://github.com/microsoft/vscode/issues/116523
|
|
.pipe(electron(electronConfig))
|
|
.pipe(filter([
|
|
'**',
|
|
'!LICENSE',
|
|
'!version',
|
|
...(platform === 'darwin' && !isInsiderOrExploration ? ['!**/Contents/Applications', '!**/Contents/Applications/**'] : []),
|
|
...(platform === 'win32' && !isInsiderOrExploration ? ['!**/electron_proxy.exe'] : []),
|
|
], { dot: true }));
|
|
|
|
if (platform === 'linux') {
|
|
result = es.merge(result, gulp.src('resources/completions/bash/code', { base: '.' })
|
|
.pipe(replace('@@APPNAME@@', product.applicationName))
|
|
.pipe(rename(function (f) { f.basename = product.applicationName; })));
|
|
|
|
result = es.merge(result, gulp.src('resources/completions/zsh/_code', { base: '.' })
|
|
.pipe(replace('@@APPNAME@@', product.applicationName))
|
|
.pipe(rename(function (f) { f.basename = '_' + product.applicationName; })));
|
|
}
|
|
|
|
if (platform === 'win32') {
|
|
result = es.merge(result, gulp.src('resources/win32/bin/code.js', { base: 'resources/win32', allowEmpty: true }));
|
|
|
|
if (versionedResourcesFolder) {
|
|
result = es.merge(result, gulp.src('resources/win32/versioned/bin/code.cmd', { base: 'resources/win32/versioned' })
|
|
.pipe(replace('@@NAME@@', product.nameShort))
|
|
.pipe(replace('@@VERSIONFOLDER@@', versionedResourcesFolder))
|
|
.pipe(rename(function (f) { f.basename = product.applicationName; })));
|
|
|
|
result = es.merge(result, gulp.src('resources/win32/versioned/bin/code.sh', { base: 'resources/win32/versioned' })
|
|
.pipe(replace('@@NAME@@', product.nameShort))
|
|
.pipe(replace('@@PRODNAME@@', product.nameLong))
|
|
.pipe(replace('@@VERSION@@', version))
|
|
.pipe(replace('@@COMMIT@@', String(commit)))
|
|
.pipe(replace('@@APPNAME@@', product.applicationName))
|
|
.pipe(replace('@@VERSIONFOLDER@@', versionedResourcesFolder))
|
|
.pipe(replace('@@SERVERDATAFOLDER@@', product.serverDataFolderName || '.vscode-remote'))
|
|
.pipe(replace('@@QUALITY@@', quality!))
|
|
.pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; })));
|
|
} else {
|
|
result = es.merge(result, gulp.src('resources/win32/bin/code.cmd', { base: 'resources/win32' })
|
|
.pipe(replace('@@NAME@@', product.nameShort))
|
|
.pipe(rename(function (f) { f.basename = product.applicationName; })));
|
|
|
|
result = es.merge(result, gulp.src('resources/win32/bin/code.sh', { base: 'resources/win32' })
|
|
.pipe(replace('@@NAME@@', product.nameShort))
|
|
.pipe(replace('@@PRODNAME@@', product.nameLong))
|
|
.pipe(replace('@@VERSION@@', version))
|
|
.pipe(replace('@@COMMIT@@', String(commit)))
|
|
.pipe(replace('@@APPNAME@@', product.applicationName))
|
|
.pipe(replace('@@SERVERDATAFOLDER@@', product.serverDataFolderName || '.vscode-remote'))
|
|
.pipe(replace('@@QUALITY@@', String(quality)))
|
|
.pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; })));
|
|
}
|
|
|
|
result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' })
|
|
.pipe(replace('@@VERSIONFOLDER@@', versionedResourcesFolder ? `${versionedResourcesFolder}\\` : ''))
|
|
.pipe(rename(product.nameShort + '.VisualElementsManifest.xml')));
|
|
|
|
result = es.merge(result, gulp.src('.build/policies/win32/**', { base: '.build/policies/win32' })
|
|
.pipe(rename(f => f.dirname = `policies/${f.dirname}`)));
|
|
|
|
if (quality === 'stable' || quality === 'insider') {
|
|
result = es.merge(result, gulp.src('.build/win32/appx/**', { base: '.build/win32' }));
|
|
const rawVersion = version.replace(/-\w+$/, '').split('.');
|
|
const appxVersion = `${rawVersion[0]}.0.${rawVersion[1]}.${rawVersion[2]}`;
|
|
result = es.merge(result, gulp.src('resources/win32/appx/AppxManifest.xml', { base: '.' })
|
|
.pipe(replace('@@AppxPackageName@@', product.win32AppUserModelId))
|
|
.pipe(replace('@@AppxPackageVersion@@', appxVersion))
|
|
.pipe(replace('@@AppxPackageDisplayName@@', product.nameLong))
|
|
.pipe(replace('@@AppxPackageDescription@@', product.win32NameVersion))
|
|
.pipe(replace('@@ApplicationIdShort@@', product.win32RegValueName))
|
|
.pipe(replace('@@ApplicationExe@@', product.nameShort + '.exe'))
|
|
.pipe(replace('@@FileExplorerContextMenuID@@', quality === 'stable' ? 'OpenWithCode' : 'OpenWithCodeInsiders'))
|
|
.pipe(replace('@@FileExplorerContextMenuCLSID@@', (product as { win32ContextMenu?: Record<string, { clsid: string }> }).win32ContextMenu![arch].clsid))
|
|
.pipe(replace('@@FileExplorerContextMenuDLL@@', `${quality === 'stable' ? 'code' : 'code_insider'}_explorer_command_${arch}.dll`))
|
|
.pipe(rename(f => f.dirname = `appx/manifest`)));
|
|
}
|
|
} else if (platform === 'linux') {
|
|
result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' })
|
|
.pipe(replace('@@PRODNAME@@', product.nameLong))
|
|
.pipe(replace('@@APPNAME@@', product.applicationName))
|
|
.pipe(rename('bin/' + product.applicationName)));
|
|
}
|
|
|
|
result = inlineMeta(result, {
|
|
targetPaths: bootstrapEntryPoints,
|
|
packageJsonFn: () => packageJsonContents,
|
|
productJsonFn: () => productJsonContents
|
|
});
|
|
|
|
return result.pipe(vfs.dest(destination));
|
|
};
|
|
task.taskName = `package-${platform}-${arch}`;
|
|
return task;
|
|
}
|
|
|
|
function patchWin32DependenciesTask(destinationFolderName: string) {
|
|
const cwd = path.join(path.dirname(root), destinationFolderName);
|
|
|
|
return async () => {
|
|
const versionedResourcesFolder = util.getVersionedResourcesFolder('win32', commit!);
|
|
const deps = (await Promise.all([
|
|
glob('**/*.node', { cwd, ignore: 'extensions/node_modules/@parcel/watcher/**' }),
|
|
glob('**/rg.exe', { cwd }),
|
|
glob('**/*explorer_command*.dll', { cwd }),
|
|
])).flatMap(o => o);
|
|
const packageJson = JSON.parse(await fs.promises.readFile(path.join(cwd, versionedResourcesFolder, 'resources', 'app', 'package.json'), 'utf8'));
|
|
const product = JSON.parse(await fs.promises.readFile(path.join(cwd, versionedResourcesFolder, 'resources', 'app', 'product.json'), 'utf8'));
|
|
const baseVersion = packageJson.version.replace(/-.*$/, '');
|
|
|
|
const patchPromises = deps.map<Promise<unknown>>(async dep => {
|
|
const basename = path.basename(dep);
|
|
|
|
await rcedit(path.join(cwd, dep), {
|
|
'file-version': baseVersion,
|
|
'version-string': {
|
|
'CompanyName': 'Microsoft Corporation',
|
|
'FileDescription': product.nameLong,
|
|
'FileVersion': packageJson.version,
|
|
'InternalName': basename,
|
|
'LegalCopyright': 'Copyright (C) 2026 Microsoft. All rights reserved',
|
|
'OriginalFilename': basename,
|
|
'ProductName': product.nameLong,
|
|
'ProductVersion': packageJson.version,
|
|
}
|
|
});
|
|
});
|
|
|
|
await Promise.all(patchPromises);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Copies VS Code's own node-pty and ripgrep binaries into the copilot SDK's
|
|
* expected locations so the copilot CLI subprocess can find them at runtime.
|
|
* The copilot-bundled prebuilds and ripgrep 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)
|
|
* ripgrep: `ripgrep/bin/{platform}-{arch}/` (rg binary)
|
|
*/
|
|
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!);
|
|
const appBase = platform === 'darwin'
|
|
? 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');
|
|
const rgBinary = platform === 'win32' ? 'rg.exe' : 'rg';
|
|
const ripgrepSource = path.join(nodeModulesDir, '@vscode', 'ripgrep', 'bin', rgBinary);
|
|
|
|
// 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}`);
|
|
}
|
|
if (!fs.existsSync(ripgrepSource)) {
|
|
throw new Error(`[copyCopilotNativeDeps] ripgrep source not found at ${ripgrepSource}`);
|
|
}
|
|
|
|
// 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}`);
|
|
|
|
// Copy ripgrep (rg binary) into copilot ripgrep
|
|
const copilotRipgrepDir = path.join(copilotBase, 'ripgrep', 'bin', platformArch);
|
|
fs.mkdirSync(copilotRipgrepDir, { recursive: true });
|
|
fs.copyFileSync(ripgrepSource, path.join(copilotRipgrepDir, rgBinary));
|
|
console.log(`[copyCopilotNativeDeps] Copied ripgrep from ${ripgrepSource} to ${copilotRipgrepDir}`);
|
|
};
|
|
}
|
|
|
|
const buildRoot = path.dirname(root);
|
|
|
|
const BUILD_TARGETS = [
|
|
{ platform: 'win32', arch: 'x64' },
|
|
{ platform: 'win32', arch: 'arm64' },
|
|
{ platform: 'darwin', arch: 'x64', opts: { stats: true } },
|
|
{ platform: 'darwin', arch: 'arm64', opts: { stats: true } },
|
|
{ platform: 'linux', arch: 'x64' },
|
|
{ platform: 'linux', arch: 'armhf' },
|
|
{ platform: 'linux', arch: 'arm64' },
|
|
];
|
|
BUILD_TARGETS.forEach(buildTarget => {
|
|
const dashed = (str: string) => (str ? `-${str}` : ``);
|
|
const platform = buildTarget.platform;
|
|
const arch = buildTarget.arch;
|
|
const opts = buildTarget.opts;
|
|
|
|
const [vscode, vscodeMin] = ['', 'min'].map(minified => {
|
|
const sourceFolderName = `out-vscode${dashed(minified)}`;
|
|
const destinationFolderName = `VSCode${dashed(platform)}${dashed(arch)}`;
|
|
|
|
const packageTasks: task.Task[] = [
|
|
compileNativeExtensionsBuildTask,
|
|
util.rimraf(path.join(buildRoot, destinationFolderName)),
|
|
packageTask(platform, arch, sourceFolderName, destinationFolderName, opts),
|
|
copyCopilotNativeDepsTask(platform, arch, destinationFolderName)
|
|
];
|
|
|
|
if (platform === 'win32') {
|
|
packageTasks.push(patchWin32DependenciesTask(destinationFolderName));
|
|
}
|
|
|
|
const vscodeTaskCI = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}-ci`, task.series(...packageTasks));
|
|
gulp.task(vscodeTaskCI);
|
|
|
|
let vscodeTask: task.Task;
|
|
if (useEsbuildTranspile) {
|
|
const esbuildBundleTask = task.define(
|
|
`esbuild-bundle${dashed(platform)}${dashed(arch)}${dashed(minified)}`,
|
|
() => runEsbuildBundle(
|
|
sourceFolderName,
|
|
!!minified,
|
|
true,
|
|
'desktop',
|
|
minified && useCdnSourceMapsForPackagingTasks ? `${sourceMappingURLBase}/core` : undefined
|
|
)
|
|
);
|
|
vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(
|
|
copyCodiconsTask,
|
|
cleanExtensionsBuildTask,
|
|
compileNonNativeExtensionsBuildTask,
|
|
compileExtensionMediaBuildTask,
|
|
writeISODate('out-build'),
|
|
esbuildBundleTask,
|
|
vscodeTaskCI
|
|
));
|
|
} else {
|
|
vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series(
|
|
minified ? compileBuildWithManglingTask : compileBuildWithoutManglingTask,
|
|
cleanExtensionsBuildTask,
|
|
compileNonNativeExtensionsBuildTask,
|
|
compileExtensionMediaBuildTask,
|
|
minified ? minifyVSCodeTask : bundleVSCodeTask,
|
|
vscodeTaskCI
|
|
));
|
|
}
|
|
gulp.task(vscodeTask);
|
|
|
|
return vscodeTask;
|
|
});
|
|
|
|
if (process.platform === platform && process.arch === arch) {
|
|
gulp.task(task.define('vscode', task.series(vscode)));
|
|
gulp.task(task.define('vscode-min', task.series(vscodeMin)));
|
|
}
|
|
});
|
|
|
|
// #region nls
|
|
|
|
const innoSetupConfig: Record<string, { codePage: string; defaultInfo?: { name: string; id: string } }> = {
|
|
'zh-cn': { codePage: 'CP936', defaultInfo: { name: 'Simplified Chinese', id: '$0804', } },
|
|
'zh-tw': { codePage: 'CP950', defaultInfo: { name: 'Traditional Chinese', id: '$0404' } },
|
|
'ko': { codePage: 'CP949', defaultInfo: { name: 'Korean', id: '$0412' } },
|
|
'ja': { codePage: 'CP932' },
|
|
'de': { codePage: 'CP1252' },
|
|
'fr': { codePage: 'CP1252' },
|
|
'es': { codePage: 'CP1252' },
|
|
'ru': { codePage: 'CP1251' },
|
|
'it': { codePage: 'CP1252' },
|
|
'pt-br': { codePage: 'CP1252' },
|
|
'hu': { codePage: 'CP1250' },
|
|
'tr': { codePage: 'CP1254' }
|
|
};
|
|
|
|
gulp.task(task.define(
|
|
'vscode-translations-export',
|
|
task.series(
|
|
gulp.task('core-ci') as task.Task,
|
|
compileAllExtensionsBuildTask,
|
|
function () {
|
|
const pathToMetadata = './out-build/nls.metadata.json';
|
|
const pathToExtensions = '.build/extensions/*';
|
|
const pathToSetup = 'build/win32/i18n/messages.en.isl';
|
|
|
|
return es.merge(
|
|
gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()),
|
|
gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()),
|
|
gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions())
|
|
).pipe(vfs.dest('../vscode-translations-export'));
|
|
}
|
|
)
|
|
));
|
|
|
|
gulp.task('vscode-translations-import', function () {
|
|
const options = minimist(process.argv.slice(2), {
|
|
string: 'location',
|
|
default: {
|
|
location: '../vscode-translations-import'
|
|
}
|
|
});
|
|
return es.merge([...i18n.defaultLanguages, ...i18n.extraLanguages].map(language => {
|
|
const id = language.id;
|
|
return gulp.src(`${options.location}/${id}/vscode-setup/messages.xlf`)
|
|
.pipe(i18n.prepareIslFiles(language, innoSetupConfig[language.id]))
|
|
.pipe(vfs.dest(`./build/win32/i18n`));
|
|
}));
|
|
});
|
|
|
|
// #endregion
|