Path: blob/main/extensions/copilot/src/extension/chatSessions/copilotcli/vscode-node/test/copilotCLISDKUpgrade.spec.ts
13406 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { promises as fs } from 'fs';6import { isBinaryFile } from 'isbinaryfile';7import * as path from 'path';8import { beforeAll, describe, it } from 'vitest';9import { TestLogService } from '../../../../../platform/testing/common/testLogService';10import { copyRipgrepShim } from '../../../copilotcli/node/ripgrepShim';1112describe('CopilotCLI SDK Upgrade', function () {13const extensionPath = path.join(__dirname, '..', '..', '..', '..', '..', '..');14const copilotSDKPath = path.join(extensionPath, 'node_modules', '@github', 'copilot');15beforeAll(async function () {16await copyBinaries(extensionPath);17});18it('should be able to load the SDK without errors', async function () {19await import('@github/copilot/sdk');20});2122it('should not contain new native binaries nor removed native binaries', async function () {23// This is a very basic check to ensure that when the Copilot CLI SDK is upgraded,24// we are aware of any changes to the native binaries it contains.25// Such changes may require us to update our extension packaging or other handling.26const existingBinaries = new Set(await findAllBinaries(copilotSDKPath));27const knownBinaries = new Set([28// node-pty related files (already accounted for in SDK, using VS Code node-pty).29path.join('prebuilds', 'darwin-arm64', 'pty.node'),30path.join('prebuilds', 'darwin-x64', 'pty.node'),31path.join('prebuilds', 'linux-arm64', 'pty.node'),32path.join('prebuilds', 'linux-x64', 'pty.node'),33path.join('prebuilds', 'win32-arm64', 'conpty', 'OpenConsole.exe'),34path.join('prebuilds', 'win32-arm64', 'conpty', 'conpty.dll'),35path.join('prebuilds', 'win32-arm64', 'conpty.node'),36path.join('prebuilds', 'win32-arm64', 'conpty.pdb'),37path.join('prebuilds', 'win32-arm64', 'conpty_console_list.node'),38path.join('prebuilds', 'win32-arm64', 'conpty_console_list.pdb'),39path.join('prebuilds', 'win32-x64', 'conpty', 'OpenConsole.exe'),40path.join('prebuilds', 'win32-x64', 'conpty', 'conpty.dll'),41path.join('prebuilds', 'win32-x64', 'conpty.node'),42path.join('prebuilds', 'win32-x64', 'conpty.pdb'),43path.join('prebuilds', 'win32-x64', 'conpty_console_list.node'),44path.join('prebuilds', 'win32-x64', 'conpty_console_list.pdb'),45// ripgrep46path.join('ripgrep', 'bin', 'win32-arm64', 'rg.exe'),47path.join('ripgrep', 'bin', 'win32-x64', 'rg.exe'),48path.join('prebuilds', 'darwin-arm64', 'spawn-helper'),49path.join('prebuilds', 'darwin-x64', 'spawn-helper'),50// computer use51path.join('prebuilds', 'darwin-arm64', 'computer.node'),52path.join('prebuilds', 'darwin-x64', 'computer.node'),53path.join('prebuilds', 'linux-arm64', 'computer.node'),54path.join('prebuilds', 'linux-x64', 'computer.node'),55path.join('prebuilds', 'win32-arm64', 'computer.node'),56path.join('prebuilds', 'win32-x64', 'computer.node'),57// win32 native module (formerly win_error_mode)58path.join('prebuilds', 'win32-arm64', 'win32.node'),59path.join('prebuilds', 'win32-x64', 'win32.node'),60// Second copy of computer.node / win32.node re-shipped by the @github/copilot/sdk subpackage61// (previously hidden by a broad sdk/prebuilds/** exclusion that masked the node-pty files we used to shim in at test setup).62path.join('sdk', 'prebuilds', 'darwin-arm64', 'computer.node'),63path.join('sdk', 'prebuilds', 'darwin-x64', 'computer.node'),64path.join('sdk', 'prebuilds', 'linux-arm64', 'computer.node'),65path.join('sdk', 'prebuilds', 'linux-x64', 'computer.node'),66path.join('sdk', 'prebuilds', 'win32-arm64', 'computer.node'),67path.join('sdk', 'prebuilds', 'win32-x64', 'computer.node'),68path.join('sdk', 'prebuilds', 'win32-arm64', 'win32.node'),69path.join('sdk', 'prebuilds', 'win32-x64', 'win32.node'),70path.join('ripgrep', 'bin', 'darwin-arm64', 'rg'),71path.join('ripgrep', 'bin', 'darwin-x64', 'rg'),72path.join('ripgrep', 'bin', 'linux-x64', 'rg'),73path.join('ripgrep', 'bin', 'linux-arm64', 'rg'),74// sharp related files75path.join('sharp', 'node_modules', '@img', 'sharp-wasm32', 'lib', 'sharp-wasm32.node.wasm'),76// sharp related files, files copied by us.77path.join('sdk', 'sharp', 'node_modules', '@img', 'sharp-wasm32', 'lib', 'sharp-wasm32.node.wasm'),78// parsing commands for shell.79'tree-sitter-bash.wasm',80'tree-sitter-powershell.wasm',81'tree-sitter.wasm',82'tree-sitter-c_sharp.wasm',83'tree-sitter-c.wasm',84'tree-sitter-cpp.wasm',85'tree-sitter-css.wasm',86'tree-sitter-html.wasm',87'tree-sitter-java.wasm',88'tree-sitter-php.wasm',89'tree-sitter-go.wasm',90'tree-sitter-json.wasm',91'tree-sitter-javascript.wasm',92'tree-sitter-python.wasm',93'tree-sitter-ruby.wasm',94'tree-sitter-tsx.wasm',95'tree-sitter-rust.wasm',96'tree-sitter-typescript.wasm',97'tree-sitter-scala.wasm',98].map(p => path.join(copilotSDKPath, p)));99100// Exclude ripgrep files that we copy over in src/extension/chatSessions/copilotcli/node/ripgrepShim.ts (until we get better API/solution from SDK)101const ripgrepFilesWeCopy = path.join(copilotSDKPath, 'sdk', 'ripgrep', 'bin');102103const errors: string[] = [];104// Look for new binaries105for (const binary of existingBinaries) {106if (binary.startsWith(ripgrepFilesWeCopy)) {107continue;108}109const binaryName = path.basename(binary);110if (binaryName.startsWith('keytar') || binaryName.startsWith('clipboard')) {111continue;112}113if (!knownBinaries.has(binary)) {114errors.push(`Unexpected native binary found in Copilot CLI SDK: ${path.relative(copilotSDKPath, binary)}`);115}116}117// Look for removed binaries.118for (const binary of knownBinaries) {119if (binary.startsWith(ripgrepFilesWeCopy)) {120continue;121}122if (!existingBinaries.has(binary)) {123errors.push(`Expected native binary missing from Copilot CLI SDK: ${path.relative(copilotSDKPath, binary)}`);124}125}126127if (errors.length > 0) {128throw new Error(errors.join('\n'));129}130});131132it('should be able to load the @github/copilot module without errors', async function () {133await import('@github/copilot/sdk');134});135});136137async function copyBinaries(extensionPath: string) {138const copilotSDKPath = path.join(extensionPath, 'node_modules', '@github', 'copilot');139const vscodeRipgrepPath = path.join(copilotSDKPath, 'ripgrep', 'bin', process.platform + '-' + process.arch);140await copyRipgrepShim(extensionPath, vscodeRipgrepPath, new TestLogService());141}142async function findAllBinaries(dir: string): Promise<string[]> {143const binaryFiles: string[] = [];144const filesToIgnore = ['.DS_Store'];145async function findFilesRecursively(dir: string): Promise<void> {146try {147await fs.access(dir);148} catch {149return;150}151152const entries = await fs.readdir(dir, { withFileTypes: true });153await Promise.all(entries.map(async (entry) => {154const fullPath = path.join(dir, entry.name);155if (filesToIgnore.includes(entry.name)) {156return;157}158if (entry.isDirectory()) {159await findFilesRecursively(fullPath);160} else if (entry.isFile()) {161const isBinary = await isBinaryFile(fullPath);162if (isBinary) {163binaryFiles.push(fullPath);164}165}166}));167}168169await findFilesRecursively(dir);170return binaryFiles;171}172173174