"use strict"; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); exports.killTree = exports.onceWithoutRejections = exports.isSubdirectory = exports.streamToBuffer = exports.validateStream = exports.isDefined = exports.resolveCliArgsFromVSCodeExecutablePath = exports.resolveCliPathFromVSCodeExecutablePath = exports.getLatestInsidersMetadata = exports.getInsidersVersionMetadata = exports.insidersDownloadDirMetadata = exports.insidersDownloadDirToExecutablePath = exports.downloadDirToExecutablePath = exports.urlToOptions = exports.getVSCodeDownloadUrl = exports.isStableVersionIdentifier = exports.isInsiderVersionIdentifier = exports.systemDefaultPlatform = void 0; const child_process_1 = require("child_process"); const fs_1 = require("fs"); const createHttpProxyAgent = require("http-proxy-agent"); const createHttpsProxyAgent = require("https-proxy-agent"); const path = require("path"); const url_1 = require("url"); const request = require("./request"); const runTest_1 = require("./runTest"); const crypto_1 = require("crypto"); const windowsPlatforms = new Set(['win32-x64-archive', 'win32-arm64-archive']); const darwinPlatforms = new Set(['darwin-arm64', 'darwin']); switch (process.platform) { case 'darwin': exports.systemDefaultPlatform = process.arch === 'arm64' ? 'darwin-arm64' : 'darwin'; break; case 'win32': exports.systemDefaultPlatform = process.arch === 'arm64' ? 'win32-arm64-archive' : 'win32-x64-archive'; break; default: exports.systemDefaultPlatform = process.arch === 'arm64' ? 'linux-arm64' : process.arch === 'arm' ? 'linux-armhf' : 'linux-x64'; } function isInsiderVersionIdentifier(version) { return version === 'insiders' || version.endsWith('-insider'); // insider or 1.2.3-insider version string } exports.isInsiderVersionIdentifier = isInsiderVersionIdentifier; function isStableVersionIdentifier(version) { return version === 'stable' || /^[0-9]+\.[0-9]+\.[0-9]$/.test(version); // stable or 1.2.3 version string } exports.isStableVersionIdentifier = isStableVersionIdentifier; function getVSCodeDownloadUrl(version, platform = exports.systemDefaultPlatform) { if (version === 'insiders') { return `https://update.code.visualstudio.com/latest/${platform}/insider`; } else if (isInsiderVersionIdentifier(version)) { return `https://update.code.visualstudio.com/${version}/${platform}/insider`; } else if (isStableVersionIdentifier(version)) { return `https://update.code.visualstudio.com/${version}/${platform}/stable`; } else { // insiders commit hash return `https://update.code.visualstudio.com/commit:${version}/${platform}/insider`; } } exports.getVSCodeDownloadUrl = getVSCodeDownloadUrl; let PROXY_AGENT = undefined; let HTTPS_PROXY_AGENT = undefined; if (process.env.npm_config_proxy) { PROXY_AGENT = createHttpProxyAgent(process.env.npm_config_proxy); HTTPS_PROXY_AGENT = createHttpsProxyAgent(process.env.npm_config_proxy); } if (process.env.npm_config_https_proxy) { HTTPS_PROXY_AGENT = createHttpsProxyAgent(process.env.npm_config_https_proxy); } function urlToOptions(url) { const parsed = new url_1.URL(url); const options = {}; if (PROXY_AGENT && parsed.protocol.startsWith('http:')) { options.agent = PROXY_AGENT; } if (HTTPS_PROXY_AGENT && parsed.protocol.startsWith('https:')) { options.agent = HTTPS_PROXY_AGENT; } return options; } exports.urlToOptions = urlToOptions; function downloadDirToExecutablePath(dir, platform) { if (windowsPlatforms.has(platform)) { return path.resolve(dir, 'Code.exe'); } else if (darwinPlatforms.has(platform)) { return path.resolve(dir, 'Visual Studio Code.app/Contents/MacOS/Electron'); } else { return path.resolve(dir, 'code'); } } exports.downloadDirToExecutablePath = downloadDirToExecutablePath; function insidersDownloadDirToExecutablePath(dir, platform) { if (windowsPlatforms.has(platform)) { return path.resolve(dir, 'Code - Insiders.exe'); } else if (darwinPlatforms.has(platform)) { return path.resolve(dir, 'Visual Studio Code - Insiders.app/Contents/MacOS/Electron'); } else { return path.resolve(dir, 'code-insiders'); } } exports.insidersDownloadDirToExecutablePath = insidersDownloadDirToExecutablePath; function insidersDownloadDirMetadata(dir, platform) { let productJsonPath; if (windowsPlatforms.has(platform)) { productJsonPath = path.resolve(dir, 'resources/app/product.json'); } else if (darwinPlatforms.has(platform)) { productJsonPath = path.resolve(dir, 'Visual Studio Code - Insiders.app/Contents/Resources/app/product.json'); } else { productJsonPath = path.resolve(dir, 'resources/app/product.json'); } const productJson = JSON.parse(fs_1.readFileSync(productJsonPath, 'utf-8')); return { version: productJson.commit, date: new Date(productJson.date), }; } exports.insidersDownloadDirMetadata = insidersDownloadDirMetadata; async function getInsidersVersionMetadata(platform, version) { const remoteUrl = `https://update.code.visualstudio.com/api/versions/${version}/${platform}/insider`; return await request.getJSON(remoteUrl, 30000); } exports.getInsidersVersionMetadata = getInsidersVersionMetadata; async function getLatestInsidersMetadata(platform) { const remoteUrl = `https://update.code.visualstudio.com/api/update/${platform}/insider/latest`; return await request.getJSON(remoteUrl, 30000); } exports.getLatestInsidersMetadata = getLatestInsidersMetadata; /** * Resolve the VS Code cli path from executable path returned from `downloadAndUnzipVSCode`. * Usually you will want {@link resolveCliArgsFromVSCodeExecutablePath} instead. */ function resolveCliPathFromVSCodeExecutablePath(vscodeExecutablePath, platform = exports.systemDefaultPlatform) { if (platform === 'win32-archive') { throw new Error('Windows 32-bit is no longer supported'); } if (windowsPlatforms.has(platform)) { if (vscodeExecutablePath.endsWith('Code - Insiders.exe')) { return path.resolve(vscodeExecutablePath, '../bin/code-insiders.cmd'); } else { return path.resolve(vscodeExecutablePath, '../bin/code.cmd'); } } else if (darwinPlatforms.has(platform)) { return path.resolve(vscodeExecutablePath, '../../../Contents/Resources/app/bin/code'); } else { if (vscodeExecutablePath.endsWith('code-insiders')) { return path.resolve(vscodeExecutablePath, '../bin/code-insiders'); } else { return path.resolve(vscodeExecutablePath, '../bin/code'); } } } exports.resolveCliPathFromVSCodeExecutablePath = resolveCliPathFromVSCodeExecutablePath; /** * Resolve the VS Code cli arguments from executable path returned from `downloadAndUnzipVSCode`. * You can use this path to spawn processes for extension management. For example: * * ```ts * const cp = require('child_process'); * const { downloadAndUnzipVSCode, resolveCliArgsFromVSCodeExecutablePath } = require('@vscode/test-electron') * const vscodeExecutablePath = await downloadAndUnzipVSCode('1.36.0'); * const [cli, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath); * * cp.spawnSync(cli, [...args, '--install-extension', ''], { * encoding: 'utf-8', * stdio: 'inherit' * }); * ``` * * @param vscodeExecutablePath The `vscodeExecutablePath` from `downloadAndUnzipVSCode`. */ function resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath, options) { var _a; const args = [ resolveCliPathFromVSCodeExecutablePath(vscodeExecutablePath, (_a = options === null || options === void 0 ? void 0 : options.platform) !== null && _a !== void 0 ? _a : exports.systemDefaultPlatform), ]; if (!(options === null || options === void 0 ? void 0 : options.reuseMachineInstall)) { args.push(...runTest_1.getProfileArguments(args)); } return args; } exports.resolveCliArgsFromVSCodeExecutablePath = resolveCliArgsFromVSCodeExecutablePath; /** Predicates whether arg is undefined or null */ function isDefined(arg) { return arg != null; } exports.isDefined = isDefined; /** * Validates the stream data matches the given length and checksum, if any. * * Note: md5 is not ideal, but it's what we get from the CDN, and for the * purposes of self-reported content verification is sufficient. */ function validateStream(readable, length, sha256) { let actualLen = 0; const checksum = sha256 ? crypto_1.createHash('sha256') : undefined; return new Promise((resolve, reject) => { readable.on('data', (chunk) => { checksum === null || checksum === void 0 ? void 0 : checksum.update(chunk); actualLen += chunk.length; }); readable.on('error', reject); readable.on('end', () => { if (actualLen !== length) { return reject(new Error(`Downloaded stream length ${actualLen} does not match expected length ${length}`)); } const digest = checksum === null || checksum === void 0 ? void 0 : checksum.digest('hex'); if (digest && digest !== sha256) { return reject(new Error(`Downloaded file checksum ${digest} does not match expected checksum ${sha256}`)); } resolve(); }); }); } exports.validateStream = validateStream; /** Gets a Buffer from a Node.js stream */ function streamToBuffer(readable) { return new Promise((resolve, reject) => { const chunks = []; readable.on('data', (chunk) => chunks.push(chunk)); readable.on('error', reject); readable.on('end', () => resolve(Buffer.concat(chunks))); }); } exports.streamToBuffer = streamToBuffer; /** Gets whether child is a subdirectory of the parent */ function isSubdirectory(parent, child) { const relative = path.relative(parent, child); return !relative.startsWith('..') && !path.isAbsolute(relative); } exports.isSubdirectory = isSubdirectory; /** * Wraps a function so that it's called once, and never again, memoizing * the result unless it rejects. */ function onceWithoutRejections(fn) { let value; return (...args) => { if (!value) { value = fn(...args).catch((err) => { value = undefined; throw err; }); } return value; }; } exports.onceWithoutRejections = onceWithoutRejections; function killTree(processId, force) { let cp; if (process.platform === 'win32') { const windir = process.env['WINDIR'] || 'C:\\Windows'; // when killing a process in Windows its child processes are *not* killed but become root processes. // Therefore we use TASKKILL.EXE cp = child_process_1.spawn(path.join(windir, 'System32', 'taskkill.exe'), [...(force ? ['/F'] : []), '/T', '/PID', processId.toString()], { stdio: 'inherit' }); } else { // on linux and OS X we kill all direct and indirect child processes as well cp = child_process_1.spawn('sh', [path.resolve(__dirname, '../killTree.sh'), processId.toString(), force ? '9' : '15'], { stdio: 'inherit', }); } return new Promise((resolve, reject) => { cp.on('error', reject).on('exit', resolve); }); } exports.killTree = killTree;