diff --git a/Documentation/contributing-workflow.md b/Documentation/contributing-workflow.md index 5048137686..181f565479 100644 --- a/Documentation/contributing-workflow.md +++ b/Documentation/contributing-workflow.md @@ -22,7 +22,6 @@ We use and recommend the following workflow: - Please follow our [Commit Messages](contributing.md#commit-messages) guidance. 5. Add new tests corresponding to your change, if applicable. -If you are having difficulty debugging changes to the library, you may want to incorporate the logging messages into your test session. To do so, set the debugOn flag to true [Here](../vscode-dotnet-runtime-library/src/Utils/Debugging.ts). Note that the runtime and sdk extensions can be tested (with breakpoints as well, through the .js files) using their corresponding workspace and launch profiles by opening their root folders in vscode. For the library, those tests are reachable by going through the runtime extension workspace and adding the runtime-library folder to the workspace. But logging may be a better approach to debug this code. diff --git a/build.ps1 b/build.ps1 index e3fc6c1e6d..6ffe57b38e 100644 --- a/build.ps1 +++ b/build.ps1 @@ -4,8 +4,8 @@ $successColor = "Green" #################### Download backup install scripts #################### function DownloadInstallScripts() { - Invoke-WebRequest https://dot.net/v1/dotnet-install.ps1 -OutFile "./vscode-dotnet-runtime-library/install scripts/dotnet-install.ps1" - Invoke-WebRequest https://dot.net/v1/dotnet-install.sh -OutFile "./vscode-dotnet-runtime-library/install scripts/dotnet-install.sh" + Invoke-WebRequest https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1 -OutFile "./vscode-dotnet-runtime-library/install scripts/dotnet-install.ps1" + Invoke-WebRequest https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh -OutFile "./vscode-dotnet-runtime-library/install scripts/dotnet-install.sh" } try diff --git a/build.sh b/build.sh index f7bb83f9d9..e4199587f5 100755 --- a/build.sh +++ b/build.sh @@ -6,8 +6,8 @@ NC=`tput sgr0` echo "" echo "----------- Bundling Install Scripts -----------" echo "" -curl https://dot.net/v1/dotnet-install.ps1 --retry 2 -o "./vscode-dotnet-runtime-library/install scripts/dotnet-install.ps1" -curl https://dot.net/v1/dotnet-install.sh --retry 2 -o "./vscode-dotnet-runtime-library/install scripts/dotnet-install.sh" +curl https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.ps1 --retry 2 -o "./vscode-dotnet-runtime-library/install scripts/dotnet-install.ps1" +curl https://builds.dotnet.microsoft.com/dotnet/scripts/v1/dotnet-install.sh --retry 2 -o "./vscode-dotnet-runtime-library/install scripts/dotnet-install.sh" if [ $? -eq 0 ]; then echo "" diff --git a/mock-webpack.ps1 b/mock-webpack.ps1 index 4665ef633f..b874d8e48f 100644 --- a/mock-webpack.ps1 +++ b/mock-webpack.ps1 @@ -1,4 +1,4 @@ # The library doesn't get webpacked, but it needs the copy of items that would normally be webpacked # ... into the SDK or Runtime Extension for it to run in local dev scenarios. Copy-Item ".\vscode-dotnet-runtime-library\distro-data\" -Destination ".\vscode-dotnet-runtime-library\dist\Acquisition\" -Recurse -Force -Copy-Item ".\vscode-dotnet-runtime-library\install scripts\" -Destination ".\vscode-dotnet-runtime-library\dist\utils\" -Recurse -Force +Copy-Item ".\vscode-dotnet-runtime-library\install scripts\" -Destination ".\vscode-dotnet-runtime-library\dist\" -Recurse -Force diff --git a/mock-webpack.sh b/mock-webpack.sh index be22c0eb7e..66472171c2 100644 --- a/mock-webpack.sh +++ b/mock-webpack.sh @@ -2,4 +2,4 @@ echo "" echo "----------- Copying Library Webpacked Dependencies -----------" echo "" # See the build.ps1 for more details on why we do this cp -r ./vscode-dotnet-runtime-library/distro-data ./vscode-dotnet-runtime-library/dist/Acquisition -cp -r "./vscode-dotnet-runtime-library/install scripts" ./vscode-dotnet-runtime-library/dist/Utils +cp -r "./vscode-dotnet-runtime-library/install scripts" ./vscode-dotnet-runtime-library/dist diff --git a/vscode-dotnet-runtime-library/src/Acquisition/DotnetCoreAcquisitionWorker.ts b/vscode-dotnet-runtime-library/src/Acquisition/DotnetCoreAcquisitionWorker.ts index 26017dba0d..18c9984da9 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/DotnetCoreAcquisitionWorker.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/DotnetCoreAcquisitionWorker.ts @@ -47,7 +47,6 @@ import { IDotnetAcquireResult } from '../IDotnetAcquireResult'; import { IExtensionState } from '../IExtensionState'; import { IVSCodeExtensionContext } from '../IVSCodeExtensionContext'; import { CommandExecutor } from '../Utils/CommandExecutor'; -import { Debugging } from '../Utils/Debugging'; import { FileUtilities } from '../Utils/FileUtilities'; import { IFileUtilities } from '../Utils/IFileUtilities'; import { getInstallFromContext, getInstallIdCustomArchitecture } from '../Utils/InstallIdUtilities'; @@ -246,8 +245,6 @@ To keep your .NET version up to date, please reconnect to the internet at your s let acquisitionPromise = null; if (globalInstallerResolver) { - Debugging.log(`The Acquisition Worker has Determined a Global Install was requested.`, context.eventStream); - acquisitionPromise = this.acquireGlobalCore(context, globalInstallerResolver, install).catch(async (error: any) => { await new CommandExecutor(context, this.utilityContext).endSudoProcessMaster(context.eventStream).catch(() => {}); diff --git a/vscode-dotnet-runtime-library/src/Acquisition/InstallScriptAcquisitionWorker.ts b/vscode-dotnet-runtime-library/src/Acquisition/InstallScriptAcquisitionWorker.ts index bcfdccd39f..853ceb885d 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/InstallScriptAcquisitionWorker.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/InstallScriptAcquisitionWorker.ts @@ -11,7 +11,6 @@ import DotnetInstallScriptAcquisitionError, EventBasedError, } from '../EventStream/EventStreamEvents'; -import { Debugging } from '../Utils/Debugging'; import { FileUtilities } from '../Utils/FileUtilities'; import { getInstallFromContext } from '../Utils/InstallIdUtilities'; import { WebRequestWorkerSingleton } from '../Utils/WebRequestWorkerSingleton'; @@ -27,26 +26,35 @@ export class InstallScriptAcquisitionWorker implements IInstallScriptAcquisition private readonly fileUtilities: FileUtilities; private readonly scriptFileEnding = os.platform() === 'win32' ? 'ps1' : 'sh'; - + protected readonly scriptFileName: string = 'dotnet-install'; constructor(private readonly context: IAcquisitionWorkerContext) { - const scriptFileName = 'dotnet-install'; - this.scriptFilePath = path.join(__dirname, 'install scripts', `${scriptFileName}.${this.scriptFileEnding}`); + this.scriptFilePath = path.join(__dirname, 'install scripts', `${this.scriptFileName}.${this.scriptFileEnding}`); this.webWorker = WebRequestWorkerSingleton.getInstance(); this.fileUtilities = new FileUtilities(); } + private async getFallbackScript(): Promise + { + const fallbackPath = this.getFallbackScriptPath(); + if ((await this.fileUtilities.exists(fallbackPath))) + { + this.context.eventStream.post(new DotnetFallbackInstallScriptUsed()); + return fallbackPath; + } + + throw new EventBasedError('UnableToAcquireDotnetInstallScript', `Failed to Find Dotnet Install Script: ${this.scriptFileName}.${this.scriptFileEnding}. Please download .NET Manually.`); + } + public async getDotnetInstallScriptPath(): Promise { try { - Debugging.log('getDotnetInstallScriptPath() invoked.'); const script = await this.webWorker.getCachedData(`${this.scriptAcquisitionUrl}${this.scriptFileEnding}`, this.context); if (!script) { - Debugging.log('The request to acquire the script failed.'); - throw new EventBasedError('NoInstallScriptPathExists', 'Unable to get script path.'); + return this.getFallbackScript(); } await this.fileUtilities.writeFileOntoDisk(script, this.scriptFilePath, this.context.eventStream); @@ -55,19 +63,8 @@ export class InstallScriptAcquisitionWorker implements IInstallScriptAcquisition } catch (error: any) { - Debugging.log('An error occurred processing the install script.'); this.context.eventStream.post(new DotnetInstallScriptAcquisitionError(error as Error, getInstallFromContext(this.context))); - - // Try to use fallback install script - const fallbackPath = this.getFallbackScriptPath(); - if ((await this.fileUtilities.exists(fallbackPath))) - { - Debugging.log('Returning the fallback script path.'); - this.context.eventStream.post(new DotnetFallbackInstallScriptUsed()); - return fallbackPath; - } - - throw new EventBasedError('UnableToAcquireDotnetInstallScript', `Failed to Acquire Dotnet Install Script: ${error}`); + return this.getFallbackScript(); } } diff --git a/vscode-dotnet-runtime-library/src/Acquisition/VersionResolver.ts b/vscode-dotnet-runtime-library/src/Acquisition/VersionResolver.ts index 5cc2c076ed..27ef3e4abd 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/VersionResolver.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/VersionResolver.ts @@ -13,7 +13,6 @@ import EventBasedError, EventCancellationError } from '../EventStream/EventStreamEvents'; -import { Debugging } from '../Utils/Debugging'; import { getAssumedInstallInfo, getInstallFromContext } from '../Utils/InstallIdUtilities'; import { WebRequestWorkerSingleton } from '../Utils/WebRequestWorkerSingleton'; @@ -169,18 +168,15 @@ export class VersionResolver implements IVersionResolver { parsedVer = null; } - Debugging.log(`Semver parsing passed: ${version}.`, this.context.eventStream); if (!parsedVer || (version.split('.').length !== 2 && version.split('.').length !== 3)) { - Debugging.log(`Resolving the version: ${version} ... it is invalid!`, this.context.eventStream); const err = new DotnetVersionResolutionError(new EventCancellationError('DotnetVersionResolutionError', `An invalid version was requested. Version: ${version}`), getAssumedInstallInfo(version, this.context.acquisitionContext.mode!)); this.context.eventStream.post(err); throw err.error; } - Debugging.log(`The version ${version} was determined to be valid.`, this.context.eventStream); } private async getReleasesInfo(mode: DotnetInstallMode): Promise diff --git a/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts b/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts index 053f8b7d34..2cbc861e06 100644 --- a/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts +++ b/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts @@ -623,22 +623,7 @@ export class DotnetInstallCancelledByUserError extends DotnetInstallExpectedAbor public readonly eventName = 'DotnetInstallCancelledByUserError'; } -export class DotnetDebuggingMessage extends IEvent -{ - public readonly eventName = 'DotnetDebuggingMessage'; - public readonly type = EventType.DotnetDebuggingMessage; - - constructor(public readonly message: string) - { - super(); - this.message = message; - } - public getProperties() - { - return { message: this.message }; - } -} export class DotnetNonZeroInstallerExitCodeError extends DotnetAcquisitionError { diff --git a/vscode-dotnet-runtime-library/src/EventStream/EventType.ts b/vscode-dotnet-runtime-library/src/EventStream/EventType.ts index 348df72463..118649e151 100644 --- a/vscode-dotnet-runtime-library/src/EventStream/EventType.ts +++ b/vscode-dotnet-runtime-library/src/EventStream/EventType.ts @@ -18,7 +18,6 @@ export enum EventType DotnetAcquisitionAlreadyInstalled, DotnetAcquisitionInProgress, DotnetUninstallMessage, - DotnetDebuggingMessage, DotnetTotalSuccessEvent, OfflineInstallUsed, OfflineWarning, diff --git a/vscode-dotnet-runtime-library/src/EventStream/OutputChannelObserver.ts b/vscode-dotnet-runtime-library/src/EventStream/OutputChannelObserver.ts index a66ccdc455..089272e530 100644 --- a/vscode-dotnet-runtime-library/src/EventStream/OutputChannelObserver.ts +++ b/vscode-dotnet-runtime-library/src/EventStream/OutputChannelObserver.ts @@ -10,7 +10,6 @@ import DotnetAcquisitionInProgress, DotnetAcquisitionStarted, DotnetCustomMessageEvent, - DotnetDebuggingMessage, DotnetExistingPathResolutionCompleted, DotnetInstallExpectedAbort, DotnetOfflineInstallUsed, @@ -138,10 +137,6 @@ export class OutputChannelObserver implements IEventStreamObserver const upgradeMessage = event as DotnetUpgradedEvent; this.outputChannel.appendLine(`${upgradeMessage.eventMessage}:`); break; - case EventType.DotnetDebuggingMessage: - const loggedMessage = event as DotnetDebuggingMessage; - this.outputChannel.appendLine(loggedMessage.message); - break; case EventType.OfflineInstallUsed: const offlineUsedMsg = event as DotnetOfflineInstallUsed; this.outputChannel.appendLine(offlineUsedMsg.eventMessage); diff --git a/vscode-dotnet-runtime-library/src/Utils/Debugging.ts b/vscode-dotnet-runtime-library/src/Utils/Debugging.ts deleted file mode 100644 index 654952142f..0000000000 --- a/vscode-dotnet-runtime-library/src/Utils/Debugging.ts +++ /dev/null @@ -1,50 +0,0 @@ -/*--------------------------------------------------------------------------------------------- -* Licensed to the .NET Foundation under one or more agreements. -* The .NET Foundation licenses this file to you under the MIT license. -*--------------------------------------------------------------------------------------------*/ - -import path = require('path'); -import { IEventStream } from '../EventStream/EventStream'; -import { - DotnetDebuggingMessage, -} from '../EventStream/EventStreamEvents'; -import * as fs from 'fs'; - -/** - * A simple wrapper around console logging that can disable / enable all debugging or logging messages. - * Use EventStreamEvents for user facing debugging logs. - */ -export class Debugging -{ - static logFile = path.join(__dirname, 'VsDotnetDebuggingLog.txt'); - static debugOn = false; - static logToTerminal = true; - static logToFile = true; - - public static log(message : string, eventStream : IEventStream | null = null) - { - if(Debugging.debugOn) - { - if(Debugging.logToTerminal) - { - eventStream?.post(new DotnetDebuggingMessage(message)); - } - - console.log(message); - - - if(Debugging.logFile) - - { - console.log(`Writing to ${Debugging.logFile}`); - if(Debugging.logToTerminal) - { - eventStream?.post(new DotnetDebuggingMessage(`Writing to ${Debugging.logFile}`)); - } - - const file = fs.createWriteStream(Debugging.logFile, { flags: 'a+' }); - file.write(message); - } - } - } -}; diff --git a/vscode-dotnet-runtime-library/src/index.ts b/vscode-dotnet-runtime-library/src/index.ts index 5d4fc54da8..ac89388f76 100644 --- a/vscode-dotnet-runtime-library/src/index.ts +++ b/vscode-dotnet-runtime-library/src/index.ts @@ -53,7 +53,6 @@ export * from './test/mocks/MockObjects'; export * from './test/mocks/MockWindowDisplayWorker'; export * from './test/unit/TestUtility'; export * from './Utils/CommandExecutor'; -export * from './Utils/Debugging'; export * from './Utils/ErrorHandler'; export * from './Utils/ExtensionConfigurationWorker'; export * from './Utils/FileUtilities'; diff --git a/vscode-dotnet-runtime-library/src/test/mocks/MockObjects.ts b/vscode-dotnet-runtime-library/src/test/mocks/MockObjects.ts index 657f1a7f5c..6ce72577df 100644 --- a/vscode-dotnet-runtime-library/src/test/mocks/MockObjects.ts +++ b/vscode-dotnet-runtime-library/src/test/mocks/MockObjects.ts @@ -322,7 +322,7 @@ export class MockVersionResolver extends VersionResolver export class MockInstallScriptWorker extends InstallScriptAcquisitionWorker { - constructor(ctx: IAcquisitionWorkerContext, failing: boolean, private fallback = false) + constructor(ctx: IAcquisitionWorkerContext, private failing: boolean, private fallback = false) { super(ctx); this.webWorker = failing ? @@ -338,6 +338,10 @@ export class MockInstallScriptWorker extends InstallScriptAcquisitionWorker } else { + if (this.failing) + { + throw new Error('Failed to Acquire Dotnet Install Script'); + } return super.getFallbackScriptPath(); } } diff --git a/vscode-dotnet-runtime-library/src/test/unit/InstallScriptAcquisitionWorker.test.ts b/vscode-dotnet-runtime-library/src/test/unit/InstallScriptAcquisitionWorker.test.ts new file mode 100644 index 0000000000..4e1f418e0e --- /dev/null +++ b/vscode-dotnet-runtime-library/src/test/unit/InstallScriptAcquisitionWorker.test.ts @@ -0,0 +1,129 @@ +/*--------------------------------------------------------------------------------------------- +* Licensed to the .NET Foundation under one or more agreements. +* The .NET Foundation licenses this file to you under the MIT license. +*--------------------------------------------------------------------------------------------*/ +import * as chai from 'chai'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as fs from 'fs'; +import * as os from 'os'; + +import { IInstallScriptAcquisitionWorker } from '../../Acquisition/IInstallScriptAcquisitionWorker'; +import { InstallScriptAcquisitionWorker } from '../../Acquisition/InstallScriptAcquisitionWorker'; +import +{ + DotnetFallbackInstallScriptUsed, + DotnetInstallScriptAcquisitionCompleted, + DotnetInstallScriptAcquisitionError, +} from '../../EventStream/EventStreamEvents'; +import { LocalMemoryCacheSingleton } from '../../LocalMemoryCacheSingleton'; +import { WebRequestWorkerSingleton } from '../../Utils/WebRequestWorkerSingleton'; +import +{ + MockEventStream, + MockInstallScriptWorker, +} from '../mocks/MockObjects'; +import { getMockAcquisitionContext } from './TestUtility'; + +const assert = chai.assert; +chai.use(chaiAsPromised); + +const maxTimeoutTime = 10000; + +suite('InstallScriptAcquisitionWorker Unit Tests', function () +{ + this.afterEach(async () => + { + // Tear down tmp storage for fresh run + WebRequestWorkerSingleton.getInstance().destroy(); + LocalMemoryCacheSingleton.getInstance().invalidate(); + }); + + test('Successful script acquisition from web', async () => + { + const eventStream = new MockEventStream(); + const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker( + getMockAcquisitionContext('runtime', '7.0', undefined, eventStream), + false // not failing + ); + + const scriptPath = await installScriptWorker.getDotnetInstallScriptPath(); + + assert.exists(scriptPath); + assert.isTrue(scriptPath.length > 0); + assert.exists(eventStream.events.find(event => event instanceof DotnetInstallScriptAcquisitionCompleted)); + }).timeout(maxTimeoutTime); + + test('Web request failure triggers fallback to bundled script', async () => + { + const eventStream = new MockEventStream(); + const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker( + getMockAcquisitionContext('runtime', '7.0', undefined, eventStream), + true, // failing + true // has fallback + ); + + const scriptPath = await installScriptWorker.getDotnetInstallScriptPath(); + + assert.exists(scriptPath); + assert.isTrue(scriptPath.length > 0); + + // Verify events were posted for both failure and fallback usage + assert.exists(eventStream.events.find(event => event instanceof DotnetInstallScriptAcquisitionError)); + assert.exists(eventStream.events.find(event => event instanceof DotnetFallbackInstallScriptUsed)); + }).timeout(maxTimeoutTime); + + test('Bundled fallback script is valid and exists', async () => + { + const eventStream = new MockEventStream(); + const installScriptWorker = new InstallScriptAcquisitionWorker( + getMockAcquisitionContext('runtime', '7.0', undefined, eventStream) + ); + + const expectedScriptPath = await installScriptWorker.getDotnetInstallScriptPath(); + + assert.isTrue(fs.existsSync(expectedScriptPath), `Bundled script should exist at: ${expectedScriptPath}`); + + // Verify the file is readable + const stats = fs.statSync(expectedScriptPath); + assert.isTrue(stats.isFile(), 'Bundled script should be a file'); + assert.isTrue(stats.size > 0, 'Bundled script should not be empty'); + + // Verify it has appropriate permissions on Unix systems + if (os.platform() !== 'win32') + { + const mode = stats.mode; + // Check if owner has read permission (at minimum) + assert.isTrue((mode & parseInt('400', 8)) !== 0, 'Script should be readable by owner'); + } + }).timeout(maxTimeoutTime); + + test('Web request failure without valid fallback throws error', async () => + { + const eventStream = new MockEventStream(); + const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker( + getMockAcquisitionContext('runtime', '7.0', undefined, eventStream), + true, // failing + false // no fallback + ); + + await assert.isRejected(installScriptWorker.getDotnetInstallScriptPath()); + }).timeout(maxTimeoutTime); + + test('Multiple calls to get script path should be idempotent', async () => + { + const eventStream = new MockEventStream(); + const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker( + getMockAcquisitionContext('runtime', '7.0', undefined, eventStream), + false // not failing + ); + + const firstPath = await installScriptWorker.getDotnetInstallScriptPath(); + const secondPath = await installScriptWorker.getDotnetInstallScriptPath(); + + assert.equal(firstPath, secondPath, 'Multiple calls should return the same path'); + + // Should have at least one completion event (could be cached on second call) + const completionEvents = eventStream.events.filter(event => event instanceof DotnetInstallScriptAcquisitionCompleted); + assert.isTrue(completionEvents.length >= 1, 'Should have at least one completion event'); + }).timeout(maxTimeoutTime); +}); \ No newline at end of file diff --git a/vscode-dotnet-runtime-library/src/test/unit/LinuxDistroTests.test.ts b/vscode-dotnet-runtime-library/src/test/unit/LinuxDistroTests.test.ts index d5995f84fe..857d389906 100644 --- a/vscode-dotnet-runtime-library/src/test/unit/LinuxDistroTests.test.ts +++ b/vscode-dotnet-runtime-library/src/test/unit/LinuxDistroTests.test.ts @@ -8,13 +8,13 @@ import * as os from 'os'; import { DotnetInstallMode } from '../../Acquisition/DotnetInstallMode'; import { GenericDistroSDKProvider } from '../../Acquisition/GenericDistroSDKProvider'; import { DistroVersionPair, DotnetDistroSupportStatus } from '../../Acquisition/LinuxVersionResolver'; +import { UBUNTU_DISTRO_INFO_KEY } from '../../Acquisition/StringConstants'; import * as versionUtils from '../../Acquisition/VersionUtilities'; +import { getMajor } from '../../Acquisition/VersionUtilities'; import { LocalMemoryCacheSingleton } from '../../LocalMemoryCacheSingleton'; import { WebRequestWorkerSingleton } from '../../Utils/WebRequestWorkerSingleton'; import { MockCommandExecutor } from '../mocks/MockObjects'; -import { UBUNTU_DISTRO_INFO_KEY } from '../../Acquisition/StringConstants'; import { getLatestLinuxDotnet, getLinuxSupportedDotnetSDKVersion, getMockAcquisitionContext, getMockUtilityContext } from './TestUtility'; -import { getMajor, getMajorMinor } from '../../Acquisition/VersionUtilities'; const assert = chai.assert; const standardTimeoutTime = 100000; diff --git a/vscode-dotnet-runtime-library/src/test/unit/WebRequestWorker.test.ts b/vscode-dotnet-runtime-library/src/test/unit/WebRequestWorker.test.ts index 5b3d4cea71..6b8a265444 100644 --- a/vscode-dotnet-runtime-library/src/test/unit/WebRequestWorker.test.ts +++ b/vscode-dotnet-runtime-library/src/test/unit/WebRequestWorker.test.ts @@ -24,10 +24,6 @@ import } from '../mocks/MockObjects'; import { LocalMemoryCacheSingleton } from '../../LocalMemoryCacheSingleton'; -import -{ - Debugging -} from '../../Utils/Debugging'; import { WebRequestWorkerSingleton } from '../../Utils/WebRequestWorkerSingleton'; import { getMockAcquisitionContext, getMockUtilityContext } from './TestUtility'; @@ -66,19 +62,14 @@ suite('WebRequestWorker Unit Tests', function () test('Install Script Request Failure With Fallback Install Script', async () => { - Debugging.log('Get Test Context.'); const eventStream = new MockEventStream(); - Debugging.log('Instantiate Install Script Worker.'); const installScriptWorker: IInstallScriptAcquisitionWorker = new MockInstallScriptWorker(getMockAcquisitionContext('runtime', '', undefined, eventStream), true, true); - Debugging.log('Request the install script path.'); const scriptPath = await installScriptWorker.getDotnetInstallScriptPath(); - Debugging.log('Asserting the path is as expected.'); assert.equal(scriptPath, path.join(__dirname, '..')); - Debugging.log('Scan the event stream events.'); assert.exists(eventStream.events.find(event => event instanceof DotnetInstallScriptAcquisitionError)); assert.exists(eventStream.events.find(event => event instanceof DotnetFallbackInstallScriptUsed)); }); diff --git a/vscode-dotnet-sdk-extension/src/extension.ts b/vscode-dotnet-sdk-extension/src/extension.ts index 7a3abd75fe..efa338ad7e 100644 --- a/vscode-dotnet-sdk-extension/src/extension.ts +++ b/vscode-dotnet-sdk-extension/src/extension.ts @@ -4,54 +4,56 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs'; -import open = require('open'); import * as os from 'os'; import * as path from 'path'; import * as vscode from 'vscode'; -import { +import +{ AcquireErrorConfiguration, - AcquisitionInvoker as LocalAcquisitionInvoker, callWithErrorHandling, + CommandExecutor, + directoryProviderFactory, DotnetAcquisitionRequested, DotnetAcquisitionStatusRequested, DotnetCoreAcquisitionWorker, DotnetSDKAcquisitionStarted, enableExtensionTelemetry, ErrorConfiguration, + EventBasedError, ExtensionConfigurationWorker, formatIssueUrl, IDotnetAcquireContext, IDotnetUninstallContext, - EventBasedError, IEventStreamContext, IExtensionContext, IIssueContext, InstallationValidator, + AcquisitionInvoker as LocalAcquisitionInvoker, registerEventStream, SdkInstallationDirectoryProvider, VersionResolver, - VSCodeExtensionContext, VSCodeEnvironment, - WindowDisplayWorker, - Debugging, - CommandExecutor, - directoryProviderFactory, + VSCodeExtensionContext, + WindowDisplayWorker } from 'vscode-dotnet-runtime-library'; +import open = require('open'); -import { dotnetCoreAcquisitionExtensionId } from './DotnetCoreAcquisitionId'; import { GlobalInstallerResolver } from 'vscode-dotnet-runtime-library/dist/Acquisition/GlobalInstallerResolver'; import { IAcquisitionWorkerContext } from 'vscode-dotnet-runtime-library/dist/Acquisition/IAcquisitionWorkerContext'; +import { dotnetCoreAcquisitionExtensionId } from './DotnetCoreAcquisitionId'; const packageJson = require('../package.json'); // Extension constants -namespace configKeys { +namespace configKeys +{ export const installTimeoutValue = 'installTimeoutValue'; export const enableTelemetry = 'enableTelemetry'; export const proxyUrl = 'proxyUrl'; export const allowInvalidPaths = 'allowInvalidPaths'; } -namespace commandKeys { +namespace commandKeys +{ export const acquire = 'acquire'; export const acquireStatus = 'acquireStatus'; export const uninstallAll = 'uninstallAll'; @@ -65,7 +67,8 @@ const defaultTimeoutValue = 600; const troubleshootingUrl = 'https://github.com/dotnet/vscode-dotnet-runtime/blob/main/Documentation/troubleshooting-sdk.md'; const knownExtensionIds = ['ms-dotnettools.sample-extension', 'ms-dotnettools.vscode-dotnet-pack']; -export function activate(context: vscode.ExtensionContext, extensionContext?: IExtensionContext) { +export function activate(context: vscode.ExtensionContext, extensionContext?: IExtensionContext) +{ const extensionConfiguration = extensionContext !== undefined && extensionContext.extensionConfiguration ? extensionContext.extensionConfiguration : vscode.workspace.getConfiguration(configPrefix); @@ -93,7 +96,8 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE const [eventStream, outputChannel, loggingObserver, eventStreamObservers, telemetryObserver, _] = registerEventStream(eventStreamContext, vsCodeExtensionContext, utilContext); const extensionConfigWorker = new ExtensionConfigurationWorker(extensionConfiguration, undefined, undefined); - const issueContext = (errorConfiguration: ErrorConfiguration | undefined, commandName: string, version?: string) => { + const issueContext = (errorConfiguration: ErrorConfiguration | undefined, commandName: string, version?: string) => + { return { logger: loggingObserver, errorConfiguration: errorConfiguration || AcquireErrorConfiguration.DisplayAllErrorPopups, @@ -112,13 +116,16 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE const resolvedTimeoutSeconds = timeoutValue === undefined ? defaultTimeoutValue : timeoutValue; let storagePath: string; - if (os.platform() === 'win32') { + if (os.platform() === 'win32') + { // Install to %AppData% on windows to avoid running into long path errors storagePath = process.env.APPDATA ? process.env.APPDATA : context.globalStoragePath; - } else { + } else + { storagePath = path.join(os.homedir(), '.vscode-dotnet-sdk'); - if (!fs.existsSync(storagePath)) { - fs.mkdirSync(storagePath, {recursive: true}); + if (!fs.existsSync(storagePath)) + { + fs.mkdirSync(storagePath, { recursive: true }); } } @@ -127,8 +134,6 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE const dotnetAcquireRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.acquire}`, async (commandContext: IDotnetAcquireContext) => { - Debugging.log(`The SDK Extension Acquire Command was Invoked.`, eventStream); - if (commandContext.requestingExtensionId === undefined) { return Promise.reject('No requesting extension id was provided.'); @@ -141,17 +146,16 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE const acquisitionContext = getContext(commandContext); const versionResolver = new VersionResolver(acquisitionContext); - const pathResult = await callWithErrorHandling(async () => { + const pathResult = await callWithErrorHandling(async () => + { eventStream.post(new DotnetSDKAcquisitionStarted(commandContext.requestingExtensionId)); eventStream.post(new DotnetAcquisitionRequested(commandContext.version, commandContext.requestingExtensionId ?? 'notProvided', 'sdk', 'local')); telemetryObserver?.setAcquisitionContext(acquisitionContext, commandContext); - if(commandContext.installType === 'global') + if (commandContext.installType === 'global') { - Debugging.log(`Acquisition Request was remarked as Global.`, eventStream); - - if(commandContext.version === '' || !commandContext.version) + if (commandContext.version === '' || !commandContext.version) { throw new EventBasedError('BadContextualSDKExtensionVersionError', `No version was defined to install.`); @@ -161,13 +165,10 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE const dotnetPath = await acquisitionWorker.acquireGlobalSDK(acquisitionContext, globalInstallerResolver); new CommandExecutor(acquisitionContext, utilContext).setPathEnvVar(dotnetPath.dotnetPath, troubleshootingUrl, displayWorker, vsCodeExtensionContext, true); - Debugging.log(`Returning path: ${dotnetPath}.`, eventStream); return dotnetPath; } else { - Debugging.log(`Acquisition Request was remarked as local.`, eventStream); - const resolvedVersion = await versionResolver.getFullVersion(commandContext.version, 'sdk'); acquisitionContext.acquisitionContext.version = resolvedVersion; const acquisitionInvoker = new LocalAcquisitionInvoker(acquisitionContext, utilContext); @@ -179,13 +180,13 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE } }, issueContext(commandContext.errorConfiguration, 'acquireSDK'), commandContext.requestingExtensionId, acquisitionContext); - Debugging.log(`Returning Path Result ${pathResult}.`, eventStream); - return pathResult; }); - const dotnetAcquireStatusRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.acquireStatus}`, async (commandContext: IDotnetAcquireContext) => { - const pathResult = await callWithErrorHandling(async () => { + const dotnetAcquireStatusRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.acquireStatus}`, async (commandContext: IDotnetAcquireContext) => + { + const pathResult = await callWithErrorHandling(async () => + { eventStream.post(new DotnetAcquisitionStatusRequested(commandContext.version, commandContext.requestingExtensionId)); const fakeContext = getContext(null); const versionResolver = new VersionResolver(fakeContext); @@ -197,36 +198,39 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE return pathResult; }); - const dotnetUninstallAllRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.uninstallAll}`, async (commandContext: IDotnetUninstallContext | undefined) => { - await callWithErrorHandling(async () => { + const dotnetUninstallAllRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.uninstallAll}`, async (commandContext: IDotnetUninstallContext | undefined) => + { + await callWithErrorHandling(async () => + { await acquisitionWorker.uninstallAll(eventStream, directoryProviderFactory('sdk', storagePath).getStoragePath(), context.globalState); }, issueContext(commandContext ? commandContext.errorConfiguration : undefined, 'uninstallAll')); }); const showOutputChannelRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.showAcquisitionLog}`, () => outputChannel.show(/* preserveFocus */ false)); - const reportIssueRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.reportIssue}`, async () => { + const reportIssueRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.reportIssue}`, async () => + { const [url, issueBody] = formatIssueUrl(undefined, issueContext(AcquireErrorConfiguration.DisableErrorPopups, 'reportIssue')); await vscode.env.clipboard.writeText(issueBody); open(url); }); - function getContext(commandContext : IDotnetAcquireContext | null) : IAcquisitionWorkerContext + function getContext(commandContext: IDotnetAcquireContext | null): IAcquisitionWorkerContext { - const acquisitionContext : IAcquisitionWorkerContext = { + const acquisitionContext: IAcquisitionWorkerContext = { storagePath, extensionState: context.globalState, eventStream, installationValidator: new InstallationValidator(eventStream), timeoutSeconds: resolvedTimeoutSeconds, installDirectoryProvider: new SdkInstallationDirectoryProvider(storagePath), - acquisitionContext : commandContext ?? { // See runtime extension for more details on this fake context. + acquisitionContext: commandContext ?? { // See runtime extension for more details on this fake context. version: 'unspecified', architecture: os.arch(), requestingExtensionId: 'notAnAcquisitionCall', mode: 'sdk' }, - isExtensionTelemetryInitiallyEnabled : isExtensionTelemetryEnabled, + isExtensionTelemetryInitiallyEnabled: isExtensionTelemetryEnabled, allowInvalidPathSetting: allowInvalidPathSetting ?? false };