mirror of
https://github.com/coder/code-server.git
synced 2025-07-29 21:12:58 +08:00
chore(vscode): update to 1.53.2
These conflicts will be resolved in the following commits. We do it this way so that PR review is possible.
This commit is contained in:
@ -189,10 +189,10 @@ function factory(nodeRequire, path, fs, perf) {
|
||||
|
||||
const initialLocale = locale;
|
||||
|
||||
perf.mark('nlsGeneration:start');
|
||||
perf.mark('code/willGenerateNls');
|
||||
|
||||
const defaultResult = function (locale) {
|
||||
perf.mark('nlsGeneration:end');
|
||||
perf.mark('code/didGenerateNls');
|
||||
return Promise.resolve({ locale: locale, availableLanguages: {} });
|
||||
};
|
||||
try {
|
||||
@ -243,7 +243,7 @@ function factory(nodeRequire, path, fs, perf) {
|
||||
if (fileExists) {
|
||||
// We don't wait for this. No big harm if we can't touch
|
||||
touch(coreLocation).catch(() => { });
|
||||
perf.mark('nlsGeneration:end');
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}
|
||||
return mkdirp(coreLocation).then(() => {
|
||||
@ -282,7 +282,7 @@ function factory(nodeRequire, path, fs, perf) {
|
||||
writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations)));
|
||||
return Promise.all(writes);
|
||||
}).then(() => {
|
||||
perf.mark('nlsGeneration:end');
|
||||
perf.mark('code/didGenerateNls');
|
||||
return result;
|
||||
}).catch(err => {
|
||||
console.error('Generating translation files failed.', err);
|
||||
|
@ -3,11 +3,11 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import * as fs from 'fs';
|
||||
import * as os from 'os';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { isMacintosh, isWindows } from 'vs/base/common/platform';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { promisify } from 'util';
|
||||
import { isRootOrDriveLetter } from 'vs/base/common/extpath';
|
||||
@ -74,8 +74,8 @@ async function rimrafUnlink(path: string): Promise<void> {
|
||||
|
||||
// chmod as needed to allow for unlink
|
||||
const mode = stat.mode;
|
||||
if (!(mode & 128)) { // 128 === 0200
|
||||
await chmod(path, mode | 128);
|
||||
if (!(mode & fs.constants.S_IWUSR)) {
|
||||
await chmod(path, mode | fs.constants.S_IWUSR);
|
||||
}
|
||||
|
||||
return unlink(path);
|
||||
@ -89,7 +89,7 @@ async function rimrafUnlink(path: string): Promise<void> {
|
||||
|
||||
async function rimrafMove(path: string): Promise<void> {
|
||||
try {
|
||||
const pathInTemp = join(os.tmpdir(), generateUuid());
|
||||
const pathInTemp = join(tmpdir(), generateUuid());
|
||||
try {
|
||||
await rename(path, pathInTemp);
|
||||
} catch (error) {
|
||||
@ -129,8 +129,8 @@ export function rimrafSync(path: string): void {
|
||||
|
||||
// chmod as needed to allow for unlink
|
||||
const mode = stat.mode;
|
||||
if (!(mode & 128)) { // 128 === 0200
|
||||
fs.chmodSync(path, mode | 128);
|
||||
if (!(mode & fs.constants.S_IWUSR)) {
|
||||
fs.chmodSync(path, mode | fs.constants.S_IWUSR);
|
||||
}
|
||||
|
||||
return fs.unlinkSync(path);
|
||||
@ -151,7 +151,7 @@ export async function readdirWithFileTypes(path: string): Promise<fs.Dirent[]> {
|
||||
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
if (platform.isMacintosh) {
|
||||
if (isMacintosh) {
|
||||
for (const child of children) {
|
||||
child.name = normalizeNFC(child.name);
|
||||
}
|
||||
@ -167,7 +167,7 @@ export function readdirSync(path: string): string[] {
|
||||
function handleDirectoryChildren(children: string[]): string[] {
|
||||
// Mac: uses NFD unicode form on disk, but we want NFC
|
||||
// See also https://github.com/nodejs/node/issues/2165
|
||||
if (platform.isMacintosh) {
|
||||
if (isMacintosh) {
|
||||
return children.map(child => normalizeNFC(child));
|
||||
}
|
||||
|
||||
@ -231,6 +231,25 @@ export async function statLink(path: string): Promise<IStatAndLink> {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
}
|
||||
|
||||
// Windows: workaround a node.js bug where reparse points
|
||||
// are not supported (https://github.com/nodejs/node/issues/36790)
|
||||
if (isWindows && error.code === 'EACCES' && lstats) {
|
||||
try {
|
||||
const stats = await stat(await readlink(path));
|
||||
|
||||
return { stat: stats, symbolicLink: lstats.isSymbolicLink() ? { dangling: false } : undefined };
|
||||
} catch (error) {
|
||||
|
||||
// If the link points to a non-existing file we still want
|
||||
// to return it as result while setting dangling: true flag
|
||||
if (error.code === 'ENOENT') {
|
||||
return { stat: lstats, symbolicLink: { dangling: true } };
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@ -247,6 +266,10 @@ export function renameIgnoreError(oldPath: string, newPath: string): Promise<voi
|
||||
return new Promise(resolve => fs.rename(oldPath, newPath, () => resolve()));
|
||||
}
|
||||
|
||||
export function readlink(path: string): Promise<string> {
|
||||
return promisify(fs.readlink)(path);
|
||||
}
|
||||
|
||||
export function unlink(path: string): Promise<void> {
|
||||
return promisify(fs.unlink)(path);
|
||||
}
|
||||
@ -290,7 +313,7 @@ export function writeFile(path: string, data: string | Buffer | Uint8Array, opti
|
||||
|
||||
function toQueueKey(path: string): string {
|
||||
let queueKey = path;
|
||||
if (platform.isWindows || platform.isMacintosh) {
|
||||
if (isWindows || isMacintosh) {
|
||||
queueKey = queueKey.toLowerCase(); // accommodate for case insensitive file systems
|
||||
}
|
||||
|
||||
@ -394,11 +417,11 @@ export function writeFileSync(path: string, data: string | Buffer, options?: IWr
|
||||
|
||||
function ensureWriteOptions(options?: IWriteFileOptions): IEnsuredWriteFileOptions {
|
||||
if (!options) {
|
||||
return { mode: 0o666, flag: 'w' };
|
||||
return { mode: 0o666 /* default node.js mode for files */, flag: 'w' };
|
||||
}
|
||||
|
||||
return {
|
||||
mode: typeof options.mode === 'number' ? options.mode : 0o666,
|
||||
mode: typeof options.mode === 'number' ? options.mode : 0o666 /* default node.js mode for files */,
|
||||
flag: typeof options.flag === 'string' ? options.flag : 'w'
|
||||
};
|
||||
}
|
||||
@ -418,22 +441,26 @@ export async function readDirsInDir(dirPath: string): Promise<string[]> {
|
||||
|
||||
export async function dirExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
const fileStat = await stat(path);
|
||||
const { stat, symbolicLink } = await statLink(path);
|
||||
|
||||
return fileStat.isDirectory();
|
||||
return stat.isDirectory() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function fileExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
const fileStat = await stat(path);
|
||||
const { stat, symbolicLink } = await statLink(path);
|
||||
|
||||
return fileStat.isFile();
|
||||
return stat.isFile() && symbolicLink?.dangling !== true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
// Ignore, path might not exist
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function whenDeleted(path: string): Promise<void> {
|
||||
@ -459,13 +486,13 @@ export function whenDeleted(path: string): Promise<void> {
|
||||
|
||||
export async function move(source: string, target: string): Promise<void> {
|
||||
if (source === target) {
|
||||
return Promise.resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
async function updateMtime(path: string): Promise<void> {
|
||||
const stat = await lstat(path);
|
||||
if (stat.isDirectory() || stat.isSymbolicLink()) {
|
||||
return Promise.resolve(); // only for files
|
||||
return; // only for files
|
||||
}
|
||||
|
||||
const fd = await promisify(fs.open)(path, 'a');
|
||||
@ -501,28 +528,43 @@ export async function move(source: string, target: string): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function copy(source: string, target: string, copiedSourcesIn?: { [path: string]: boolean }): Promise<void> {
|
||||
const copiedSources = copiedSourcesIn ? copiedSourcesIn : Object.create(null);
|
||||
// When copying a file or folder, we want to preserve the mode
|
||||
// it had and as such provide it when creating. However, modes
|
||||
// can go beyond what we expect (see link below), so we mask it.
|
||||
// (https://github.com/nodejs/node-v0.x-archive/issues/3045#issuecomment-4862588)
|
||||
//
|
||||
// The `copy` method is very old so we should probably revisit
|
||||
// it's implementation and check wether this mask is still needed.
|
||||
const COPY_MODE_MASK = 0o777;
|
||||
|
||||
const fileStat = await stat(source);
|
||||
if (!fileStat.isDirectory()) {
|
||||
return doCopyFile(source, target, fileStat.mode & 511);
|
||||
export async function copy(source: string, target: string, handledSourcesIn?: { [path: string]: boolean }): Promise<void> {
|
||||
|
||||
// Keep track of paths already copied to prevent
|
||||
// cycles from symbolic links to cause issues
|
||||
const handledSources = handledSourcesIn ?? Object.create(null);
|
||||
if (handledSources[source]) {
|
||||
return;
|
||||
} else {
|
||||
handledSources[source] = true;
|
||||
}
|
||||
|
||||
if (copiedSources[source]) {
|
||||
return Promise.resolve(); // escape when there are cycles (can happen with symlinks)
|
||||
const { stat, symbolicLink } = await statLink(source);
|
||||
if (symbolicLink?.dangling) {
|
||||
return; // skip over dangling symbolic links (https://github.com/microsoft/vscode/issues/111621)
|
||||
}
|
||||
|
||||
copiedSources[source] = true; // remember as copied
|
||||
if (!stat.isDirectory()) {
|
||||
return doCopyFile(source, target, stat.mode & COPY_MODE_MASK);
|
||||
}
|
||||
|
||||
// Create folder
|
||||
await mkdirp(target, fileStat.mode & 511);
|
||||
await mkdirp(target, stat.mode & COPY_MODE_MASK);
|
||||
|
||||
// Copy each file recursively
|
||||
const files = await readdir(source);
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
await copy(join(source, file), join(target, file), copiedSources);
|
||||
await copy(join(source, file), join(target, file), handledSources);
|
||||
}
|
||||
}
|
||||
|
||||
|
316
lib/vscode/src/vs/base/node/powershell.ts
Normal file
316
lib/vscode/src/vs/base/node/powershell.ts
Normal file
@ -0,0 +1,316 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import * as os from 'os';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import { env } from 'vs/base/common/process';
|
||||
|
||||
const WindowsPowerShell64BitLabel = 'Windows PowerShell';
|
||||
const WindowsPowerShell32BitLabel = 'Windows PowerShell (x86)';
|
||||
|
||||
// This is required, since parseInt("7-preview") will return 7.
|
||||
const IntRegex: RegExp = /^\d+$/;
|
||||
|
||||
const PwshMsixRegex: RegExp = /^Microsoft.PowerShell_.*/;
|
||||
const PwshPreviewMsixRegex: RegExp = /^Microsoft.PowerShellPreview_.*/;
|
||||
|
||||
// The platform details descriptor for the platform we're on
|
||||
const isProcess64Bit: boolean = process.arch === 'x64';
|
||||
const isOS64Bit: boolean = isProcess64Bit || os.arch() === 'x64';
|
||||
|
||||
export interface IPowerShellExeDetails {
|
||||
readonly displayName: string;
|
||||
readonly exePath: string;
|
||||
}
|
||||
|
||||
export interface IPossiblePowerShellExe extends IPowerShellExeDetails {
|
||||
exists(): Promise<boolean>;
|
||||
}
|
||||
|
||||
class PossiblePowerShellExe implements IPossiblePowerShellExe {
|
||||
constructor(
|
||||
public readonly exePath: string,
|
||||
public readonly displayName: string,
|
||||
private knownToExist?: boolean) { }
|
||||
|
||||
public async exists(): Promise<boolean> {
|
||||
if (this.knownToExist === undefined) {
|
||||
this.knownToExist = await pfs.fileExists(this.exePath);
|
||||
}
|
||||
return this.knownToExist;
|
||||
}
|
||||
}
|
||||
|
||||
function getProgramFilesPath(
|
||||
{ useAlternateBitness = false }: { useAlternateBitness?: boolean } = {}): string | null {
|
||||
|
||||
if (!useAlternateBitness) {
|
||||
// Just use the native system bitness
|
||||
return env.ProgramFiles || null;
|
||||
}
|
||||
|
||||
// We might be a 64-bit process looking for 32-bit program files
|
||||
if (isProcess64Bit) {
|
||||
return env['ProgramFiles(x86)'] || null;
|
||||
}
|
||||
|
||||
// We might be a 32-bit process looking for 64-bit program files
|
||||
if (isOS64Bit) {
|
||||
return env.ProgramW6432 || null;
|
||||
}
|
||||
|
||||
// We're a 32-bit process on 32-bit Windows, there is no other Program Files dir
|
||||
return null;
|
||||
}
|
||||
|
||||
function getSystem32Path({ useAlternateBitness = false }: { useAlternateBitness?: boolean } = {}): string {
|
||||
const windir: string = env.windir!;
|
||||
|
||||
if (!useAlternateBitness) {
|
||||
// Just use the native system bitness
|
||||
return path.join(windir, 'System32');
|
||||
}
|
||||
|
||||
// We might be a 64-bit process looking for 32-bit system32
|
||||
if (isProcess64Bit) {
|
||||
return path.join(windir, 'SysWOW64');
|
||||
}
|
||||
|
||||
// We might be a 32-bit process looking for 64-bit system32
|
||||
if (isOS64Bit) {
|
||||
return path.join(windir, 'Sysnative');
|
||||
}
|
||||
|
||||
// We're on a 32-bit Windows, so no alternate bitness
|
||||
return path.join(windir, 'System32');
|
||||
}
|
||||
|
||||
async function findPSCoreWindowsInstallation(
|
||||
{ useAlternateBitness = false, findPreview = false }:
|
||||
{ useAlternateBitness?: boolean; findPreview?: boolean } = {}): Promise<IPossiblePowerShellExe | null> {
|
||||
|
||||
const programFilesPath = getProgramFilesPath({ useAlternateBitness });
|
||||
if (!programFilesPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const powerShellInstallBaseDir = path.join(programFilesPath, 'PowerShell');
|
||||
|
||||
// Ensure the base directory exists
|
||||
if (!await pfs.dirExists(powerShellInstallBaseDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let highestSeenVersion: number = -1;
|
||||
let pwshExePath: string | null = null;
|
||||
for (const item of await pfs.readdir(powerShellInstallBaseDir)) {
|
||||
|
||||
let currentVersion: number = -1;
|
||||
if (findPreview) {
|
||||
// We are looking for something like "7-preview"
|
||||
|
||||
// Preview dirs all have dashes in them
|
||||
const dashIndex = item.indexOf('-');
|
||||
if (dashIndex < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify that the part before the dash is an integer
|
||||
// and that the part after the dash is "preview"
|
||||
const intPart: string = item.substring(0, dashIndex);
|
||||
if (!IntRegex.test(intPart) || item.substring(dashIndex + 1) !== 'preview') {
|
||||
continue;
|
||||
}
|
||||
|
||||
currentVersion = parseInt(intPart, 10);
|
||||
} else {
|
||||
// Search for a directory like "6" or "7"
|
||||
if (!IntRegex.test(item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
currentVersion = parseInt(item, 10);
|
||||
}
|
||||
|
||||
// Ensure we haven't already seen a higher version
|
||||
if (currentVersion <= highestSeenVersion) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Now look for the file
|
||||
const exePath = path.join(powerShellInstallBaseDir, item, 'pwsh.exe');
|
||||
if (!await pfs.fileExists(exePath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
pwshExePath = exePath;
|
||||
highestSeenVersion = currentVersion;
|
||||
}
|
||||
|
||||
if (!pwshExePath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const bitness: string = programFilesPath.includes('x86') ? ' (x86)' : '';
|
||||
const preview: string = findPreview ? ' Preview' : '';
|
||||
|
||||
return new PossiblePowerShellExe(pwshExePath, `PowerShell${preview}${bitness}`, true);
|
||||
}
|
||||
|
||||
async function findPSCoreMsix({ findPreview }: { findPreview?: boolean } = {}): Promise<IPossiblePowerShellExe | null> {
|
||||
// We can't proceed if there's no LOCALAPPDATA path
|
||||
if (!env.LOCALAPPDATA) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Find the base directory for MSIX application exe shortcuts
|
||||
const msixAppDir = path.join(env.LOCALAPPDATA, 'Microsoft', 'WindowsApps');
|
||||
|
||||
if (!await pfs.dirExists(msixAppDir)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Define whether we're looking for the preview or the stable
|
||||
const { pwshMsixDirRegex, pwshMsixName } = findPreview
|
||||
? { pwshMsixDirRegex: PwshPreviewMsixRegex, pwshMsixName: 'PowerShell Preview (Store)' }
|
||||
: { pwshMsixDirRegex: PwshMsixRegex, pwshMsixName: 'PowerShell (Store)' };
|
||||
|
||||
// We should find only one such application, so return on the first one
|
||||
for (const subdir of await pfs.readdir(msixAppDir)) {
|
||||
if (pwshMsixDirRegex.test(subdir)) {
|
||||
const pwshMsixPath = path.join(msixAppDir, subdir, 'pwsh.exe');
|
||||
return new PossiblePowerShellExe(pwshMsixPath, pwshMsixName);
|
||||
}
|
||||
}
|
||||
|
||||
// If we find nothing, return null
|
||||
return null;
|
||||
}
|
||||
|
||||
function findPSCoreDotnetGlobalTool(): IPossiblePowerShellExe {
|
||||
const dotnetGlobalToolExePath: string = path.join(os.homedir(), '.dotnet', 'tools', 'pwsh.exe');
|
||||
|
||||
return new PossiblePowerShellExe(dotnetGlobalToolExePath, '.NET Core PowerShell Global Tool');
|
||||
}
|
||||
|
||||
function findWinPS({ useAlternateBitness = false }: { useAlternateBitness?: boolean } = {}): IPossiblePowerShellExe | null {
|
||||
|
||||
// x86 and ARM only have one WinPS on them
|
||||
if (!isOS64Bit && useAlternateBitness) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const systemFolderPath = getSystem32Path({ useAlternateBitness });
|
||||
|
||||
const winPSPath = path.join(systemFolderPath, 'WindowsPowerShell', 'v1.0', 'powershell.exe');
|
||||
|
||||
let displayName: string;
|
||||
if (isProcess64Bit) {
|
||||
displayName = useAlternateBitness
|
||||
? WindowsPowerShell32BitLabel
|
||||
: WindowsPowerShell64BitLabel;
|
||||
} else if (isOS64Bit) {
|
||||
displayName = useAlternateBitness
|
||||
? WindowsPowerShell64BitLabel
|
||||
: WindowsPowerShell32BitLabel;
|
||||
} else {
|
||||
// NOTE: ARM Windows devices also have Windows PowerShell x86 on them. There is no
|
||||
// "ARM Windows PowerShell".
|
||||
displayName = WindowsPowerShell32BitLabel;
|
||||
}
|
||||
|
||||
return new PossiblePowerShellExe(winPSPath, displayName, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through all the possible well-known PowerShell installations on a machine.
|
||||
* Returned values may not exist, but come with an .exists property
|
||||
* which will check whether the executable exists.
|
||||
*/
|
||||
async function* enumerateDefaultPowerShellInstallations(): AsyncIterable<IPossiblePowerShellExe> {
|
||||
// Find PSCore stable first
|
||||
let pwshExe = await findPSCoreWindowsInstallation();
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
|
||||
// Windows may have a 32-bit pwsh.exe
|
||||
pwshExe = await findPSCoreWindowsInstallation({ useAlternateBitness: true });
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
|
||||
// Also look for the MSIX/UWP installation
|
||||
pwshExe = await findPSCoreMsix();
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
|
||||
// Look for the .NET global tool
|
||||
// Some older versions of PowerShell have a bug in this where startup will fail,
|
||||
// but this is fixed in newer versions
|
||||
pwshExe = findPSCoreDotnetGlobalTool();
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
|
||||
// Look for PSCore preview
|
||||
pwshExe = await findPSCoreWindowsInstallation({ findPreview: true });
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
|
||||
// Find a preview MSIX
|
||||
pwshExe = await findPSCoreMsix({ findPreview: true });
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
|
||||
// Look for pwsh-preview with the opposite bitness
|
||||
pwshExe = await findPSCoreWindowsInstallation({ useAlternateBitness: true, findPreview: true });
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
|
||||
// Finally, get Windows PowerShell
|
||||
|
||||
// Get the natural Windows PowerShell for the process bitness
|
||||
pwshExe = findWinPS();
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
|
||||
// Get the alternate bitness Windows PowerShell
|
||||
pwshExe = findWinPS({ useAlternateBitness: true });
|
||||
if (pwshExe) {
|
||||
yield pwshExe;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through PowerShell installations on the machine according
|
||||
* to configuration passed in through the constructor.
|
||||
* PowerShell items returned by this object are verified
|
||||
* to exist on the filesystem.
|
||||
*/
|
||||
export async function* enumeratePowerShellInstallations(): AsyncIterable<IPowerShellExeDetails> {
|
||||
// Get the default PowerShell installations first
|
||||
for await (const defaultPwsh of enumerateDefaultPowerShellInstallations()) {
|
||||
if (await defaultPwsh.exists()) {
|
||||
yield defaultPwsh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first available PowerShell executable found in the search order.
|
||||
*/
|
||||
export async function getFirstAvailablePowerShellInstallation(): Promise<IPowerShellExeDetails | null> {
|
||||
for await (const pwsh of enumeratePowerShellInstallations()) {
|
||||
return pwsh;
|
||||
}
|
||||
return null;
|
||||
}
|
@ -50,7 +50,7 @@ function terminateProcess(process: cp.ChildProcess, cwd?: string): Promise<Termi
|
||||
options.cwd = cwd;
|
||||
}
|
||||
const killProcess = cp.execFile('taskkill', ['/T', '/F', '/PID', process.pid.toString()], options);
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise(resolve => {
|
||||
killProcess.once('error', (err) => {
|
||||
resolve({ success: false, error: err });
|
||||
});
|
||||
@ -68,7 +68,7 @@ function terminateProcess(process: cp.ChildProcess, cwd?: string): Promise<Termi
|
||||
} else if (Platform.isLinux || Platform.isMacintosh) {
|
||||
try {
|
||||
const cmd = FileAccess.asFileUri('vs/base/node/terminateProcess.sh', require).fsPath;
|
||||
return new Promise((resolve, reject) => {
|
||||
return new Promise(resolve => {
|
||||
cp.execFile(cmd, [process.pid.toString()], { encoding: 'utf8', shell: true } as cp.ExecFileOptions, (err, stdout, stderr) => {
|
||||
if (err) {
|
||||
resolve({ success: false, error: err });
|
||||
@ -86,8 +86,8 @@ function terminateProcess(process: cp.ChildProcess, cwd?: string): Promise<Termi
|
||||
return Promise.resolve({ success: true });
|
||||
}
|
||||
|
||||
export function getWindowsShell(environment: Platform.IProcessEnvironment = process.env as Platform.IProcessEnvironment): string {
|
||||
return environment['comspec'] || 'cmd.exe';
|
||||
export function getWindowsShell(env = process.env as Platform.IProcessEnvironment): string {
|
||||
return env['comspec'] || 'cmd.exe';
|
||||
}
|
||||
|
||||
export abstract class AbstractProcess<TProgressData> {
|
||||
@ -447,8 +447,8 @@ export namespace win32 {
|
||||
// to the current working directory.
|
||||
return path.join(cwd, command);
|
||||
}
|
||||
if (paths === undefined && Types.isString(process.env.PATH)) {
|
||||
paths = process.env.PATH.split(path.delimiter);
|
||||
if (paths === undefined && Types.isString(process.env['PATH'])) {
|
||||
paths = process.env['PATH'].split(path.delimiter);
|
||||
}
|
||||
// No PATH environment. Make path absolute to the cwd.
|
||||
if (paths === undefined || paths.length === 0) {
|
||||
|
93
lib/vscode/src/vs/base/node/shell.ts
Normal file
93
lib/vscode/src/vs/base/node/shell.ts
Normal file
@ -0,0 +1,93 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as os from 'os';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { getFirstAvailablePowerShellInstallation } from 'vs/base/node/powershell';
|
||||
import * as processes from 'vs/base/node/processes';
|
||||
|
||||
/**
|
||||
* Gets the detected default shell for the _system_, not to be confused with VS Code's _default_
|
||||
* shell that the terminal uses by default.
|
||||
* @param p The platform to detect the shell of.
|
||||
*/
|
||||
export async function getSystemShell(p: platform.Platform, env = process.env as platform.IProcessEnvironment): Promise<string> {
|
||||
if (p === platform.Platform.Windows) {
|
||||
if (platform.isWindows) {
|
||||
return getSystemShellWindows();
|
||||
}
|
||||
// Don't detect Windows shell when not on Windows
|
||||
return processes.getWindowsShell(env);
|
||||
}
|
||||
|
||||
return getSystemShellUnixLike(p, env);
|
||||
}
|
||||
|
||||
export function getSystemShellSync(p: platform.Platform, env = process.env as platform.IProcessEnvironment): string {
|
||||
if (p === platform.Platform.Windows) {
|
||||
if (platform.isWindows) {
|
||||
return getSystemShellWindowsSync(env);
|
||||
}
|
||||
// Don't detect Windows shell when not on Windows
|
||||
return processes.getWindowsShell(env);
|
||||
}
|
||||
|
||||
return getSystemShellUnixLike(p, env);
|
||||
}
|
||||
|
||||
let _TERMINAL_DEFAULT_SHELL_UNIX_LIKE: string | null = null;
|
||||
function getSystemShellUnixLike(p: platform.Platform, env: platform.IProcessEnvironment): string {
|
||||
// Only use $SHELL for the current OS
|
||||
if (platform.isLinux && p === platform.Platform.Mac || platform.isMacintosh && p === platform.Platform.Linux) {
|
||||
return '/bin/bash';
|
||||
}
|
||||
|
||||
if (!_TERMINAL_DEFAULT_SHELL_UNIX_LIKE) {
|
||||
let unixLikeTerminal: string;
|
||||
if (platform.isWindows) {
|
||||
unixLikeTerminal = '/bin/bash'; // for WSL
|
||||
} else {
|
||||
unixLikeTerminal = env['SHELL'];
|
||||
|
||||
if (!unixLikeTerminal) {
|
||||
try {
|
||||
// It's possible for $SHELL to be unset, this API reads /etc/passwd. See https://github.com/github/codespaces/issues/1639
|
||||
// Node docs: "Throws a SystemError if a user has no username or homedir."
|
||||
unixLikeTerminal = os.userInfo().shell;
|
||||
} catch (err) { }
|
||||
}
|
||||
|
||||
if (!unixLikeTerminal) {
|
||||
unixLikeTerminal = 'sh';
|
||||
}
|
||||
|
||||
// Some systems have $SHELL set to /bin/false which breaks the terminal
|
||||
if (unixLikeTerminal === '/bin/false') {
|
||||
unixLikeTerminal = '/bin/bash';
|
||||
}
|
||||
}
|
||||
_TERMINAL_DEFAULT_SHELL_UNIX_LIKE = unixLikeTerminal;
|
||||
}
|
||||
return _TERMINAL_DEFAULT_SHELL_UNIX_LIKE;
|
||||
}
|
||||
|
||||
let _TERMINAL_DEFAULT_SHELL_WINDOWS: string | null = null;
|
||||
async function getSystemShellWindows(): Promise<string> {
|
||||
if (!_TERMINAL_DEFAULT_SHELL_WINDOWS) {
|
||||
_TERMINAL_DEFAULT_SHELL_WINDOWS = (await getFirstAvailablePowerShellInstallation())!.exePath;
|
||||
}
|
||||
return _TERMINAL_DEFAULT_SHELL_WINDOWS;
|
||||
}
|
||||
|
||||
function getSystemShellWindowsSync(env: platform.IProcessEnvironment): string {
|
||||
if (_TERMINAL_DEFAULT_SHELL_WINDOWS) {
|
||||
return _TERMINAL_DEFAULT_SHELL_WINDOWS;
|
||||
}
|
||||
|
||||
const isAtLeastWindows10 = platform.isWindows && parseFloat(os.release()) >= 10;
|
||||
const is32ProcessOn64Windows = env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
|
||||
const powerShellPath = `${env['windir']}\\${is32ProcessOn64Windows ? 'Sysnative' : 'System32'}\\WindowsPowerShell\\v1.0\\powershell.exe`;
|
||||
return isAtLeastWindows10 ? powerShellPath : processes.getWindowsShell(env);
|
||||
}
|
Reference in New Issue
Block a user