* Make Command Executor return an object instead of string

This prevents us from having to run commands twice to get the status and stdout.

Caution must be taken to make sure that the assumptions here in what is returned as stdout vs stderr are correct.

Furthermore, when looking at the code, I saw something that looked just plain incorrect, so I will need to investigate that by adding the TODO before merge

* Fix Execution Type Parsing

* fix linting issue

* Fix line parsing logic

* Fix tests

* Run icacls to set execute permissions

* move to sha256

* remove unnecessary code

* Remove todo that should go in another PR

* Remove redundant trim

* rename class CommandProcessorOutput since it didnt match CommandExecutor

* Rename class file

* Report json object for failed permissions.

* Fix test to use correct folder

* fix lint

* Fix installer file deletion

* respond to linter

* Try to Add a Timeout for Installer.Exe

This will allow us to not count times when users never respond to the installer as a failure and or remediate when the installer is taking too long.

* Add .editorconfig for style settings

* try  forking

* just rely on node timeout

* Handle .net installer timeouts

* remove unnecessary import

* fix options check

* Fix option

* Include options that are needed to preserve the working env on a general command execution

* Add passive option to automate dialog

* dont use english url
This commit is contained in:
Noah Gilson 2024-07-09 16:52:13 -07:00 коммит произвёл GitHub
Родитель e68687f05e
Коммит a5d3ad5d51
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
19 изменённых файлов: 729 добавлений и 644 удалений

Просмотреть файл

@ -0,0 +1,22 @@
# Editor config
# http://EditorConfig.org
root = true
# Default rules applied to all file types
[*]
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
# JavaScript-specific settings
[*.{js,ts}]
quote_type = single
continuation_indent_size = 2
curly_bracket_next_line = true
indent_brace_style = BSD
spaces_around_operators = true
spaces_around_brackets = none

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -38,7 +38,6 @@ import { WinMacGlobalInstaller } from './WinMacGlobalInstaller';
import { LinuxGlobalInstaller } from './LinuxGlobalInstaller';
import { TelemetryUtilities } from '../EventStream/TelemetryUtilities';
import { Debugging } from '../Utils/Debugging';
import { DotnetInstallType } from '../IDotnetAcquireContext';
import { IGlobalInstaller } from './IGlobalInstaller';
import { IVSCodeExtensionContext } from '../IVSCodeExtensionContext';
import { IUtilityContext } from '../Utils/IUtilityContext';
@ -60,7 +59,6 @@ import { InstallationGraveyard } from './InstallationGraveyard';
import { InstallTrackerSingleton } from './InstallTrackerSingleton';
import { DotnetInstallMode } from './DotnetInstallMode';
import { IEventStream } from '../EventStream/EventStream';
import { strict } from 'assert';
import { IExtensionState } from '../IExtensionState';
import { getInstallKeyCustomArchitecture } from '../Utils/InstallKeyUtilities';

Просмотреть файл

@ -8,7 +8,7 @@ import * as path from 'path';
import { WebRequestWorker } from '../Utils/WebRequestWorker';
import { VersionResolver } from './VersionResolver';
import { getInstallKeyFromContext } from '../Utils/InstallKeyUtilities';
import { getInstallFromContext } from '../Utils/InstallKeyUtilities';
import { DotnetFeatureBandDoesNotExistError,
DotnetFileIntegrityCheckEvent,
DotnetInvalidReleasesJSONError,
@ -162,7 +162,7 @@ export class GlobalInstallerResolver {
}
const err = new DotnetVersionResolutionError(new EventCancellationError('DotnetVersionResolutionError',
`${this.badResolvedVersionErrorString} ${version}`), getInstallKeyFromContext(this.context));
`${this.badResolvedVersionErrorString} ${version}`), getInstallFromContext(this.context));
this.context.eventStream.post(err);
throw err.error;
}
@ -180,7 +180,7 @@ export class GlobalInstallerResolver {
{
const versionErr = new DotnetVersionResolutionError(new EventCancellationError('DotnetVersionResolutionError',
`${this.badResolvedVersionErrorString} ${specificVersion}.`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
this.context.eventStream.post(versionErr);
throw versionErr.error;
}
@ -189,7 +189,7 @@ export class GlobalInstallerResolver {
if(convertedOs === 'auto')
{
const osErr = new DotnetUnexpectedInstallerOSError(new EventBasedError('DotnetUnexpectedInstallerOSError',
`The OS ${os.platform()} is currently unsupported or unknown.`), getInstallKeyFromContext(this.context));
`The OS ${os.platform()} is currently unsupported or unknown.`), getInstallFromContext(this.context));
this.context.eventStream.post(osErr);
throw osErr.error;
}
@ -199,7 +199,7 @@ export class GlobalInstallerResolver {
{
const archErr = new DotnetUnexpectedInstallerArchitectureError(new EventBasedError('DotnetUnexpectedInstallerArchitectureError',
`The architecture ${os.arch()} is currently unsupported or unknown.
Your architecture: ${os.arch()}. Your OS: ${os.platform()}.`), getInstallKeyFromContext(this.context));
Your architecture: ${os.arch()}. Your OS: ${os.platform()}.`), getInstallFromContext(this.context));
this.context.eventStream.post(archErr);
throw archErr.error;
}
@ -211,7 +211,7 @@ export class GlobalInstallerResolver {
if(releases.length === 0)
{
const jsonErr = new DotnetInvalidReleasesJSONError(new EventBasedError('DotnetInvalidReleasesJSONError',
`${this.releasesJsonErrorString}${indexUrl}`), getInstallKeyFromContext(this.context));
`${this.releasesJsonErrorString}${indexUrl}`), getInstallFromContext(this.context));
this.context.eventStream.post(jsonErr);
throw jsonErr.error;
}
@ -239,7 +239,7 @@ export class GlobalInstallerResolver {
const releaseJsonErr = new DotnetInvalidReleasesJSONError(new EventBasedError('DotnetInvalidReleasesJSONError',
`URL for ${desiredRidPackage} on ${specificVersion} is unavailable:
The version may be Out of Support, or the releases json format used by ${indexUrl} may be invalid and the extension needs to be updated.`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
this.context.eventStream.post(releaseJsonErr);
throw releaseJsonErr.error;
}
@ -248,7 +248,7 @@ The version may be Out of Support, or the releases json format used by ${indexUr
const releaseJsonErr = new DotnetInvalidReleasesJSONError(new EventBasedError('DotnetInvalidReleasesJSONError',
`The url: ${installerUrl} is hosted on an unexpected domain.
We cannot verify that .NET downloads are hosted in a secure location, so we have rejected .NET. The url should be download.visualstudio.microsoft.com.
Please report this issue so it can be remedied or investigated.`), getInstallKeyFromContext(this.context));
Please report this issue so it can be remedied or investigated.`), getInstallFromContext(this.context));
this.context.eventStream.post(releaseJsonErr);
throw releaseJsonErr.error;
}
@ -268,7 +268,7 @@ Please report this issue so it can be remedied or investigated.`), getInstallKey
const installerErr = new DotnetNoInstallerFileExistsError(new EventBasedError('DotnetNoInstallerFileExistsError',
`An installer for the runtime ${desiredRidPackage} could not be found for version ${specificVersion}.`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
this.context.eventStream.post(installerErr);
throw installerErr.error;
}
@ -277,7 +277,7 @@ Please report this issue so it can be remedied or investigated.`), getInstallKey
const fileErr = new DotnetNoInstallerFileExistsError(new EventBasedError('DotnetNoInstallerFileExistsError',
`The SDK installation files for version ${specificVersion} running on ${desiredRidPackage} couldn't be found.
Is the version in support? Note that -preview versions or versions with build numbers aren't yet supported.
Visit https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core for support information.`), getInstallKeyFromContext(this.context));
Visit https://dotnet.microsoft.com/platform/support/policy/dotnet-core for support information.`), getInstallFromContext(this.context));
this.context.eventStream.post(fileErr);
throw fileErr.error;
}
@ -308,7 +308,7 @@ Visit https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core for
`${this.releasesJsonErrorString}
${this.getIndexUrl(this.versionResolver.getMajorMinor(version))}.
The json does not have the parameter ${this.releasesSdkNameKey} which means the API publisher has published invalid dotnet release data.
Please file an issue at https://github.com/dotnet/vscode-dotnet-runtime.`), getInstallKeyFromContext(this.context));
Please file an issue at https://github.com/dotnet/vscode-dotnet-runtime.`), getInstallFromContext(this.context));
this.context.eventStream.post(err);
throw err.error;
}
@ -333,7 +333,7 @@ Please file an issue at https://github.com/dotnet/vscode-dotnet-runtime.`), getI
{
const err = new DotnetUnexpectedInstallerOSError(new EventBasedError('DotnetUnexpectedInstallerOSError',
`The SDK Extension failed to map the OS ${operatingSystemInDotnetFormat} to a proper package type.
Your architecture: ${os.arch()}. Your OS: ${os.platform()}.`), getInstallKeyFromContext(this.context));
Your architecture: ${os.arch()}. Your OS: ${os.platform()}.`), getInstallFromContext(this.context));
this.context.eventStream.post(err);
throw err.error;
}
@ -359,7 +359,7 @@ Your architecture: ${os.arch()}. Your OS: ${os.platform()}.`), getInstallKeyFrom
if(releases.length === 0)
{
const badJsonErr = new DotnetInvalidReleasesJSONError(new EventBasedError('DotnetInvalidReleasesJSONError',
`${this.releasesJsonErrorString}${indexUrl}`), getInstallKeyFromContext(this.context));
`${this.releasesJsonErrorString}${indexUrl}`), getInstallFromContext(this.context));
this.context.eventStream.post(badJsonErr);
throw badJsonErr.error;
}
@ -380,7 +380,7 @@ Your architecture: ${os.arch()}. Your OS: ${os.platform()}.`), getInstallKeyFrom
const availableBands = Array.from(new Set(sdks.map((x : any) => this.versionResolver.getFeatureBandFromVersion(x[this.releasesSdkVersionKey]))));
const err = new DotnetFeatureBandDoesNotExistError(new EventBasedError('DotnetFeatureBandDoesNotExistError',
`The feature band '${band}' doesn't exist for the SDK major version '${version}'.
Available feature bands for this SDK version are ${availableBands}.`), getInstallKeyFromContext(this.context));
Available feature bands for this SDK version are ${availableBands}.`), getInstallFromContext(this.context));
this.context.eventStream.post(err);
throw err.error;
}

Просмотреть файл

@ -17,7 +17,7 @@ import { LinuxPackageCollection } from './LinuxPackageCollection';
import { ICommandExecutor } from '../Utils/ICommandExecutor';
import { IAcquisitionWorkerContext } from './IAcquisitionWorkerContext';
import { IUtilityContext } from '../Utils/IUtilityContext';
import { getInstallKeyFromContext } from '../Utils/InstallKeyUtilities';
import { getInstallFromContext } from '../Utils/InstallKeyUtilities';
/* tslint:disable:no-any */
/**
@ -84,7 +84,7 @@ export abstract class IDistroDotnetSDKProvider {
const error = new DotnetAcquisitionDistroUnknownError(new EventBasedError('DotnetAcquisitionDistroUnknownError',
`Automated installation for the distro ${this.distroVersion.distro} is not yet supported.
Please install the .NET SDK manually: https://dotnet.microsoft.com/download`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
throw error.error;
}
@ -363,7 +363,7 @@ Please install the .NET SDK manually: https://dotnet.microsoft.com/download`),
}
}
const err = new EventCancellationError('DotnetVersionResolutionError', `Could not find a .NET package for version ${fullySpecifiedDotnetVersion}. Found only: ${JSON.stringify(myDotnetVersions)}`);
this.context.eventStream.post(new DotnetVersionResolutionError(err, getInstallKeyFromContext(this.context)));
this.context.eventStream.post(new DotnetVersionResolutionError(err, getInstallFromContext(this.context)));
throw err;
}

Просмотреть файл

@ -14,7 +14,7 @@ import {
import { WebRequestWorker } from '../Utils/WebRequestWorker';
import { Debugging } from '../Utils/Debugging';
import { FileUtilities } from '../Utils/FileUtilities';
import { getInstallKeyFromContext } from '../Utils/InstallKeyUtilities';
import { getInstallFromContext } from '../Utils/InstallKeyUtilities';
import { IInstallScriptAcquisitionWorker } from './IInstallScriptAcquisitionWorker';
import { IAcquisitionWorkerContext } from './IAcquisitionWorkerContext';
@ -51,7 +51,7 @@ export class InstallScriptAcquisitionWorker implements IInstallScriptAcquisition
catch (error)
{
Debugging.log('An error occurred processing the install script.');
this.context.eventStream.post(new DotnetInstallScriptAcquisitionError(error as Error, getInstallKeyFromContext(this.context)));
this.context.eventStream.post(new DotnetInstallScriptAcquisitionError(error as Error, getInstallFromContext(this.context)));
// Try to use fallback install script
const fallbackPath = this.getFallbackScriptPath();

Просмотреть файл

@ -25,7 +25,7 @@ import { IDistroDotnetSDKProvider } from './IDistroDotnetSDKProvider';
import { ICommandExecutor } from '../Utils/ICommandExecutor';
import { IUtilityContext } from '../Utils/IUtilityContext';
import { IDotnetAcquireContext } from '../IDotnetAcquireContext'
import { getInstallKeyFromContext } from '../Utils/InstallKeyUtilities';
import { getInstallFromContext } from '../Utils/InstallKeyUtilities';
import { DotnetInstallMode } from './DotnetInstallMode';
/**
@ -132,7 +132,7 @@ If you would like to contribute to the list of supported distros, please visit:
if(distroName === '' || distroVersion === '')
{
const error = new DotnetAcquisitionDistroUnknownError(new EventCancellationError('DotnetAcquisitionDistroUnknownError',
this.baseUnsupportedDistroErrorMessage), getInstallKeyFromContext(this.acquisitionContext));
this.baseUnsupportedDistroErrorMessage), getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(error);
throw error.error;
}
@ -144,7 +144,7 @@ If you would like to contribute to the list of supported distros, please visit:
{
const err = new DotnetAcquisitionDistroUnknownError(new EventCancellationError('DotnetAcquisitionDistroUnknownError',
`${this.baseUnsupportedDistroErrorMessage} ... does /etc/os-release exist?`),
getInstallKeyFromContext(this.acquisitionContext));
getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
@ -173,7 +173,7 @@ If you would like to contribute to the list of supported distros, please visit:
const error = new DotnetAcquisitionDistroUnknownError(new EventCancellationError(
'DotnetAcquisitionDistroUnknownError',
`${this.baseUnsupportedDistroErrorMessage} ... we cannot initialize.`),
getInstallKeyFromContext(this.acquisitionContext));
getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(error);
throw error.error;
}
@ -195,7 +195,7 @@ If you would like to contribute to the list of supported distros, please visit:
case null:
const unknownDistroErr = new DotnetAcquisitionDistroUnknownError(new EventCancellationError(
'DotnetAcquisitionDistroUnknownError',
this.unsupportedDistroErrorMessage), getInstallKeyFromContext(this.acquisitionContext));
this.unsupportedDistroErrorMessage), getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(unknownDistroErr);
throw unknownDistroErr.error;
case 'Red Hat Enterprise Linux':
@ -204,7 +204,7 @@ If you would like to contribute to the list of supported distros, please visit:
const unsupportedRhelErr = new DotnetAcquisitionDistroUnknownError(new EventCancellationError(
'DotnetAcquisitionDistroUnknownError',
this.redhatUnsupportedDistroErrorMessage),
getInstallKeyFromContext(this.acquisitionContext));
getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(unsupportedRhelErr);
throw unsupportedRhelErr.error;
}
@ -233,7 +233,7 @@ If you would like to contribute to the list of supported distros, please visit:
{
const err = new DotnetConflictingLinuxInstallTypesError(new EventCancellationError('DotnetConflictingLinuxInstallTypesError',
this.conflictingInstallErrorMessage + microsoftFeedDir),
getInstallKeyFromContext(this.acquisitionContext));
getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
@ -245,7 +245,7 @@ If you would like to contribute to the list of supported distros, please visit:
{
const err = new DotnetConflictingLinuxInstallTypesError(new EventCancellationError('DotnetConflictingLinuxInstallTypesError',
this.conflictingInstallErrorMessage + distroFeedDir),
getInstallKeyFromContext(this.acquisitionContext));
getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
@ -266,7 +266,7 @@ If you would like to contribute to the list of supported distros, please visit:
{
const err = new DotnetCustomLinuxInstallExistsError(new EventCancellationError('DotnetCustomLinuxInstallExistsError',
this.conflictingCustomInstallErrorMessage + existingInstall),
getInstallKeyFromContext(this.acquisitionContext));
getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
@ -299,7 +299,7 @@ If you would like to contribute to the list of supported distros, please visit:
// We shouldn't downgrade to a lower patch
const err = new DotnetCustomLinuxInstallExistsError(new EventCancellationError('DotnetCustomLinuxInstallExistsError',
`An installation of ${fullySpecifiedDotnetVersion} was requested but ${existingGlobalInstallSDKVersion} is already available.`),
getInstallKeyFromContext(this.acquisitionContext));
getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}

Просмотреть файл

@ -15,7 +15,7 @@ import {
EventBasedError
} from '../EventStream/EventStreamEvents';
import { WebRequestWorker } from '../Utils/WebRequestWorker';
import { getInstallKeyFromContext } from '../Utils/InstallKeyUtilities';
import { getInstallFromContext } from '../Utils/InstallKeyUtilities';
import { Debugging } from '../Utils/Debugging';
import { IVersionResolver } from './IVersionResolver';
@ -69,7 +69,7 @@ export class VersionResolver implements IVersionResolver {
if (!response)
{
const offlineError = new Error('Unable to connect to the index server: Cannot find .NET versions.');
this.context.eventStream.post(new DotnetOfflineFailure(offlineError, getInstallKeyFromContext(this.context)));
this.context.eventStream.post(new DotnetOfflineFailure(offlineError, getInstallFromContext(this.context)));
reject(offlineError);
}
else
@ -181,7 +181,7 @@ export class VersionResolver implements IVersionResolver {
if (!response)
{
const err = new DotnetInvalidReleasesJSONError(new EventBasedError('DotnetInvalidReleasesJSONError', `We could not reach the releases API ${this.releasesUrl} to download dotnet, is your machine offline or is this website down?`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
this.context.eventStream.post(err);
throw err.error;
}
@ -211,7 +211,7 @@ export class VersionResolver implements IVersionResolver {
{
const event = new DotnetVersionResolutionError(new EventCancellationError('DotnetVersionResolutionError',
`The requested version ${fullySpecifiedVersion} is invalid.`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
this.context.eventStream.post(event);
throw event.error;
}
@ -231,7 +231,7 @@ export class VersionResolver implements IVersionResolver {
if(band === undefined)
{
const event = new DotnetFeatureBandDoesNotExistError(new EventCancellationError('DotnetFeatureBandDoesNotExistError', `${VersionResolver.invalidFeatureBandErrorString}${fullySpecifiedVersion}.`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
this.context.eventStream.post(event);
throw event.error;
}
@ -260,7 +260,7 @@ export class VersionResolver implements IVersionResolver {
{
const event = new DotnetFeatureBandDoesNotExistError(new EventCancellationError('DotnetFeatureBandDoesNotExistError',
`${VersionResolver.invalidFeatureBandErrorString}${fullySpecifiedVersion}.`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
this.context.eventStream.post(event);
throw event.error;
}

Просмотреть файл

@ -6,23 +6,27 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as proc from 'child_process';
import { FileUtilities } from '../Utils/FileUtilities';
import { VersionResolver } from './VersionResolver';
import { WebRequestWorker } from '../Utils/WebRequestWorker';
import { getInstallKeyFromContext } from '../Utils/InstallKeyUtilities';
import { getInstallFromContext } from '../Utils/InstallKeyUtilities';
import { CommandExecutor } from '../Utils/CommandExecutor';
import {
DotnetAcquisitionAlreadyInstalled,
DotnetConflictingGlobalWindowsInstallError,
DotnetFileIntegrityCheckEvent,
DotnetInstallCancelledByUserError,
DotnetNoInstallerResponseError,
DotnetUnexpectedInstallerOSError,
EventBasedError,
EventCancellationError,
NetInstallerBeginExecutionEvent,
NetInstallerEndExecutionEvent,
OSXOpenNotAvailableError,
SuppressedAcquisitionError
SuppressedAcquisitionError,
WaitingForDotnetInstallerResponse
} from '../EventStream/EventStreamEvents';
import { IGlobalInstaller } from './IGlobalInstaller';
@ -31,6 +35,7 @@ import { IFileUtilities } from '../Utils/IFileUtilities';
import { IUtilityContext } from '../Utils/IUtilityContext';
import { IAcquisitionWorkerContext } from './IAcquisitionWorkerContext';
import { DotnetInstall } from './DotnetInstall';
import { CommandExecutorResult } from '../Utils/CommandExecutorResult';
/* tslint:disable:only-arrow-functions */
/* tslint:disable:no-empty */
/* tslint:disable:no-any */
@ -55,6 +60,7 @@ export class WinMacGlobalInstaller extends IGlobalInstaller {
private installerHash : string;
protected commandRunner : ICommandExecutor;
public cleanupInstallFiles = true;
private completedInstall = false;
protected versionResolver : VersionResolver;
public file : IFileUtilities;
protected webWorker : WebRequestWorker;
@ -92,7 +98,7 @@ export class WinMacGlobalInstaller extends IGlobalInstaller {
'DotnetConflictingGlobalWindowsInstallError',
`An global install is already on the machine: version ${conflictingVersion}, that conflicts with the requested version.
Please uninstall this version first if you would like to continue.
If Visual Studio is installed, you may need to use the VS Setup Window to uninstall the SDK component.`), getInstallKeyFromContext(this.acquisitionContext));
If Visual Studio is installed, you may need to use the VS Setup Window to uninstall the SDK component.`), getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
@ -104,7 +110,7 @@ export class WinMacGlobalInstaller extends IGlobalInstaller {
{
const err = new DotnetConflictingGlobalWindowsInstallError(new EventCancellationError('DotnetConflictingGlobalWindowsInstallError',
`The integrity of the .NET install file is invalid, or there was no integrity to check and you denied the request to continue with those risks.
We cannot verify .NET is safe to download at this time. Please try again later.`), getInstallKeyFromContext(this.acquisitionContext));
We cannot verify .NET is safe to download at this time. Please try again later.`), getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
@ -124,7 +130,7 @@ We cannot verify .NET is safe to download at this time. Please try again later.`
{
// Special code for when user cancels the install
const err = new DotnetInstallCancelledByUserError(new EventCancellationError('DotnetInstallCancelledByUserError',
`The install of .NET was cancelled by the user. Aborting.`), getInstallKeyFromContext(this.acquisitionContext));
`The install of .NET was cancelled by the user. Aborting.`), getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
@ -155,7 +161,8 @@ We cannot verify .NET is safe to download at this time. Please try again later.`
{
if(os.platform() === 'win32') // Windows does not have chmod +x ability with nodejs.
{
const commandRes = await this.commandRunner.execute(CommandExecutor.makeCommand('icacls', [`"${installerPath}"`, '/grant:r', `"%username%":F`, '/t', '/c']), null, false);
const permissionsCommand = CommandExecutor.makeCommand('icacls', [`"${installerPath}"`, '/grant:r', `"%username%":F`, '/t', '/c']);
const commandRes = await this.commandRunner.execute(permissionsCommand, {}, false);
if(commandRes.stderr !== '')
{
const error = new EventBasedError('FailedToSetInstallerPermissions', `Failed to set icacls permissions on the installer file ${installerPath}. ${commandRes.stderr}`);
@ -222,11 +229,24 @@ We cannot verify .NET is safe to download at this time. Please try again later.`
}
const err = new DotnetUnexpectedInstallerOSError(new EventBasedError('DotnetUnexpectedInstallerOSError',
`The operating system ${os.platform()} is unsupported.`), getInstallKeyFromContext(this.acquisitionContext));
`The operating system ${os.platform()} is unsupported.`), getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(err);
throw err.error;
}
private handleTimeout(commandResult : CommandExecutorResult)
{
if(commandResult.status === 'SIGTERM')
{
const noResponseError = new DotnetNoInstallerResponseError(new EventBasedError('DotnetNoInstallerResponseError',
`The .NET Installer did not complete after ${this.acquisitionContext.timeoutSeconds} seconds.
If you would like to install .NET, please proceed to interact with the .NET Installer pop-up.
If you were waiting for the install to succeed, please extend the timeout setting of the .NET Install Tool extension.`), getInstallFromContext(this.acquisitionContext));
this.acquisitionContext.eventStream.post(noResponseError);
throw noResponseError.error;
}
}
/**
*
* @param installerPath The path to the installer file to run.
@ -251,7 +271,7 @@ We cannot verify .NET is safe to download at this time. Please try again later.`
const error = new EventBasedError('OSXOpenNotAvailableError',
`The 'open' command on OSX was not detected. This is likely due to the PATH environment variable on your system being clobbered by another program.
Please correct your PATH variable or make sure the 'open' utility is installed so .NET can properly execute.`);
this.acquisitionContext.eventStream.post(new OSXOpenNotAvailableError(error, getInstallKeyFromContext(this.acquisitionContext)));
this.acquisitionContext.eventStream.post(new OSXOpenNotAvailableError(error, getInstallFromContext(this.acquisitionContext)));
throw error;
}
else if(workingCommand.commandRoot === 'command')
@ -260,10 +280,11 @@ Please correct your PATH variable or make sure the 'open' utility is installed s
}
this.acquisitionContext.eventStream.post(new NetInstallerBeginExecutionEvent(`The OS X .NET Installer has been launched.`));
const commandResult = await this.commandRunner.execute(
workingCommand
);
const commandResult = await this.commandRunner.execute(workingCommand, {timeout : this.acquisitionContext.timeoutSeconds * 1000});
this.acquisitionContext.eventStream.post(new NetInstallerEndExecutionEvent(`The OS X .NET Installer has closed.`));
this.handleTimeout(commandResult);
return commandResult.status;
}
@ -275,11 +296,16 @@ Please correct your PATH variable or make sure the 'open' utility is installed s
{
commandOptions = [`/quiet`, `/install`, `/norestart`];
}
else
{
commandOptions = [`/passive`, `/install`, `/norestart`]
}
this.acquisitionContext.eventStream.post(new NetInstallerBeginExecutionEvent(`The Windows .NET Installer has been launched.`));
try
{
const commandResult = await this.commandRunner.execute(CommandExecutor.makeCommand(command, commandOptions));
const commandResult = await this.commandRunner.execute(CommandExecutor.makeCommand(command, commandOptions), {timeout : this.acquisitionContext.timeoutSeconds * 1000});
this.handleTimeout(commandResult);
this.acquisitionContext.eventStream.post(new NetInstallerEndExecutionEvent(`The Windows .NET Installer has closed.`));
return commandResult.status;
}

Просмотреть файл

@ -226,7 +226,7 @@ export class DotnetAcquisitionCompleted extends IEvent {
}
export abstract class DotnetAcquisitionError extends IEvent {
public readonly type = EventType.DotnetAcquisitionError;
public type = EventType.DotnetAcquisitionError;
public isError = true;
/**
@ -251,8 +251,8 @@ export abstract class DotnetAcquisitionError extends IEvent {
export class DotnetAcquisitionFinalError extends GenericModalEvent
{
public readonly type = EventType.DotnetTotalSuccessEvent;
public readonly eventName = 'DotnetAcquisitionTotalSuccessEvent';
public readonly type = EventType.DotnetAcquisitionFinalError;
public readonly eventName = 'DotnetAcquisitionFinalError';
public readonly mode;
public readonly installType: DotnetInstallType;
@ -279,9 +279,11 @@ export class DotnetAcquisitionFinalError extends GenericModalEvent
*/
abstract class DotnetAcquisitionFinalErrorBase extends DotnetAcquisitionError
{
constructor(public readonly error: Error, public readonly originalEventName : string, public readonly install: DotnetInstall)
{
super(error, install);
this.type = EventType.DotnetAcquisitionFinalError;
}
public getProperties(telemetry = false): { [key: string]: string } | undefined {
@ -451,6 +453,10 @@ export class DotnetNoInstallerFileExistsError extends DotnetAcquisitionError {
public readonly eventName = 'DotnetNoInstallerFileExistsError';
}
export class DotnetNoInstallerResponseError extends DotnetInstallExpectedAbort {
public readonly eventName = 'DotnetNoInstallerResponseError';
}
export class DotnetUnexpectedInstallerOSError extends DotnetAcquisitionError {
public readonly eventName = 'DotnetUnexpectedInstallerOSError';
}
@ -890,6 +896,10 @@ export class SudoProcCommandExchangePing extends DotnetCustomMessageEvent {
public readonly eventName = 'SudoProcCommandExchangePing';
}
export class WaitingForDotnetInstallerResponse extends DotnetCustomMessageEvent {
public readonly eventName = 'WaitingForDotnetInstallerResponse';
}
export class SudoProcCommandExchangeEnd extends DotnetCustomMessageEvent {
public readonly eventName = 'SudoProcCommandExchangeEnd';
}

Просмотреть файл

@ -10,6 +10,7 @@ export enum EventType {
DotnetASPNetRuntimeAcquisitionStarted,
DotnetAcquisitionCompleted,
DotnetAcquisitionError,
DotnetAcquisitionFinalError,
DotnetAcquisitionSuccessEvent,
DotnetAcquisitionMessage,
DotnetAcquisitionTest,

Просмотреть файл

@ -96,7 +96,7 @@ export class OutputChannelObserver implements IEventStreamObserver {
break;
case EventType.DotnetAcquisitionError:
const error = event as DotnetAcquisitionError;
this.outputChannel.appendLine('Error');
this.outputChannel.appendLine(`\nError : (${error.eventName ?? ''})`);
this.outputChannel.appendLine(`Failed to download .NET ${error.install?.installKey}:`);
this.outputChannel.appendLine(error.error.message);
this.outputChannel.appendLine('');

Просмотреть файл

@ -30,8 +30,6 @@ import {
DotnetCommandNotFoundEvent,
DotnetLockAcquiredEvent,
DotnetLockReleasedEvent,
DotnetWSLCheckEvent,
DotnetWSLOperationOutputEvent,
DotnetWSLSecurityError,
SudoProcAliveCheckBegin,
SudoProcAliveCheckEnd,
@ -45,7 +43,7 @@ import {
import {exec} from '@vscode/sudo-prompt';
import * as lockfile from 'proper-lockfile';
import { CommandExecutorCommand } from './CommandExecutorCommand';
import { getInstallKeyFromContext } from './InstallKeyUtilities';
import { getInstallFromContext } from './InstallKeyUtilities';
import { ICommandExecutor } from './ICommandExecutor';
@ -56,8 +54,7 @@ import { IAcquisitionWorkerContext } from '../Acquisition/IAcquisitionWorkerCont
import { FileUtilities } from './FileUtilities';
import { IFileUtilities } from './IFileUtilities';
import { CommandExecutorResult } from './CommandExecutorResult';
import { setTimeout } from 'timers';
import { IEventStream } from '../EventStream/EventStream';
import { isRunningUnderWSL, loopWithTimeoutOnCond } from './TypescriptUtilities';
/* tslint:disable:no-any */
/* tslint:disable:no-string-literal */
@ -75,38 +72,6 @@ export class CommandExecutor extends ICommandExecutor
this.fileUtil = new FileUtilities();
}
/**
* Returns true if the linux agent is running under WSL, else false.
*/
public static isRunningUnderWSL(eventStream? : IEventStream) : boolean
{
// See https://github.com/microsoft/WSL/issues/4071 for evidence that we can rely on this behavior.
eventStream?.post(new DotnetWSLCheckEvent(`Checking if system is WSL. OS: ${os.platform()}`));
if(os.platform() !== 'linux')
{
return false;
}
const command = 'grep';
const args = ['-i', 'Microsoft', '/proc/version'];
const commandResult = proc.spawnSync(command, args);
eventStream?.post(new DotnetWSLOperationOutputEvent(`The output of the WSL check:
stdout: ${commandResult.stdout?.toString()}
stderr: ${commandResult.stderr?.toString()}
status: ${commandResult.status?.toString()}`
));
if(!commandResult || !commandResult.stdout)
{
return false;
}
return commandResult.stdout.toString() !== '';
}
/**
*
* @returns The output of the command.
@ -117,7 +82,7 @@ status: ${commandResult.status?.toString()}`
this.context?.eventStream.post(new CommandExecutionUnderSudoEvent(`The command ${fullCommandString} is being ran under sudo.`));
const shellScript = path.join(this.sudoProcessCommunicationDir, 'interprocess-communicator.sh');
if(CommandExecutor.isRunningUnderWSL(this.context?.eventStream))
if(isRunningUnderWSL(this.context?.eventStream))
{
// For WSL, vscode/sudo-prompt does not work.
// This is because it relies on pkexec or a GUI app to popup and request sudo privilege.
@ -127,7 +92,7 @@ status: ${commandResult.status?.toString()}`
const err = new DotnetWSLSecurityError(new EventCancellationError('DotnetWSLSecurityError',
`Automatic .NET SDK Installation is not yet supported in WSL due to VS Code & WSL limitations.
Please install the .NET SDK manually by following https://learn.microsoft.com/en-us/dotnet/core/install/linux-ubuntu. Then, add it to the path by following https://github.com/dotnet/vscode-dotnet-runtime/blob/main/Documentation/troubleshooting-runtime.md#manually-installing-net`,
), getInstallKeyFromContext(this.context));
), getInstallFromContext(this.context));
this.context?.eventStream.post(err);
throw err.error;
}
@ -183,7 +148,7 @@ ${stderr}`));
const cancelledErr = new CommandExecutionUserRejectedPasswordRequest(new EventCancellationError('CommandExecutionUserRejectedPasswordRequest',
`Cancelling .NET Install, as command ${fullCommandString} failed.
The user refused the password prompt.`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
this.context?.eventStream.post(cancelledErr);
return Promise.reject(cancelledErr.error);
}
@ -192,7 +157,7 @@ The user refused the password prompt.`),
const securityErr = new CommandExecutionUnknownCommandExecutionAttempt(new EventCancellationError('CommandExecutionUnknownCommandExecutionAttempt',
`Cancelling .NET Install, as command ${fullCommandString} is UNKNOWN.
Please report this at https://github.com/dotnet/vscode-dotnet-runtime/issues.`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
this.context?.eventStream.post(securityErr);
return Promise.reject(securityErr.error);
}
@ -244,9 +209,11 @@ Please report this at https://github.com/dotnet/vscode-dotnet-runtime/issues.`),
this.context?.eventStream.post(new SudoProcAliveCheckBegin(`Looking for Sudo Process Master, wrote OK file. ${new Date().toISOString()}`));
const waitTime = this.context?.timeoutSeconds ? ((this.context?.timeoutSeconds/3) * 1000) : 180000;
await this.loopWithTimeoutOnCond(100, waitTime,
await loopWithTimeoutOnCond(100, waitTime,
function processRespondedByDeletingOkFile() : boolean { return !fs.existsSync(processAliveOkSentinelFile) },
function setProcessIsAlive() : void { isLive = true; }
function setProcessIsAlive() : void { isLive = true; },
this.context.eventStream,
new SudoProcCommandExchangePing(`Ping : Waiting. ${new Date().toISOString()}`)
)
.catch(error =>
{
@ -264,7 +231,7 @@ Please report this at https://github.com/dotnet/vscode-dotnet-runtime/issues.`),
{
const err = new TimeoutSudoProcessSpawnerError(new EventCancellationError('TimeoutSudoProcessSpawnerError', `We are unable to spawn the process to run commands under sudo for installing .NET.
Process Directory: ${this.sudoProcessCommunicationDir} failed with error mode: ${errorIfDead}.
It had previously spawned: ${this.hasEverLaunchedSudoFork}.`), getInstallKeyFromContext(this.context));
It had previously spawned: ${this.hasEverLaunchedSudoFork}.`), getInstallFromContext(this.context));
this.context?.eventStream.post(err);
throw err.error;
}
@ -272,25 +239,6 @@ It had previously spawned: ${this.hasEverLaunchedSudoFork}.`), getInstallKeyFrom
return isLive;
}
private async loopWithTimeoutOnCond(sampleRatePerMs : number, durationToWaitBeforeTimeoutMs : number, conditionToStop : () => boolean, doAfterStop : () => void )
{
return new Promise(async (resolve, reject) =>
{
for (let i = 0; i < (durationToWaitBeforeTimeoutMs / sampleRatePerMs); i++)
{
if(conditionToStop())
{
doAfterStop();
return resolve('The promise succeeded.');
}
this.context?.eventStream.post(new SudoProcCommandExchangePing(`Ping : Waiting. ${new Date().toISOString()}`));
await new Promise(waitAndResolve => setTimeout(waitAndResolve, sampleRatePerMs));
}
return reject('The promise timed out.');
});
}
/**
*
* @param commandToExecuteString The command to tell the sudo'd master process to execute. It must be live.
@ -333,9 +281,11 @@ It had previously spawned: ${this.hasEverLaunchedSudoFork}.`), getInstallKeyFrom
const waitTime = this.context?.timeoutSeconds ? (this.context?.timeoutSeconds * 1000) : 600000;
await this.loopWithTimeoutOnCond(100, waitTime,
await loopWithTimeoutOnCond(100, waitTime,
function ProcessFinishedExecutingAndWroteOutput() : boolean { return fs.existsSync(outputFile) },
function doNothing() : void { ; }
function doNothing() : void { ; },
this.context.eventStream,
new SudoProcCommandExchangePing(`Ping : Waiting. ${new Date().toISOString()}`)
)
.catch(error =>
{
@ -360,7 +310,7 @@ It had previously spawned: ${this.hasEverLaunchedSudoFork}.`), getInstallKeyFrom
const err = new TimeoutSudoCommandExecutionError(new EventCancellationError('TimeoutSudoCommandExecutionError',
`Timeout: The master process with command ${commandToExecuteString} never finished executing.
Process Directory: ${this.sudoProcessCommunicationDir} failed with error mode: ${terminalFailure}.
It had previously spawned: ${this.hasEverLaunchedSudoFork}.`), getInstallKeyFromContext(this.context));
It had previously spawned: ${this.hasEverLaunchedSudoFork}.`), getInstallFromContext(this.context));
this.context?.eventStream.post(err);
throw err.error;
}
@ -382,7 +332,7 @@ ${(commandOutputJson as CommandExecutorResult).stderr}`));
{
const err = new CommandExecutionNonZeroExitFailure(new EventBasedError('CommandExecutionNonZeroExitFailure',
`Cancelling .NET Install, as command ${commandToExecuteString} returned with status ${(commandOutputJson as CommandExecutorResult).status}.`),
getInstallKeyFromContext(this.context));
getInstallFromContext(this.context));
this.context?.eventStream.post(err);
throw err.error;
}
@ -411,6 +361,14 @@ ${(commandOutputJson as CommandExecutorResult).stderr}`));
public async execute(command : CommandExecutorCommand, options : any | null = null, terminalFailure = true) : Promise<CommandExecutorResult>
{
const fullCommandStringForTelemetryOnly = `${command.commandRoot} ${command.commandParts.join(' ')}`;
if(options && !options?.cwd)
{
options.cwd = path.resolve(__dirname);
}
if(options && !options?.shell)
{
options.shell = true;
}
if(!options)
{
options = {cwd : path.resolve(__dirname), shell: true};
@ -426,6 +384,12 @@ ${(commandOutputJson as CommandExecutorResult).stderr}`));
with options ${JSON.stringify(options)}.`));
const commandResult = proc.spawnSync(command.commandRoot, command.commandParts, options);
if(os.platform() === 'win32')
{
proc.spawn('taskkill', ['/pid', commandResult.pid.toString(), '/f', '/t']);
}
this.logCommandResult(commandResult, fullCommandStringForTelemetryOnly);
const statusCode : string = (() =>

Просмотреть файл

@ -12,7 +12,7 @@ import {
DotnetNotInstallRelatedCommandFailed,
EventCancellationError
} from '../EventStream/EventStreamEvents';
import { getInstallKeyFromContext } from './InstallKeyUtilities';
import { getInstallFromContext } from './InstallKeyUtilities';
import { IIssueContext } from './IIssueContext';
import { formatIssueUrl } from './IssueReporter';
import { IAcquisitionWorkerContext } from '../Acquisition/IAcquisitionWorkerContext';
@ -68,7 +68,7 @@ export async function callWithErrorHandling<T>(callback: () => T, context: IIssu
if(!isCancellationStyleError(error))
{
context.eventStream.post(isAcquisitionError ?
new DotnetCommandFailed(error, context.commandName, getInstallKeyFromContext(acquireContext!)) :
new DotnetCommandFailed(error, context.commandName, getInstallFromContext(acquireContext!)) :
// The output observer will keep track of installs and we don't want a non-install failure to make it think it should -=1 from the no. of installs
new DotnetNotInstallRelatedCommandFailed(error, context.commandName)
);

Просмотреть файл

@ -28,7 +28,7 @@ export function getInstallKeyCustomArchitecture(version : string, architecture:
`${version}~${architecture}${mode === 'aspnetcore' ? '~aspnetcore' : ''}`;
}
export function getInstallKeyFromContext(ctx : IAcquisitionWorkerContext) : DotnetInstall
export function getInstallFromContext(ctx : IAcquisitionWorkerContext) : DotnetInstall
{
const acquireContext = ctx.acquisitionContext!;

Просмотреть файл

@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Licensed to the .NET Foundation under one or more agreements.
* The .NET Foundation licenses this file to you under the MIT license.
*--------------------------------------------------------------------------------------------*/
import * as proc from 'child_process';
import * as os from 'os';
import { IEventStream } from '../EventStream/EventStream';
import { DotnetWSLCheckEvent, DotnetWSLOperationOutputEvent } from '../EventStream/EventStreamEvents';
import { IEvent } from '../EventStream/IEvent';
export async function loopWithTimeoutOnCond(sampleRatePerMs : number, durationToWaitBeforeTimeoutMs : number, conditionToStop : () => boolean, doAfterStop : () => void,
eventStream : IEventStream, waitEvent : IEvent)
{
return new Promise(async (resolve, reject) =>
{
for (let i = 0; i < (durationToWaitBeforeTimeoutMs / sampleRatePerMs); i++)
{
if(conditionToStop())
{
doAfterStop();
return resolve('The promise succeeded.');
}
eventStream.post(waitEvent);
await new Promise(waitAndResolve => setTimeout(waitAndResolve, sampleRatePerMs));
}
return reject('The promise timed out.');
});
}
/**
* Returns true if the linux agent is running under WSL, else false.
*/
export function isRunningUnderWSL(eventStream? : IEventStream) : boolean
{
// See https://github.com/microsoft/WSL/issues/4071 for evidence that we can rely on this behavior.
eventStream?.post(new DotnetWSLCheckEvent(`Checking if system is WSL. OS: ${os.platform()}`));
if(os.platform() !== 'linux')
{
return false;
}
const command = 'grep';
const args = ['-i', 'Microsoft', '/proc/version'];
const commandResult = proc.spawnSync(command, args);
eventStream?.post(new DotnetWSLOperationOutputEvent(`The output of the WSL check:
stdout: ${commandResult.stdout?.toString()}
stderr: ${commandResult.stderr?.toString()}
status: ${commandResult.status?.toString()}`
));
if(!commandResult || !commandResult.stdout)
{
return false;
}
return commandResult.stdout.toString() !== '';
}

Просмотреть файл

@ -8,7 +8,7 @@ import { HttpsProxyAgent } from 'https-proxy-agent';
import { getProxySettings } from 'get-proxy-settings';
import { AxiosCacheInstance, buildMemoryStorage, setupCache } from 'axios-cache-interceptor';
import {EventBasedError, SuppressedAcquisitionError, WebRequestError, WebRequestSent } from '../EventStream/EventStreamEvents';
import { getInstallKeyFromContext } from './InstallKeyUtilities';
import { getInstallFromContext } from './InstallKeyUtilities';
import * as fs from 'fs';
import { promisify } from 'util';
@ -222,14 +222,14 @@ ${axiosBasedError.cause? `Error Cause: ${axiosBasedError.cause!.message}` : ``}
Please ensure that you are online.
If you're on a proxy and disable registry access, you must set the proxy in our extension settings. See https://github.com/dotnet/vscode-dotnet-runtime/blob/main/Documentation/troubleshooting-runtime.md.`);
this.context.eventStream.post(new WebRequestError(summarizedError, getInstallKeyFromContext(this.context)));
this.context.eventStream.post(new WebRequestError(summarizedError, getInstallFromContext(this.context)));
throw summarizedError;
}
else
{
const genericError = new EventBasedError('WebRequestFailedGenerically',
`Web Request to ${this.url} Failed: ${error.message}. Aborting. Stack: ${'stack' in error ? error?.stack : 'unavailable.'}`);
this.context.eventStream.post(new WebRequestError(genericError, getInstallKeyFromContext(this.context)));
this.context.eventStream.post(new WebRequestError(genericError, getInstallFromContext(this.context)));
throw genericError;
}
}

Просмотреть файл

@ -23,6 +23,7 @@ export * from './Utils/Debugging';
export * from './Utils/ErrorHandler';
export * from './Utils/ExtensionConfigurationWorker';
export * from './Utils/FileUtilities';
export * from './Utils/TypescriptUtilities';
export * from './Utils/ICommandExecutor';
export * from './Utils/IFileUtilities';
export * from './Utils/InstallKeyUtilities';

Просмотреть файл

@ -19,7 +19,6 @@ import { GenericDistroSDKProvider } from '../../Acquisition/GenericDistroSDKProv
import { getMockUtilityContext } from '../unit/TestUtility';
import { DistroVersionPair, DotnetDistroSupportStatus } from '../../Acquisition/LinuxVersionResolver';
import { CommandExecutorCommand } from '../../Utils/CommandExecutorCommand';
import { IAcquisitionInvoker } from '../../Acquisition/IAcquisitionInvoker';
import { ICommandExecutor } from '../../Utils/ICommandExecutor';
import { IEvent } from '../../EventStream/IEvent';
@ -35,7 +34,6 @@ import { IVSCodeExtensionContext } from '../../IVSCodeExtensionContext';
import { ITelemetryReporter } from '../../EventStream/TelemetryObserver';
import { IUtilityContext } from '../../Utils/IUtilityContext';
import { IVSCodeEnvironment } from '../../Utils/IVSCodeEnvironment';
import { IDotnetAcquireResult } from '../../IDotnetAcquireResult';
import { GetDotnetInstallInfo } from '../../Acquisition/DotnetInstall';
import { DotnetInstall } from '../../Acquisition/DotnetInstall';
import { InstallTrackerSingleton } from '../../Acquisition/InstallTrackerSingleton';