diff --git a/.vscode/settings.json b/.vscode/settings.json index b7c259c482..6d4884baf4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,7 +12,8 @@ "proccom", "programfiles", "REGHEXVALUE", - "REGTYPE" + "REGTYPE", + "Republisher" ], "azure-pipelines.1ESPipelineTemplatesSchemaFile": true } \ No newline at end of file diff --git a/sample/package-lock.json b/sample/package-lock.json index 85f7198632..de919ae888 100644 --- a/sample/package-lock.json +++ b/sample/package-lock.json @@ -20,6 +20,7 @@ "@types/mocha": "9.0.0", "@types/node": "16.11.7", "@types/rimraf": "3.0.2", + "@types/source-map-support": "^0.5.10", "@types/vscode": "1.74.0", "mocha": "^9.2.2", "rimraf": "3.0.2", @@ -32,7 +33,7 @@ }, "../vscode-dotnet-runtime-extension": { "name": "vscode-dotnet-runtime", - "version": "2.0.6", + "version": "2.0.7", "license": "MIT", "dependencies": { "@types/chai-as-promised": "^7.1.8", @@ -47,6 +48,7 @@ "open": "^8.4.0", "rimraf": "3.0.2", "shelljs": "^0.8.5", + "ts-loader": "^9.5.1", "typescript": "4.4.4", "vscode-dotnet-runtime-library": "file:../vscode-dotnet-runtime-library", "webpack-permissions-plugin": "^1.0.9" @@ -56,6 +58,7 @@ "@types/mocha": "^9.0.0", "@types/node": "16.11.7", "@types/rimraf": "3.0.2", + "@types/source-map-support": "^0.5.10", "@types/vscode": "1.74.0", "copy-webpack-plugin": "9.0.1", "webpack": "5.88.2", @@ -90,6 +93,7 @@ "proper-lockfile": "^4.1.2", "rimraf": "3.0.2", "run-script-os": "^1.1.6", + "semver": "^7.6.2", "shelljs": "0.8.5", "typescript": "4.4.4", "vscode-extension-telemetry": "^0.4.3", @@ -132,11 +136,12 @@ "run-script-os": "^1.1.6", "shelljs": "^0.8.5", "source-map-support": "^0.5.21", + "ts-loader": "^9.5.1", "typescript": "4.4.4", "vscode-dotnet-runtime-library": "file:../vscode-dotnet-runtime-library" }, "devDependencies": { - "@types/source-map-support": "^0.5.6", + "@types/source-map-support": "^0.5.10", "copy-webpack-plugin": "^9.0.1", "webpack": "5.76.0", "webpack-cli": "4.9.1" @@ -316,6 +321,16 @@ "@types/node": "*" } }, + "node_modules/@types/source-map-support": { + "version": "0.5.10", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha1-gk3O+YlJa66Y6dBMjcGsHXDhvTk=", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "^0.6.0" + } + }, "node_modules/@types/vscode": { "version": "1.74.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/vscode/-/vscode-1.74.0.tgz", @@ -3668,6 +3683,15 @@ "@types/node": "*" } }, + "@types/source-map-support": { + "version": "0.5.10", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha1-gk3O+YlJa66Y6dBMjcGsHXDhvTk=", + "dev": true, + "requires": { + "source-map": "^0.6.0" + } + }, "@types/vscode": { "version": "1.74.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/vscode/-/vscode-1.74.0.tgz", @@ -5615,6 +5639,7 @@ "@types/mocha": "^9.0.0", "@types/node": "16.11.7", "@types/rimraf": "3.0.2", + "@types/source-map-support": "^0.5.10", "@types/vscode": "1.74.0", "@vscode/test-electron": "^2.3.9", "axios": "^1.3.4", @@ -5628,6 +5653,7 @@ "open": "^8.4.0", "rimraf": "3.0.2", "shelljs": "^0.8.5", + "ts-loader": "^9.5.1", "typescript": "4.4.4", "vscode-dotnet-runtime-library": "file:../vscode-dotnet-runtime-library", "webpack": "5.88.2", @@ -5663,6 +5689,7 @@ "proper-lockfile": "^4.1.2", "rimraf": "3.0.2", "run-script-os": "^1.1.6", + "semver": "^7.6.2", "shelljs": "0.8.5", "typescript": "4.4.4", "vscode-extension-telemetry": "^0.4.3", @@ -5677,7 +5704,7 @@ "@types/mocha": "^9.0.0", "@types/node": "16.11.7", "@types/rimraf": "3.0.2", - "@types/source-map-support": "^0.5.6", + "@types/source-map-support": "^0.5.10", "@types/vscode": "1.74.0", "@vscode/test-electron": "^2.3.9", "axios": "^1.3.4", @@ -5694,6 +5721,7 @@ "run-script-os": "^1.1.6", "shelljs": "^0.8.5", "source-map-support": "^0.5.21", + "ts-loader": "^9.5.1", "typescript": "4.4.4", "vscode-dotnet-runtime-library": "file:../vscode-dotnet-runtime-library", "webpack": "5.76.0", diff --git a/sample/package.json b/sample/package.json index 2d59fb15bb..9633a09476 100644 --- a/sample/package.json +++ b/sample/package.json @@ -31,6 +31,11 @@ "title": "Acquire .NET runtime", "category": "Sample" }, + { + "command": "sample.dotnet.acquireASPNET", + "title": "Acquire ASPNET runtime", + "category": "Sample" + }, { "command": "sample.dotnet.acquireStatus", "title": "Check the status of acquiring the .NET runtime", @@ -46,6 +51,11 @@ "title": "Concurrently acquire all 2.X .NET Core runtimes", "category": "Sample" }, + { + "command": "sample.dotnet.concurrentASPNETTest", + "title": "Concurrently acquire all 2.X ASPNET Core runtimes", + "category": "Sample" + }, { "command": "sample.dotnet.showAcquisitionLog", "title": "Show .NET runtime acquisition log", @@ -112,6 +122,7 @@ "@types/mocha": "9.0.0", "@types/node": "16.11.7", "@types/rimraf": "3.0.2", + "@types/source-map-support": "^0.5.10", "@types/vscode": "1.74.0", "mocha": "^9.2.2", "rimraf": "3.0.2", diff --git a/sample/src/extension.ts b/sample/src/extension.ts index bb10b94a43..3b612c9fa6 100644 --- a/sample/src/extension.ts +++ b/sample/src/extension.ts @@ -7,13 +7,14 @@ import * as cp from 'child_process'; import * as path from 'path'; import * as vscode from 'vscode'; import { + DotnetInstallMode, IDotnetAcquireContext, IDotnetAcquireResult, IDotnetListVersionsResult, - IDotnetVersion, } from 'vscode-dotnet-runtime-library'; import * as runtimeExtension from 'vscode-dotnet-runtime'; import * as sdkExtension from 'vscode-dotnet-sdk'; +import { install } from 'source-map-support'; export function activate(context: vscode.ExtensionContext) { @@ -76,7 +77,8 @@ ${stderr}`); } }); - const sampleAcquireRegistration = vscode.commands.registerCommand('sample.dotnet.acquire', async (version) => { + async function callAcquireAPI(version : string | undefined, installMode : DotnetInstallMode | undefined) + { if (!version) { version = await vscode.window.showInputBox({ placeHolder: '3.1', @@ -87,10 +89,18 @@ ${stderr}`); try { await vscode.commands.executeCommand('dotnet.showAcquisitionLog'); - await vscode.commands.executeCommand('dotnet.acquire', { version, requestingExtensionId }); + await vscode.commands.executeCommand('dotnet.acquire', { version, requestingExtensionId, mode: installMode }); } catch (error) { vscode.window.showErrorMessage((error as Error).toString()); } + } + + const sampleAcquireRegistration = vscode.commands.registerCommand('sample.dotnet.acquire', async (version) => { + await callAcquireAPI(version, undefined); + }); + + const sampleAcquireASPNETRegistration = vscode.commands.registerCommand('sample.dotnet.acquireASPNET', async (version) => { + await callAcquireAPI(version, 'aspnetcore' ); }); const sampleAcquireStatusRegistration = vscode.commands.registerCommand('sample.dotnet.acquireStatus', async (version) => { @@ -120,21 +130,33 @@ ${stderr}`); } }); - const sampleConcurrentTest = vscode.commands.registerCommand('sample.dotnet.concurrentTest', async () => { - try { + async function acquireConcurrent(versions : [string, string, string], installMode? : DotnetInstallMode) + { + try + { vscode.commands.executeCommand('dotnet.showAcquisitionLog'); const promises = [ - vscode.commands.executeCommand('dotnet.acquire', { version: '2.0', requestingExtensionId }), - vscode.commands.executeCommand('dotnet.acquire', { version: '2.1', requestingExtensionId }), - vscode.commands.executeCommand('dotnet.acquire', { version: '2.2', requestingExtensionId })]; + vscode.commands.executeCommand('dotnet.acquire', { version: versions[0], requestingExtensionId, mode: installMode }), + vscode.commands.executeCommand('dotnet.acquire', { version: versions[1], requestingExtensionId, mode: installMode }), + vscode.commands.executeCommand('dotnet.acquire', { version: versions[2], requestingExtensionId, mode: installMode })]; - for (const promise of promises) { + for (const promise of promises) + { // Await here so we can detect errors await promise; } - } catch (error) { + } catch (error) + { vscode.window.showErrorMessage((error as Error).toString()); } + } + + const sampleConcurrentTest = vscode.commands.registerCommand('sample.dotnet.concurrentTest', async () => { + await acquireConcurrent(['2.0', '2.1', '2.2'], 'runtime'); + }); + + const sampleConcurrentASPNETTest = vscode.commands.registerCommand('sample.dotnet.concurrentASPNETTest', async () => { + await acquireConcurrent(['2.0', '2.1', '2.2'], 'aspnetcore'); }); const sampleShowAcquisitionLogRegistration = vscode.commands.registerCommand('sample.dotnet.showAcquisitionLog', async () => { @@ -148,9 +170,11 @@ ${stderr}`); context.subscriptions.push( sampleHelloWorldRegistration, sampleAcquireRegistration, + sampleAcquireASPNETRegistration, sampleAcquireStatusRegistration, sampleDotnetUninstallAllRegistration, sampleConcurrentTest, + sampleConcurrentASPNETTest, sampleShowAcquisitionLogRegistration, ); diff --git a/sample/yarn.lock b/sample/yarn.lock index 434d3df0e9..333da169a5 100644 --- a/sample/yarn.lock +++ b/sample/yarn.lock @@ -121,6 +121,13 @@ "@types/glob" "*" "@types/node" "*" +"@types/source-map-support@^0.5.10": + "integrity" "sha1-gk3O+YlJa66Y6dBMjcGsHXDhvTk=" + "resolved" "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.10.tgz" + "version" "0.5.10" + dependencies: + "source-map" "^0.6.0" + "@types/vscode@1.74.0": "integrity" "sha1-StwhtOf1J7iT3jQYwhqR8eUDvc0=" "resolved" "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/vscode/-/vscode-1.74.0.tgz" @@ -1814,6 +1821,7 @@ "proper-lockfile" "^4.1.2" "rimraf" "3.0.2" "run-script-os" "^1.1.6" + "semver" "^7.6.2" "shelljs" "0.8.5" "typescript" "4.4.4" "vscode-extension-telemetry" "^0.4.3" @@ -1823,7 +1831,7 @@ "vscode-dotnet-runtime@file:../vscode-dotnet-runtime-extension": "resolved" "file:../vscode-dotnet-runtime-extension" - "version" "2.0.6" + "version" "2.0.7" dependencies: "@types/chai-as-promised" "^7.1.8" "@vscode/test-electron" "^2.3.9" @@ -1837,6 +1845,7 @@ "open" "^8.4.0" "rimraf" "3.0.2" "shelljs" "^0.8.5" + "ts-loader" "^9.5.1" "typescript" "4.4.4" "vscode-dotnet-runtime-library" "file:../vscode-dotnet-runtime-library" "webpack-permissions-plugin" "^1.0.9" @@ -1865,6 +1874,7 @@ "run-script-os" "^1.1.6" "shelljs" "^0.8.5" "source-map-support" "^0.5.21" + "ts-loader" "^9.5.1" "typescript" "4.4.4" "vscode-dotnet-runtime-library" "file:../vscode-dotnet-runtime-library" diff --git a/vscode-dotnet-runtime-extension/CHANGELOG.md b/vscode-dotnet-runtime-extension/CHANGELOG.md index 9aa9c7c660..5435e50dac 100644 --- a/vscode-dotnet-runtime-extension/CHANGELOG.md +++ b/vscode-dotnet-runtime-extension/CHANGELOG.md @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning]. ## [Unreleased] +## [2.0.7] - 2024-06-30 + +Adds support for ASP.NET Core Runtime installation via the `acquire` API. + + +Smaller bug fixes for error handling and reporting. +Updated dependencies and added additional notices to our main page. + ## [2.0.6] - 2024-06-10 Keeps track of which extensions manage which installs to allow for better cleanup of old runtimes and sdks. diff --git a/vscode-dotnet-runtime-extension/package-lock.json b/vscode-dotnet-runtime-extension/package-lock.json index 03a32e1190..496af3611b 100644 --- a/vscode-dotnet-runtime-extension/package-lock.json +++ b/vscode-dotnet-runtime-extension/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-dotnet-runtime", - "version": "2.0.6", + "version": "2.0.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vscode-dotnet-runtime", - "version": "2.0.6", + "version": "2.0.7", "license": "MIT", "dependencies": { "@types/chai-as-promised": "^7.1.8", @@ -31,6 +31,7 @@ "@types/mocha": "^9.0.0", "@types/node": "16.11.7", "@types/rimraf": "3.0.2", + "@types/source-map-support": "^0.5.10", "@types/vscode": "1.74.0", "copy-webpack-plugin": "9.0.1", "webpack": "5.88.2", @@ -299,6 +300,16 @@ "@types/node": "*" } }, + "node_modules/@types/source-map-support": { + "version": "0.5.10", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha1-gk3O+YlJa66Y6dBMjcGsHXDhvTk=", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "^0.6.0" + } + }, "node_modules/@types/vscode": { "version": "1.74.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/vscode/-/vscode-1.74.0.tgz", @@ -3622,6 +3633,15 @@ "@types/node": "*" } }, + "@types/source-map-support": { + "version": "0.5.10", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha1-gk3O+YlJa66Y6dBMjcGsHXDhvTk=", + "dev": true, + "requires": { + "source-map": "^0.6.0" + } + }, "@types/vscode": { "version": "1.74.0", "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/vscode/-/vscode-1.74.0.tgz", diff --git a/vscode-dotnet-runtime-extension/package.json b/vscode-dotnet-runtime-extension/package.json index b0bf3e3ed8..e8c4bc88ee 100644 --- a/vscode-dotnet-runtime-extension/package.json +++ b/vscode-dotnet-runtime-extension/package.json @@ -13,7 +13,7 @@ "description": "This extension installs and manages different versions of the .NET SDK and Runtime.", "appInsightsKey": "02dc18e0-7494-43b2-b2a3-18ada5fcb522", "icon": "images/dotnetIcon.png", - "version": "2.0.6", + "version": "2.0.7", "publisher": "ms-dotnettools", "engines": { "vscode": "^1.74.0" @@ -129,6 +129,7 @@ "@types/mocha": "^9.0.0", "@types/node": "16.11.7", "@types/rimraf": "3.0.2", + "@types/source-map-support": "^0.5.10", "@types/vscode": "1.74.0", "copy-webpack-plugin": "9.0.1", "webpack": "5.88.2", diff --git a/vscode-dotnet-runtime-extension/src/extension.ts b/vscode-dotnet-runtime-extension/src/extension.ts index 553389c7ac..3745244868 100644 --- a/vscode-dotnet-runtime-extension/src/extension.ts +++ b/vscode-dotnet-runtime-extension/src/extension.ts @@ -18,9 +18,6 @@ import { DotnetCoreAcquisitionWorker, DotnetCoreDependencyInstaller, DotnetExistingPathResolutionCompleted, - DotnetRuntimeAcquisitionStarted, - DotnetRuntimeAcquisitionTotalSuccessEvent, - DotnetGlobalSDKAcquisitionTotalSuccessEvent, enableExtensionTelemetry, EventBasedError, ErrorConfiguration, @@ -43,7 +40,6 @@ import { VSCodeExtensionContext, VSCodeEnvironment, WindowDisplayWorker, - DotnetSDKAcquisitionStarted, GlobalInstallerResolver, CommandExecutor, IDotnetListVersionsContext, @@ -60,7 +56,9 @@ import { UserManualInstallFailure, DotnetInstall, EventCancellationError, + getInstallKeyCustomArchitecture, DotnetInstallType, + DotnetAcquisitionTotalSuccessEvent, } from 'vscode-dotnet-runtime-library'; import { dotnetCoreAcquisitionExtensionId } from './DotnetCoreAcquisitionId'; @@ -95,10 +93,10 @@ const defaultTimeoutValue = 600; const moreInfoUrl = 'https://github.com/dotnet/vscode-dotnet-runtime/blob/main/Documentation/troubleshooting-runtime.md'; let disableActivationUnderTest = true; -export function activate(context: vscode.ExtensionContext, extensionContext?: IExtensionContext) +export function activate(vsCodeContext: vscode.ExtensionContext, extensionContext?: IExtensionContext) { - if((process.env.DOTNET_INSTALL_TOOL_UNDER_TEST === 'true' || (context?.extensionMode === vscode.ExtensionMode.Test)) && disableActivationUnderTest) + if((process.env.DOTNET_INSTALL_TOOL_UNDER_TEST === 'true' || (vsCodeContext?.extensionMode === vscode.ExtensionMode.Test)) && disableActivationUnderTest) { return; } @@ -110,8 +108,8 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE // Reading Extension Configuration const timeoutValue = extensionConfiguration.get(configKeys.installTimeoutValue); - if (!fs.existsSync(context.globalStoragePath)) { - fs.mkdirSync(context.globalStoragePath); + if (!fs.existsSync(vsCodeContext.globalStoragePath)) { + fs.mkdirSync(vsCodeContext.globalStoragePath); } const resolvedTimeoutSeconds = timeoutValue === undefined ? defaultTimeoutValue : timeoutValue; const proxyLink = extensionConfiguration.get(configKeys.proxyUrl); @@ -124,17 +122,18 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE vsCodeEnv: new VSCodeEnvironment() } - const vsCodeExtensionContext = new VSCodeExtensionContext(context); + const vsCodeExtensionContext = new VSCodeExtensionContext(vsCodeContext); const eventStreamContext = { displayChannelName, - logPath: context.logPath, + logPath: vsCodeContext.logPath, extensionId: dotnetCoreAcquisitionExtensionId, enableTelemetry: isExtensionTelemetryEnabled, telemetryReporter: extensionContext ? extensionContext.telemetryReporter : undefined, showLogCommand: `${commandPrefix}.${commandKeys.showAcquisitionLog}`, packageJson } as IEventStreamContext; - const [globalEventStream, outputChannel, loggingObserver, eventStreamObservers, telemetryObserver] = registerEventStream(eventStreamContext, vsCodeExtensionContext, utilContext); + const [globalEventStream, outputChannel, loggingObserver, + eventStreamObservers, telemetryObserver, _] = registerEventStream(eventStreamContext, vsCodeExtensionContext, utilContext); // Setting up command-shared classes for Runtime & SDK Acquisition @@ -145,12 +144,14 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE const dotnetAcquireRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.acquire}`, async (commandContext: IDotnetAcquireContext) => { const worker = getAcquisitionWorker(); - const runtimeContext = getAcquisitionWorkerContext('runtime', commandContext); + commandContext.mode = commandContext.mode ?? 'runtime' as DotnetInstallMode; + const mode = commandContext.mode; + + const runtimeContext = getAcquisitionWorkerContext(mode, commandContext); const dotnetPath = await callWithErrorHandling>(async () => { - globalEventStream.post(new DotnetRuntimeAcquisitionStarted(commandContext.requestingExtensionId)); - globalEventStream.post(new DotnetAcquisitionRequested(commandContext.version, commandContext.requestingExtensionId)); + globalEventStream.post(new DotnetAcquisitionRequested(commandContext.version, commandContext.requestingExtensionId ?? 'notProvided', mode, commandContext.installType ?? 'local')); telemetryObserver?.setAcquisitionContext(runtimeContext, commandContext); @@ -174,28 +175,36 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE // Note: This will impact the context object given to the worker and error handler since objects own a copy of a reference in JS. const runtimeVersionResolver = new VersionResolver(runtimeContext); - commandContext.version = await runtimeVersionResolver.getFullRuntimeVersion(commandContext.version); + commandContext.version = await runtimeVersionResolver.getFullVersion(commandContext.version, mode); const acquisitionInvoker = new AcquisitionInvoker(runtimeContext, utilContext); - return worker.acquireRuntime(runtimeContext, acquisitionInvoker); + return mode === 'aspnetcore' ? worker.acquireLocalASPNET(runtimeContext, acquisitionInvoker) : worker.acquireLocalRuntime(runtimeContext, acquisitionInvoker); }, getIssueContext(existingPathConfigWorker)(commandContext.errorConfiguration, 'acquire', commandContext.version), commandContext.requestingExtensionId, runtimeContext); - const iKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(commandContext.version, commandContext.architecture, 'local'); - const install = {installKey : iKey, version : commandContext.version, installMode: 'runtime', isGlobal: false, + const iKey = getInstallKeyCustomArchitecture(commandContext.version, commandContext.architecture, mode, 'local'); + const install = {installKey : iKey, version : commandContext.version, installMode: mode, isGlobal: false, architecture: commandContext.architecture ?? DotnetCoreAcquisitionWorker.defaultArchitecture()} as DotnetInstall; - globalEventStream.post(new DotnetRuntimeAcquisitionTotalSuccessEvent(commandContext.version, install, commandContext.requestingExtensionId ?? '', dotnetPath?.dotnetPath ?? '')); + + if(dotnetPath !== undefined && dotnetPath?.dotnetPath !== null) + { + globalEventStream.post(new DotnetAcquisitionTotalSuccessEvent(commandContext.version, install, commandContext.requestingExtensionId ?? '', dotnetPath.dotnetPath)); + } + + loggingObserver.dispose(); return dotnetPath; }); const dotnetAcquireGlobalSDKRegistration = vscode.commands.registerCommand(`${commandPrefix}.${commandKeys.acquireGlobalSDK}`, async (commandContext: IDotnetAcquireContext) => { + commandContext.mode = commandContext.mode ?? 'sdk' as DotnetInstallMode; + if (commandContext.requestingExtensionId === undefined) { return Promise.reject('No requesting extension id was provided.'); } let fullyResolvedVersion = ''; - const sdkContext = getAcquisitionWorkerContext('runtime', commandContext); + const sdkContext = getAcquisitionWorkerContext(commandContext.mode, commandContext); const worker = getAcquisitionWorker(); const pathResult = await callWithErrorHandling(async () => @@ -211,8 +220,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE `No version was defined to install.`); } - globalEventStream.post(new DotnetSDKAcquisitionStarted(commandContext.requestingExtensionId)); - globalEventStream.post(new DotnetAcquisitionRequested(commandContext.version, commandContext.requestingExtensionId)); + globalEventStream.post(new DotnetAcquisitionRequested(commandContext.version, commandContext.requestingExtensionId ?? 'notProvided', commandContext.mode!, commandContext.installType ?? 'global')); const existingPath = await resolveExistingPathIfExists(existingPathConfigWorker, commandContext); if(existingPath) @@ -235,11 +243,16 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE return dotnetPath; }, getIssueContext(existingPathConfigWorker)(commandContext.errorConfiguration, commandKeys.acquireGlobalSDK), commandContext.requestingExtensionId, sdkContext); - const iKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(commandContext.version, commandContext.architecture, 'global'); - const install = {installKey : iKey, version : commandContext.version, installMode: 'sdk', isGlobal: true, - architecture: commandContext.architecture ?? DotnetCoreAcquisitionWorker.defaultArchitecture()} as DotnetInstall; + const iKey = getInstallKeyCustomArchitecture(commandContext.version, commandContext.architecture, commandContext.mode, 'global'); + const install = {installKey : iKey, version : commandContext.version, installMode: commandContext.mode, isGlobal: true, + architecture: commandContext.architecture ?? DotnetCoreAcquisitionWorker.defaultArchitecture()} as DotnetInstall; + + if(pathResult !== undefined && pathResult?.dotnetPath !== null) + { + globalEventStream.post(new DotnetAcquisitionTotalSuccessEvent(commandContext.version, install, commandContext.requestingExtensionId ?? '', pathResult.dotnetPath)); + } - globalEventStream.post(new DotnetGlobalSDKAcquisitionTotalSuccessEvent(commandContext.version, install, commandContext.requestingExtensionId ?? '', pathResult?.dotnetPath ?? '')); + loggingObserver.dispose(); return pathResult; }); @@ -312,7 +325,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE globalEventStream.post(new DotnetAcquisitionStatusRequested(commandContext.version, commandContext.requestingExtensionId)); const runtimeVersionResolver = new VersionResolver(workerContext); - const resolvedVersion = await runtimeVersionResolver.getFullRuntimeVersion(commandContext.version); + const resolvedVersion = await runtimeVersionResolver.getFullVersion(commandContext.version, mode); commandContext.version = resolvedVersion; const dotnetPath = await worker.acquireStatus(workerContext, 'runtime'); return dotnetPath; @@ -325,9 +338,9 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE { const mode = 'runtime' as DotnetInstallMode; const worker = getAcquisitionWorker(); - const installDirectoryProvider = directoryProviderFactory(mode, context.globalStoragePath); + const installDirectoryProvider = directoryProviderFactory(mode, vsCodeContext.globalStoragePath); - await worker.uninstallAll(globalEventStream, installDirectoryProvider.getStoragePath(), context.globalState); + await worker.uninstallAll(globalEventStream, installDirectoryProvider.getStoragePath(), vsCodeContext.globalState); }, getIssueContext(existingPathConfigWorker)(commandContext ? commandContext.errorConfiguration : undefined, 'uninstallAll') ); @@ -387,8 +400,8 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE customWebWorker: WebRequestWorker | undefined, onRecommendationMode : boolean) : Promise => { const mode = 'sdk' as DotnetInstallMode; - const context = getVersionResolverContext(mode, 'global', commandContext?.errorConfiguration); - const customVersionResolver = new VersionResolver(context, customWebWorker); + const workercontext = getVersionResolverContext(mode, 'global', commandContext?.errorConfiguration); + const customVersionResolver = new VersionResolver(workercontext, customWebWorker); if(os.platform() !== 'linux' || !onRecommendationMode) { @@ -401,7 +414,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE } else { - const linuxResolver = new LinuxVersionResolver(context, utilContext); + const linuxResolver = new LinuxVersionResolver(workercontext, utilContext); try { const suggestedVersion = await linuxResolver.getRecommendedDotnetVersion('sdk' as DotnetInstallMode); @@ -448,14 +461,13 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE function getAcquisitionWorkerContext(mode : DotnetInstallMode, acquiringContext : IDotnetAcquireContext) : IAcquisitionWorkerContext { return { - storagePath: context.globalStoragePath, - extensionState: context.globalState, + storagePath: vsCodeContext.globalStoragePath, + extensionState: vsCodeContext.globalState, eventStream: globalEventStream, installationValidator: new InstallationValidator(globalEventStream), timeoutSeconds: resolvedTimeoutSeconds, acquisitionContext: acquiringContext, - installMode: mode, - installDirectoryProvider: directoryProviderFactory(mode, context.globalStoragePath), + installDirectoryProvider: directoryProviderFactory(mode, vsCodeContext.globalStoragePath), proxyUrl: proxyLink, isExtensionTelemetryInitiallyEnabled: isExtensionTelemetryEnabled } @@ -484,7 +496,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE } // Exposing API Endpoints - context.subscriptions.push( + vsCodeContext.subscriptions.push( dotnetAcquireRegistration, dotnetAcquireStatusRegistration, dotnetAcquireGlobalSDKRegistration, diff --git a/vscode-dotnet-runtime-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts b/vscode-dotnet-runtime-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts index 27eaa55cf0..b74068253c 100644 --- a/vscode-dotnet-runtime-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts +++ b/vscode-dotnet-runtime-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts @@ -9,21 +9,21 @@ import * as path from 'path'; import * as rimraf from 'rimraf'; import * as vscode from 'vscode'; import { - DotnetCoreAcquisitionWorker, FileUtilities, IDotnetAcquireContext, IDotnetAcquireResult, IExistingPaths, IDotnetListVersionsContext, IDotnetListVersionsResult, - IDotnetVersion, + getInstallKeyCustomArchitecture, ITelemetryEvent, MockExtensionConfiguration, MockExtensionContext, MockTelemetryReporter, MockWebRequestWorker, MockWindowDisplayWorker, - getMockAcquisitionContext + getMockAcquisitionContext, + DotnetInstallMode } from 'vscode-dotnet-runtime-library'; import * as extension from '../../extension'; import { warn } from 'console'; @@ -33,7 +33,8 @@ import { warn } from 'console'; const assert : any = chai.assert; const standardTimeoutTime = 40000; -suite('DotnetCoreAcquisitionExtension End to End', function() { +suite('DotnetCoreAcquisitionExtension End to End', function() +{ this.retries(3); const storagePath = path.join(__dirname, 'tmp'); const mockState = new MockExtensionContext(); @@ -46,7 +47,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function() { const mockExistingPathsWithGlobalConfig: IExistingPaths = { individualizedExtensionPaths: [{extensionId: 'alternative.extension', path: 'foo'}], sharedExistingPath: undefined -} + } const mockReleasesData = `{ "releases-index": [ @@ -102,18 +103,30 @@ suite('DotnetCoreAcquisitionExtension End to End', function() { assert.isAbove(extensionContext.subscriptions.length, 0); }).timeout(standardTimeoutTime); - test('Install Local Runtime Command', async () => { - const context: IDotnetAcquireContext = { version: '2.2', requestingExtensionId }; + async function installRuntime(dotnetVersion : string, installMode : DotnetInstallMode) + { + const context: IDotnetAcquireContext = { version: dotnetVersion, requestingExtensionId, mode: installMode }; const result = await vscode.commands.executeCommand('dotnet.acquire', context); assert.exists(result, 'Command results a result'); assert.exists(result!.dotnetPath, 'The return type of the local runtime install command has a .dotnetPath property'); assert.isTrue(fs.existsSync(result!.dotnetPath), 'The returned path of .net does exist'); assert.include(result!.dotnetPath, '.dotnet', '.dotnet is in the path of the local runtime install'); assert.include(result!.dotnetPath, context.version, 'the path of the local runtime install includes the version of the runtime requested'); + } + + test('Install Local Runtime Command', async () => + { + await installRuntime('2.2', 'runtime'); }).timeout(standardTimeoutTime); - test('Uninstall Local Runtime Command', async () => { - const context: IDotnetAcquireContext = { version: '2.1', requestingExtensionId }; + test('Install Local ASP.NET Runtime Command', async () => + { + await installRuntime('2.2', 'aspnetcore'); + }).timeout(standardTimeoutTime); + + async function installUninstallAll(dotnetVersion : string, installMode : DotnetInstallMode) + { + const context: IDotnetAcquireContext = { version: dotnetVersion, requestingExtensionId, mode: installMode }; const result = await vscode.commands.executeCommand('dotnet.acquire', context); assert.exists(result); assert.exists(result!.dotnetPath); @@ -121,14 +134,21 @@ suite('DotnetCoreAcquisitionExtension End to End', function() { assert.include(result!.dotnetPath, context.version); await vscode.commands.executeCommand('dotnet.uninstallAll', context.version); assert.isFalse(fs.existsSync(result!.dotnetPath), 'the dotnet path result does not exist after uninstall'); + } + + test('Uninstall Local Runtime Command', async () => { + await installUninstallAll('2.2', 'runtime') }).timeout(standardTimeoutTime); + test('Uninstall Local ASP.NET Runtime Command', async () => { + await installUninstallAll('2.2', 'aspnetcore') + }).timeout(standardTimeoutTime); - test('Install and Uninstall Multiple Local Runtime Versions', async () => { - const versions = ['2.2', '3.0', '3.1']; + async function installMultipleVersions(versions : string[], installMode : DotnetInstallMode) + { let dotnetPaths: string[] = []; for (const version of versions) { - const result = await vscode.commands.executeCommand('dotnet.acquire', { version, requestingExtensionId }); + const result = await vscode.commands.executeCommand('dotnet.acquire', { version, requestingExtensionId, mode: installMode }); assert.exists(result); assert.exists(result!.dotnetPath); assert.include(result!.dotnetPath, version); @@ -140,8 +160,17 @@ suite('DotnetCoreAcquisitionExtension End to End', function() { for (const dotnetPath of dotnetPaths) { assert.isTrue(fs.existsSync(dotnetPath)); } + } + + test('Install and Uninstall Multiple Local Runtime Versions', async () => { + await installMultipleVersions(['2.2', '3.0', '3.1'], 'runtime'); + }).timeout(standardTimeoutTime * 2); + + test('Install and Uninstall Multiple Local ASP.NET Runtime Versions', async () => { + await installMultipleVersions(['2.2', '3.0', '3.1'], 'aspnetcore'); }).timeout(standardTimeoutTime * 2); + test('Install SDK Globally E2E (Requires Admin)', async () => { // We only test if the process is running under ADMIN because non-admin requires user-intervention. if(new FileUtilities().isElevated()) @@ -202,7 +231,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function() { const rntVersion = '2.2'; const fullyResolvedVersion = '2.2.8'; // 2.2 is very much out of support, so we don't expect this to change to a newer version - const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(fullyResolvedVersion, os.arch()); + const installKey = getInstallKeyCustomArchitecture(fullyResolvedVersion, os.arch(), 'runtime', 'local'); const context: IDotnetAcquireContext = { version: rntVersion, requestingExtensionId }; const result = await vscode.commands.executeCommand('dotnet.acquire', context); @@ -257,9 +286,9 @@ suite('DotnetCoreAcquisitionExtension End to End', function() { test('Install Local Runtime Command Passes With Warning With No RequestingExtensionId', async () => { const context: IDotnetAcquireContext = { version: '3.1' }; const result = await vscode.commands.executeCommand('dotnet.acquire', context); - assert.exists(result); - assert.exists(result!.dotnetPath); - assert.include(result!.dotnetPath, context.version); + assert.exists(result, 'A result from the API exists'); + assert.exists(result!.dotnetPath, 'The result has a dotnet path'); + assert.include(result!.dotnetPath, context.version, 'The version is included in the path'); assert.include(mockDisplayWorker.warningMessage, 'Ignoring existing .NET paths'); }).timeout(standardTimeoutTime); diff --git a/vscode-dotnet-runtime-extension/yarn.lock b/vscode-dotnet-runtime-extension/yarn.lock index 5baa82ead4..c7b7650fbd 100644 --- a/vscode-dotnet-runtime-extension/yarn.lock +++ b/vscode-dotnet-runtime-extension/yarn.lock @@ -149,6 +149,13 @@ "@types/glob" "*" "@types/node" "*" +"@types/source-map-support@^0.5.10": + "integrity" "sha1-gk3O+YlJa66Y6dBMjcGsHXDhvTk=" + "resolved" "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.10.tgz" + "version" "0.5.10" + dependencies: + "source-map" "^0.6.0" + "@types/vscode@1.74.0": "integrity" "sha1-StwhtOf1J7iT3jQYwhqR8eUDvc0=" "resolved" "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/vscode/-/vscode-1.74.0.tgz" @@ -1860,12 +1867,13 @@ "chai-as-promised" "^7.1.1" "eol" "^0.9.1" "get-proxy-settings" "^0.1.13" - "https-proxy-agent" "^7.0.2" + "https-proxy-agent" "^7.0.4" "mocha" "^9.1.3" "open" "^8.4.0" "proper-lockfile" "^4.1.2" "rimraf" "3.0.2" "run-script-os" "^1.1.6" + "semver" "^7.6.2" "shelljs" "0.8.5" "typescript" "4.4.4" "vscode-extension-telemetry" "^0.4.3" diff --git a/vscode-dotnet-runtime-library/src/Acquisition/ASPNetRuntimeInstallationDirectoryProvider.ts b/vscode-dotnet-runtime-library/src/Acquisition/ASPNetRuntimeInstallationDirectoryProvider.ts new file mode 100644 index 0000000000..ff59e16765 --- /dev/null +++ b/vscode-dotnet-runtime-library/src/Acquisition/ASPNetRuntimeInstallationDirectoryProvider.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- +* Licensed to the .NET Foundation under one or more agreements. +* The .NET Foundation licenses this file to you under the MIT license. +*--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import { IInstallationDirectoryProvider } from './IInstallationDirectoryProvider'; + +export class ASPNetRuntimeInstallationDirectoryProvider extends IInstallationDirectoryProvider { + public getInstallDir(installKey: string): string + { + const dotnetInstallDir = path.join(this.getStoragePath(), installKey); + return dotnetInstallDir; + } +} diff --git a/vscode-dotnet-runtime-library/src/Acquisition/AcquisitionInvoker.ts b/vscode-dotnet-runtime-library/src/Acquisition/AcquisitionInvoker.ts index 02af7a0355..a0bad8b780 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/AcquisitionInvoker.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/AcquisitionInvoker.ts @@ -18,10 +18,9 @@ import { EventBasedError, } from '../EventStream/EventStreamEvents'; -import { timeoutConstants } from '../Utils/ErrorHandler'; +import { timeoutConstants } from '../Utils/ErrorHandler' import { InstallScriptAcquisitionWorker } from './InstallScriptAcquisitionWorker'; import { TelemetryUtilities } from '../EventStream/TelemetryUtilities'; -import { DotnetCoreAcquisitionWorker } from './DotnetCoreAcquisitionWorker'; import { FileUtilities } from '../Utils/FileUtilities'; import { CommandExecutor } from '../Utils/CommandExecutor'; @@ -31,6 +30,7 @@ import { IAcquisitionInvoker } from './IAcquisitionInvoker'; import { IDotnetInstallationContext } from './IDotnetInstallationContext'; import { IInstallScriptAcquisitionWorker } from './IInstallScriptAcquisitionWorker'; import { DotnetInstall } from './DotnetInstall'; +import { DotnetInstallMode } from './DotnetInstallMode'; /* tslint:disable:no-any */ /* tslint:disable:only-arrow-functions */ @@ -69,10 +69,10 @@ You will need to restart VS Code after these changes. If PowerShell is still not return false; } - public async installDotnet(installContext: IDotnetInstallationContext, installKey : DotnetInstall): Promise + public async installDotnet(installContext: IDotnetInstallationContext, install : DotnetInstall): Promise { const winOS = os.platform() === 'win32'; - const installCommand = await this.getInstallCommand(installContext.version, installContext.installDir, installContext.installRuntime, installContext.architecture); + const installCommand = await this.getInstallCommand(installContext.version, installContext.installDir, installContext.installMode, installContext.architecture); return new Promise(async (resolve, reject) => { @@ -81,7 +81,7 @@ You will need to restart VS Code after these changes. If PowerShell is still not let windowsFullCommand = `powershell.exe -NoProfile -NonInteractive -NoLogo -ExecutionPolicy unrestricted -Command "& { [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12; & ${installCommand} }"`; if(winOS) { - const powershellReference = await this.verifyPowershellCanRun(installContext, installKey); + const powershellReference = await this.verifyPowershellCanRun(installContext, install); windowsFullCommand = windowsFullCommand.replace('powershell.exe', powershellReference); } @@ -91,42 +91,42 @@ You will need to restart VS Code after these changes. If PowerShell is still not { if (stdout) { - this.eventStream.post(new DotnetAcquisitionScriptOutput(installKey, TelemetryUtilities.HashAllPaths(stdout))); + this.eventStream.post(new DotnetAcquisitionScriptOutput(install, TelemetryUtilities.HashAllPaths(stdout))); } if (stderr) { - this.eventStream.post(new DotnetAcquisitionScriptOutput(installKey, `STDERR: ${TelemetryUtilities.HashAllPaths(stderr)}`)); + this.eventStream.post(new DotnetAcquisitionScriptOutput(install, `STDERR: ${TelemetryUtilities.HashAllPaths(stderr)}`)); } if (error) { if (!(await this.isOnline(installContext))) { const offlineError = new EventBasedError('DotnetOfflineFailure', 'No internet connection detected: Cannot install .NET'); - this.eventStream.post(new DotnetOfflineFailure(offlineError, installKey)); + this.eventStream.post(new DotnetOfflineFailure(offlineError, install)); reject(offlineError); } else if (error.signal === 'SIGKILL') { const newError = new EventBasedError('DotnetAcquisitionTimeoutError', `${timeoutConstants.timeoutMessage}, MESSAGE: ${error.message}, CODE: ${error.code}, KILLED: ${error.killed}`, error.stack); - this.eventStream.post(new DotnetAcquisitionTimeoutError(error, installKey, installContext.timeoutSeconds)); + this.eventStream.post(new DotnetAcquisitionTimeoutError(error, install, installContext.timeoutSeconds)); reject(newError); } else { const newError = new EventBasedError('DotnetAcquisitionInstallError', `${timeoutConstants.timeoutMessage}, MESSAGE: ${error.message}, CODE: ${error.code}, SIGNAL: ${error.signal}`, error.stack); - this.eventStream.post(new DotnetAcquisitionInstallError(newError, installKey)); + this.eventStream.post(new DotnetAcquisitionInstallError(newError, install)); reject(newError); } } else if (stderr && stderr.length > 0) { - this.eventStream.post(new DotnetAcquisitionCompleted(installKey, installContext.dotnetPath, installContext.version)); + this.eventStream.post(new DotnetAcquisitionCompleted(install, installContext.dotnetPath, installContext.version)); resolve(); } else { - this.eventStream.post(new DotnetAcquisitionCompleted(installKey, installContext.dotnetPath, installContext.version)); + this.eventStream.post(new DotnetAcquisitionCompleted(install, installContext.dotnetPath, installContext.version)); resolve(); } }); @@ -134,23 +134,27 @@ You will need to restart VS Code after these changes. If PowerShell is still not catch (error : any) { const newError = new EventBasedError('DotnetAcquisitionUnexpectedError', error?.message, error?.stack) - this.eventStream.post(new DotnetAcquisitionUnexpectedError(newError, installKey)); + this.eventStream.post(new DotnetAcquisitionUnexpectedError(newError, install)); reject(newError); } }); } - private async getInstallCommand(version: string, dotnetInstallDir: string, installRuntime: boolean, architecture: string): Promise { + private async getInstallCommand(version: string, dotnetInstallDir: string, installMode: DotnetInstallMode, architecture: string): Promise { const arch = this.fileUtilities.nodeArchToDotnetArch(architecture, this.eventStream); let args = [ '-InstallDir', this.escapeFilePath(dotnetInstallDir), '-Version', version, '-Verbose' ]; - if (installRuntime) + if (installMode === 'runtime') { args = args.concat('-Runtime', 'dotnet'); } + else if(installMode === 'aspnetcore') + { + args = args.concat('-Runtime', 'aspnetcore'); + } if(arch !== 'auto') { args = args.concat('-Architecture', arch); diff --git a/vscode-dotnet-runtime-library/src/Acquisition/DirectoryProviderFactory.ts b/vscode-dotnet-runtime-library/src/Acquisition/DirectoryProviderFactory.ts index 30955de58f..5572d72e3c 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/DirectoryProviderFactory.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/DirectoryProviderFactory.ts @@ -4,6 +4,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ +import { ASPNetRuntimeInstallationDirectoryProvider } from './ASPNetRuntimeInstallationDirectoryProvider'; import { DotnetInstallMode } from './DotnetInstallMode'; import { IInstallationDirectoryProvider } from './IInstallationDirectoryProvider'; import { RuntimeInstallationDirectoryProvider } from './RuntimeInstallationDirectoryProvider'; @@ -12,5 +13,8 @@ import { SdkInstallationDirectoryProvider } from './SdkInstallationDirectoryProv export function directoryProviderFactory(mode: DotnetInstallMode, storagePath: string) : IInstallationDirectoryProvider { - return mode === 'runtime' ? new RuntimeInstallationDirectoryProvider(storagePath) : new SdkInstallationDirectoryProvider(storagePath); + return mode === 'runtime' ? new RuntimeInstallationDirectoryProvider(storagePath) : + mode === 'sdk' ? new SdkInstallationDirectoryProvider(storagePath) : + mode === 'aspnetcore' ? new ASPNetRuntimeInstallationDirectoryProvider(storagePath) + : new RuntimeInstallationDirectoryProvider(storagePath); // default if no mode is provided - should never happen } diff --git a/vscode-dotnet-runtime-library/src/Acquisition/DotnetCoreAcquisitionWorker.ts b/vscode-dotnet-runtime-library/src/Acquisition/DotnetCoreAcquisitionWorker.ts index 7385d2858a..6efaf813f7 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/DotnetCoreAcquisitionWorker.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/DotnetCoreAcquisitionWorker.ts @@ -38,7 +38,7 @@ import { WinMacGlobalInstaller } from './WinMacGlobalInstaller'; import { LinuxGlobalInstaller } from './LinuxGlobalInstaller'; import { TelemetryUtilities } from '../EventStream/TelemetryUtilities'; import { Debugging } from '../Utils/Debugging'; -import { DotnetInstallType, IDotnetAcquireContext} from '../IDotnetAcquireContext'; +import { DotnetInstallType } from '../IDotnetAcquireContext'; import { IGlobalInstaller } from './IGlobalInstaller'; import { IVSCodeExtensionContext } from '../IVSCodeExtensionContext'; import { IUtilityContext } from '../Utils/IUtilityContext'; @@ -62,6 +62,7 @@ import { DotnetInstallMode } from './DotnetInstallMode'; import { IEventStream } from '../EventStream/EventStream'; import { strict } from 'assert'; import { IExtensionState } from '../IExtensionState'; +import { getInstallKeyCustomArchitecture } from '../Utils/InstallKeyUtilities'; /* tslint:disable:no-any */ @@ -99,7 +100,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker * @remarks this is simply a wrapper around the acquire function. * @returns the requested dotnet path. */ - public async acquireSDK(context: IAcquisitionWorkerContext, invoker : IAcquisitionInvoker): Promise { + public async acquireLocalSDK(context: IAcquisitionWorkerContext, invoker : IAcquisitionInvoker): Promise { return this.acquire(context, 'sdk', undefined, invoker); } @@ -109,12 +110,18 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker return this.acquire(context, 'sdk', installerResolver); } + public async acquireLocalASPNET(context: IAcquisitionWorkerContext, invoker : IAcquisitionInvoker) + { + return this.acquire(context, 'aspnetcore', undefined, invoker); + } + /** * * @remarks this is simply a wrapper around the acquire function. * @returns the requested dotnet path. */ - public async acquireRuntime(context: IAcquisitionWorkerContext, invoker : IAcquisitionInvoker): Promise { + public async acquireLocalRuntime(context: IAcquisitionWorkerContext, invoker : IAcquisitionInvoker): Promise + { return this.acquire(context, 'runtime', undefined, invoker); } @@ -191,7 +198,8 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker { install = { - installKey: DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(version, context.acquisitionContext.architecture, globalInstallerResolver !== null ? 'global' : 'local'), + installKey: getInstallKeyCustomArchitecture(version, context.acquisitionContext.architecture, + context.acquisitionContext.mode!, globalInstallerResolver !== null ? 'global' : 'local'), version: install.version, isGlobal: install.isGlobal, installMode: mode, @@ -240,22 +248,6 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker } } - public static getInstallKeyCustomArchitecture(version : string, architecture: string | null | undefined, - installType : DotnetInstallType = 'local') : string - { - if(architecture === null || architecture === 'null') - { - // Use the legacy method (no architecture) of installs - return installType === 'global' ? `${version}-global` : version; - } - else if(architecture === undefined) - { - architecture = DotnetCoreAcquisitionWorker.defaultArchitecture(); - } - - return installType === 'global' ? `${version}-global~${architecture}` : `${version}~${architecture}`; - } - /** * * @param version The version of the object to acquire. @@ -302,7 +294,6 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker version, dotnetPath, timeoutSeconds: context.timeoutSeconds, - installRuntime : mode === 'runtime', installMode : mode, installType : context.acquisitionContext.installType ?? 'local', // Before this API param existed, all calls were for local types. architecture: context.acquisitionContext.architecture ?? this.getDefaultInternalArchitecture(context.acquisitionContext.architecture), @@ -378,7 +369,7 @@ export class DotnetCoreAcquisitionWorker implements IDotnetCoreAcquisitionWorker } else { - const newError = new EventBasedError('DotnetAcquisitionError', `.NET Acquisition Failed: ${error?.message ?? error}`); + const newError = new EventBasedError('DotnetAcquisitionError', `.NET Acquisition Failed: ${error?.message ?? JSON.stringify(error)}`); return newError; } } diff --git a/vscode-dotnet-runtime-library/src/Acquisition/DotnetInstall.ts b/vscode-dotnet-runtime-library/src/Acquisition/DotnetInstall.ts index 7887ef4c36..944e08a214 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/DotnetInstall.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/DotnetInstall.ts @@ -3,8 +3,8 @@ * The .NET Foundation licenses this file to you under the MIT license. *--------------------------------------------------------------------------------------------*/ -import { DotnetInstallType } from '..'; -import { DotnetCoreAcquisitionWorker } from './DotnetCoreAcquisitionWorker'; +import { DotnetInstallType } from '../IDotnetAcquireContext'; +import { getInstallKeyCustomArchitecture } from '../Utils/InstallKeyUtilities'; import { DotnetInstallMode } from './DotnetInstallMode'; export interface DotnetInstall { @@ -71,7 +71,7 @@ export function looksLikeRuntimeVersion(version: string): boolean { export function GetDotnetInstallInfo(installVersion: string, installationMode: DotnetInstallMode, installType: DotnetInstallType, installArchitecture: string): DotnetInstall { return { - installKey: DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(installVersion, installArchitecture, installType), + installKey: getInstallKeyCustomArchitecture(installVersion, installArchitecture, installationMode, installType), version: installVersion, architecture: installArchitecture, isGlobal: installType === 'global', diff --git a/vscode-dotnet-runtime-library/src/Acquisition/IAcquisitionWorkerContext.ts b/vscode-dotnet-runtime-library/src/Acquisition/IAcquisitionWorkerContext.ts index df76de65db..f5011dee0b 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/IAcquisitionWorkerContext.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/IAcquisitionWorkerContext.ts @@ -7,7 +7,6 @@ import { IEventStream } from '../EventStream/EventStream'; import { IExtensionState } from '../IExtensionState'; import { IInstallationDirectoryProvider } from './IInstallationDirectoryProvider'; import { IInstallationValidator } from './IInstallationValidator'; -import { DotnetInstallMode } from './DotnetInstallMode'; export interface IAcquisitionWorkerContext { @@ -18,7 +17,6 @@ export interface IAcquisitionWorkerContext timeoutSeconds: number; installDirectoryProvider: IInstallationDirectoryProvider; acquisitionContext : IDotnetAcquireContext; - installMode: DotnetInstallMode; proxyUrl? : string | undefined; isExtensionTelemetryInitiallyEnabled : boolean; } diff --git a/vscode-dotnet-runtime-library/src/Acquisition/IDotnetCoreAcquisitionWorker.ts b/vscode-dotnet-runtime-library/src/Acquisition/IDotnetCoreAcquisitionWorker.ts index 350d854a34..4fcc268d90 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/IDotnetCoreAcquisitionWorker.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/IDotnetCoreAcquisitionWorker.ts @@ -14,9 +14,11 @@ export interface IDotnetCoreAcquisitionWorker { uninstallAll(eventStream : IEventStream, storagePath : string, extensionState : IExtensionState): void; - acquireRuntime(context: IAcquisitionWorkerContext, invoker : IAcquisitionInvoker): Promise; + acquireLocalRuntime(context: IAcquisitionWorkerContext, invoker : IAcquisitionInvoker): Promise; - acquireSDK(context: IAcquisitionWorkerContext, invoker : IAcquisitionInvoker): Promise; + acquireLocalASPNET(context: IAcquisitionWorkerContext, invoker : IAcquisitionInvoker) : Promise; + + acquireLocalSDK(context: IAcquisitionWorkerContext, invoker : IAcquisitionInvoker): Promise; acquireGlobalSDK(context: IAcquisitionWorkerContext, installerResolver: GlobalInstallerResolver): Promise; } diff --git a/vscode-dotnet-runtime-library/src/Acquisition/IDotnetInstallationContext.ts b/vscode-dotnet-runtime-library/src/Acquisition/IDotnetInstallationContext.ts index 983625566d..d47c59dae3 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/IDotnetInstallationContext.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/IDotnetInstallationContext.ts @@ -11,7 +11,6 @@ export interface IDotnetInstallationContext { version: string; dotnetPath: string; timeoutSeconds: number; - installRuntime: boolean; // kept to remove breaking change installMode : DotnetInstallMode; installType : DotnetInstallType; architecture: string; diff --git a/vscode-dotnet-runtime-library/src/Acquisition/IVersionResolver.ts b/vscode-dotnet-runtime-library/src/Acquisition/IVersionResolver.ts index 348dd2c321..33c6f3c08a 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/IVersionResolver.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/IVersionResolver.ts @@ -3,7 +3,9 @@ * The .NET Foundation licenses this file to you under the MIT license. *--------------------------------------------------------------------------------------------*/ -export interface IVersionResolver { - getFullRuntimeVersion(version: string): Promise; - getFullSDKVersion(version: string): Promise; +import { DotnetInstallMode } from './DotnetInstallMode'; + +export interface IVersionResolver +{ + getFullVersion(version: string, mode : DotnetInstallMode): Promise; } diff --git a/vscode-dotnet-runtime-library/src/Acquisition/InstallTrackerSingleton.ts b/vscode-dotnet-runtime-library/src/Acquisition/InstallTrackerSingleton.ts index e234e6a145..716bca5cd1 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/InstallTrackerSingleton.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/InstallTrackerSingleton.ts @@ -33,7 +33,6 @@ import { import { getVersionFromLegacyInstallKey, getAssumedInstallInfo } from '../Utils/InstallKeyUtilities'; import { InstallRecord, InstallRecordOrStr } from './InstallRecord'; import { DotnetCoreAcquisitionWorker } from './DotnetCoreAcquisitionWorker'; -import { error } from 'console'; import { IEventStream } from '../EventStream/EventStream'; import { IExtensionState } from '../IExtensionState'; /* tslint:disable:no-any */ diff --git a/vscode-dotnet-runtime-library/src/Acquisition/VersionResolver.ts b/vscode-dotnet-runtime-library/src/Acquisition/VersionResolver.ts index 5b07cb85c1..ccac967a27 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/VersionResolver.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/VersionResolver.ts @@ -59,8 +59,7 @@ export class VersionResolver implements IVersionResolver { */ public async GetAvailableDotnetVersions(commandContext: IDotnetListVersionsContext | undefined) : Promise { - // If shouldObtainSdkVersions === false, get Runtimes. Else, get Sdks. - const shouldObtainSdkVersions : boolean = !commandContext?.listRuntimes; + const getSdkVersions : boolean = !commandContext?.listRuntimes; const availableVersions : IDotnetListVersionsResult = []; const response : any = await this.webWorker.getCachedData(); @@ -75,17 +74,17 @@ export class VersionResolver implements IVersionResolver { } else { - const sdkDetailsJson = response['releases-index']; + const releases = response['releases-index']; - for(const availableSdk of sdkDetailsJson) + for(const release of releases) { - if(availableSdk['release-type'] === 'lts' || availableSdk['release-type'] === 'sts') + if(release['release-type'] === 'lts' || release['release-type'] === 'sts') { availableVersions.push({ - supportStatus: (availableSdk['release-type'] as DotnetVersionSupportStatus), - supportPhase: (availableSdk['support-phase'] as DotnetVersionSupportPhase), - version: availableSdk[shouldObtainSdkVersions ? 'latest-sdk' : 'latest-runtime'], - channelVersion: availableSdk['channel-version'] + supportStatus: (release['release-type'] as DotnetVersionSupportStatus), + supportPhase: (release['support-phase'] as DotnetVersionSupportPhase), + version: release[getSdkVersions ? 'latest-sdk' : 'latest-runtime'], + channelVersion: release['channel-version'] } as IDotnetVersion ); } @@ -96,18 +95,7 @@ export class VersionResolver implements IVersionResolver { }); } - public async getFullRuntimeVersion(version: string): Promise { - return this.getFullVersion(version, 'runtime'); - } - - public async getFullSDKVersion(version: string): Promise { - return this.getFullVersion(version, 'sdk'); - } - - /** - * @param getRuntimeVersion - True for getting the full runtime version, false for the SDK version. - */ - private async getFullVersion(version: string, mode: DotnetInstallMode): Promise + public async getFullVersion(version: string, mode: DotnetInstallMode): Promise { let releasesVersions : IDotnetListVersionsResult; try @@ -136,12 +124,13 @@ export class VersionResolver implements IVersionResolver { }); } - private resolveVersion(version: string, releases: IDotnetListVersionsResult): string { - Debugging.log(`Resolving the version: ${version}`, this.context.eventStream); + private resolveVersion(version: string, releases: IDotnetListVersionsResult): string + { this.validateVersionInput(version); // Search for the specific version let matchingVersion = releases.filter((availableVersions : IDotnetVersion) => availableVersions.version === version); + // If a x.y version is given, just find that instead (which is how almost all requests are given atm) if(!matchingVersion || matchingVersion.length < 1) { @@ -151,7 +140,7 @@ export class VersionResolver implements IVersionResolver { { const err = new DotnetVersionResolutionError(new EventCancellationError('DotnetVersionResolutionError', `The requested and or resolved version is invalid.`), - getAssumedInstallInfo(version, this.context.installMode)); + getAssumedInstallInfo(version, this.context.acquisitionContext.mode!)); this.context.eventStream.post(err); throw err.error; } @@ -177,7 +166,7 @@ export class VersionResolver implements IVersionResolver { 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.installMode)); + getAssumedInstallInfo(version, this.context.acquisitionContext.mode!)); this.context.eventStream.post(err); throw err.error; } @@ -186,7 +175,7 @@ export class VersionResolver implements IVersionResolver { private async getReleasesInfo(mode : DotnetInstallMode): Promise { - const apiContext: IDotnetListVersionsContext = { listRuntimes: mode === 'runtime' }; + const apiContext: IDotnetListVersionsContext = { listRuntimes: mode === 'runtime' || mode === 'aspnetcore' }; const response = await this.GetAvailableDotnetVersions(apiContext); if (!response) diff --git a/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts b/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts index 7847e0273a..7ab5c62198 100644 --- a/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts +++ b/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts @@ -10,6 +10,8 @@ import { IEvent } from './IEvent'; import { TelemetryUtilities } from './TelemetryUtilities'; import { InstallToStrings } from '../Acquisition/DotnetInstall'; import { DotnetInstall } from '../Acquisition/DotnetInstall'; +import { DotnetInstallMode } from '../Acquisition/DotnetInstallMode'; +import { DotnetInstallType } from '../IDotnetAcquireContext'; // tslint:disable max-classes-per-file @@ -29,12 +31,23 @@ export class EventBasedError extends Error } } -export class DotnetAcquisitionStarted extends IEvent { +export abstract class GenericModalEvent extends IEvent +{ + abstract readonly mode : DotnetInstallMode; + abstract readonly installType : DotnetInstallType; +} + +export class DotnetAcquisitionStarted extends GenericModalEvent +{ public readonly eventName = 'DotnetAcquisitionStarted'; public readonly type = EventType.DotnetAcquisitionStart; + public readonly mode : DotnetInstallMode; + public readonly installType: DotnetInstallType; constructor(public readonly install: DotnetInstall, public readonly startingVersion: string, public readonly requestingExtensionId = '') { super(); + this.mode = install.installMode; + this.installType = install.isGlobal ? 'global' : 'local'; } public getProperties() { @@ -47,10 +60,8 @@ export class DotnetAcquisitionStarted extends IEvent { } } -export class DotnetRuntimeAcquisitionStarted extends IEvent { - public readonly eventName = 'DotnetRuntimeAcquisitionStarted'; - public readonly type = EventType.DotnetRuntimeAcquisitionStart; - +abstract class DotnetAcquisitionStartedBase extends IEvent +{ constructor(public readonly requestingExtensionId = '') { super(); } @@ -60,79 +71,157 @@ export class DotnetRuntimeAcquisitionStarted extends IEvent { } } -export class DotnetSDKAcquisitionStarted extends IEvent { +export class DotnetRuntimeAcquisitionStarted extends DotnetAcquisitionStartedBase { + public readonly eventName = 'DotnetRuntimeAcquisitionStarted'; + public readonly type = EventType.DotnetModalChildEvent; +} + +export class DotnetSDKAcquisitionStarted extends DotnetAcquisitionStartedBase { public readonly eventName = 'DotnetSDKAcquisitionStarted'; - public readonly type = EventType.DotnetSDKAcquisitionStart; + public readonly type = EventType.DotnetModalChildEvent; +} - constructor(public readonly requestingExtensionId = '') { - super(); - } +export class DotnetGlobalSDKAcquisitionStarted extends DotnetAcquisitionStartedBase { + public readonly eventName = 'DotnetGlobalSDKAcquisitionStarted'; + public readonly type = EventType.DotnetModalChildEvent; +} - public getProperties() { - return {extensionId : TelemetryUtilities.HashData(this.requestingExtensionId)}; - } +export class DotnetASPNetRuntimeAcquisitionStarted extends DotnetAcquisitionStartedBase { + public readonly eventName = 'DotnetASPNetRuntimeAcquisitionStarted'; + public readonly type = EventType.DotnetModalChildEvent; } -export class DotnetAcquisitionCompleted extends IEvent { - public readonly eventName = 'DotnetAcquisitionCompleted'; - public readonly type = EventType.DotnetAcquisitionCompleted; +export class DotnetAcquisitionTotalSuccessEvent extends GenericModalEvent +{ + public readonly type = EventType.DotnetTotalSuccessEvent; + public readonly eventName = 'DotnetAcquisitionTotalSuccessEvent'; + public readonly mode; + public readonly installType: DotnetInstallType; - constructor(public readonly install: DotnetInstall, public readonly dotnetPath: string, public readonly version: string) { + constructor(public readonly startingVersion: string, public readonly install: DotnetInstall, public readonly requestingExtensionId = '', public readonly finalPath: string) { super(); + this.mode = install.installMode; + this.installType = install.isGlobal ? 'global' : 'local'; } - public getProperties(telemetry = false): { [key: string]: string } | undefined { - if (telemetry) { - return {...InstallToStrings(this.install), - AcquisitionCompletedInstallKey : this.install.installKey, - AcquisitionCompletedVersion: this.version}; - } else { - return {...InstallToStrings(this.install), - AcquisitionCompletedVersion: this.version, - AcquisitionCompletedInstallKey : this.install.installKey, - AcquisitionCompletedDotnetPath : this.dotnetPath}; - } - + public getProperties() { + return { + AcquisitionStartVersion : this.startingVersion, + AcquisitionInstallKey : this.install.installKey, + ...InstallToStrings(this.install), + ExtensionId : TelemetryUtilities.HashData(this.requestingExtensionId), + FinalPath : this.finalPath, + }; } } -export class DotnetRuntimeAcquisitionTotalSuccessEvent extends IEvent +abstract class DotnetAcquisitionTotalSuccessEventBase extends IEvent { - public readonly eventName = 'DotnetRuntimeAcquisitionTotalSuccessEvent'; - public readonly type = EventType.DotnetTotalSuccessEvent; - + public readonly type = EventType.DotnetModalChildEvent; - constructor(public readonly startingVersion: string, public readonly installKey: DotnetInstall, public readonly requestingExtensionId = '', public readonly finalPath: string) { + constructor(public readonly installKey: DotnetInstall) { super(); } public getProperties() { return { - AcquisitionStartVersion : this.startingVersion, - AcquisitionInstallKey : this.installKey.installKey, ...InstallToStrings(this.installKey), - ExtensionId : TelemetryUtilities.HashData(this.requestingExtensionId), - FinalPath : this.finalPath, }; } } -export class DotnetGlobalSDKAcquisitionTotalSuccessEvent extends IEvent +export class DotnetRuntimeAcquisitionTotalSuccessEvent extends DotnetAcquisitionTotalSuccessEventBase +{ + public readonly eventName = 'DotnetRuntimeAcquisitionTotalSuccessEvent'; +} + +export class DotnetGlobalSDKAcquisitionTotalSuccessEvent extends DotnetAcquisitionTotalSuccessEventBase { public readonly eventName = 'DotnetGlobalSDKAcquisitionTotalSuccessEvent'; +} + +export class DotnetASPNetRuntimeAcquisitionTotalSuccessEvent extends DotnetAcquisitionTotalSuccessEventBase +{ + public readonly eventName = 'DotnetASPNetRuntimeAcquisitionTotalSuccessEvent'; +} + + +export class DotnetAcquisitionRequested extends GenericModalEvent +{ + public readonly eventName = 'DotnetAcquisitionRequested'; public readonly type = EventType.DotnetTotalSuccessEvent; + public readonly mode; + public readonly installType: DotnetInstallType; - constructor(public readonly startingVersion: string, public readonly installKey: DotnetInstall, public readonly requestingExtensionId = '', public readonly finalPath: string) { + constructor(public readonly startingVersion: string, public readonly requestingId = '', mode : DotnetInstallMode, installType : DotnetInstallType) + { super(); + this.mode = mode; + this.installType = installType; } public getProperties() { + return {AcquisitionStartVersion : this.startingVersion, + RequestingExtensionId: TelemetryUtilities.HashData(this.requestingId)}; + } +} + + +abstract class DotnetAcquisitionRequestedEventBase extends IEvent +{ + public readonly type = EventType.DotnetModalChildEvent; + + constructor(public readonly startingVersion: string, public readonly requestingId = '', public readonly mode : DotnetInstallMode) { + super(); + } + + public getProperties() + { return { - AcquisitionStartVersion : this.startingVersion, - ...InstallToStrings(this.installKey), - ExtensionId : TelemetryUtilities.HashData(this.requestingExtensionId), - FinalPath : this.finalPath, - }; + AcquisitionStartVersion : this.startingVersion, + RequestingExtensionId: TelemetryUtilities.HashData(this.requestingId), + Mode: this.mode + }; + } +} + +export class DotnetRuntimeAcquisitionRequested extends DotnetAcquisitionRequestedEventBase +{ + public readonly eventName = 'DotnetRuntimeAcquisitionRequested'; +} + +export class DotnetGlobalSDKAcquisitionRequested extends DotnetAcquisitionRequestedEventBase +{ + public readonly eventName = 'DotnetGlobalSDKAcquisitionRequested'; +} + +export class DotnetASPNetRuntimeAcquisitionRequested extends DotnetAcquisitionRequestedEventBase +{ + public readonly eventName = 'DotnetASPNetRuntimeAcquisitionRequested'; +} + + +export class DotnetAcquisitionCompleted extends IEvent { + public readonly eventName = 'DotnetAcquisitionCompleted'; + public readonly type = EventType.DotnetAcquisitionCompleted; + + constructor(public readonly install: DotnetInstall, public readonly dotnetPath: string, public readonly version: string) { + super(); + } + + public getProperties(telemetry = false): { [key: string]: string } | undefined { + if (telemetry) { + return {...InstallToStrings(this.install), + AcquisitionCompletedInstallKey : this.install.installKey, + AcquisitionCompletedVersion: this.version}; + } + else + { + return {...InstallToStrings(this.install), + AcquisitionCompletedVersion: this.version, + AcquisitionCompletedInstallKey : this.install.installKey, + AcquisitionCompletedDotnetPath : this.dotnetPath}; + } } } @@ -160,16 +249,37 @@ export abstract class DotnetAcquisitionError extends IEvent { } } +export class DotnetAcquisitionFinalError extends GenericModalEvent +{ + public readonly type = EventType.DotnetTotalSuccessEvent; + public readonly eventName = 'DotnetAcquisitionTotalSuccessEvent'; + public readonly mode; + public readonly installType: DotnetInstallType; + + constructor(public readonly error: Error, public readonly originalEventName : string, public readonly install: DotnetInstall) + { + super(); + this.mode = install.installMode; + this.installType = install.isGlobal ? 'global' : 'local'; + } + + public getProperties(telemetry = false): { [key: string]: string } | undefined { + return {ErrorName : this.error.name, + ErrorMessage : this.error.message, + StackTrace : this.error.stack ? TelemetryUtilities.HashAllPaths(this.error.stack) : '', + InstallKey : this.install?.installKey ?? 'null', + ...InstallToStrings(this.install!)}; + } +} + /** - * @remarks A wrapper around events to detect them as a failure to install the Global SDK. + * @remarks A wrapper around events to detect them as a failure to install. * This allows us to count all errors and analyze them into categories. * The event name for the failure cause is stored in the originalEventName property. */ -export class DotnetGlobalSDKAcquisitionError extends DotnetAcquisitionError +abstract class DotnetAcquisitionFinalErrorBase extends DotnetAcquisitionError { - public eventName = 'DotnetGlobalSDKAcquisitionError'; - - constructor(public readonly error: Error, public readonly originalEventName : string, public readonly install: DotnetInstall | null) + constructor(public readonly error: Error, public readonly originalEventName : string, public readonly install: DotnetInstall) { super(error, install); } @@ -186,6 +296,21 @@ export class DotnetGlobalSDKAcquisitionError extends DotnetAcquisitionError } } +export class DotnetGlobalSDKAcquisitionError extends DotnetAcquisitionFinalErrorBase +{ + public eventName = 'DotnetGlobalSDKAcquisitionError'; +} + +export class DotnetRuntimeFinalAcquisitionError extends DotnetAcquisitionFinalErrorBase +{ + public eventName = 'DotnetRuntimeFinalAcquisitionError'; +} + +export class DotnetASPNetRuntimeFinalAcquisitionError extends DotnetAcquisitionFinalErrorBase +{ + public eventName = 'DotnetASPNetRuntimeFinalAcquisitionError'; +} + export abstract class DotnetNonAcquisitionError extends IEvent { public readonly type = EventType.DotnetAcquisitionError; public isError = true; @@ -894,20 +1019,6 @@ export class DotnetInstallationValidated extends DotnetAcquisitionMessage { } } -export class DotnetAcquisitionRequested extends DotnetAcquisitionMessage { - public readonly eventName = 'DotnetAcquisitionRequested'; - - constructor(public readonly startingVersion: string, - public readonly requestingId = '') { - super(); - } - - public getProperties() { - return {AcquisitionStartVersion : this.startingVersion, - RequestingExtensionId: TelemetryUtilities.HashData(this.requestingId)}; - } -} - export class DotnetAcquisitionStatusRequested extends DotnetAcquisitionMessage { public readonly eventName = 'DotnetAcquisitionStatusRequested'; diff --git a/vscode-dotnet-runtime-library/src/EventStream/EventStreamRegistration.ts b/vscode-dotnet-runtime-library/src/EventStream/EventStreamRegistration.ts index fc48d13d9e..3f4beee66d 100644 --- a/vscode-dotnet-runtime-library/src/EventStream/EventStreamRegistration.ts +++ b/vscode-dotnet-runtime-library/src/EventStream/EventStreamRegistration.ts @@ -15,6 +15,7 @@ import { StatusBarObserver } from './StatusBarObserver'; import { ITelemetryReporter, TelemetryObserver } from './TelemetryObserver'; import { IVSCodeExtensionContext } from '../IVSCodeExtensionContext'; import { IUtilityContext } from '../Utils/IUtilityContext'; +import { ModalEventRepublisher } from './ModalEventPublisher'; export interface IPackageJson { version: string; @@ -33,7 +34,7 @@ export interface IEventStreamContext { } export function registerEventStream(context: IEventStreamContext, extensionContext : IVSCodeExtensionContext, - utilityContext : IUtilityContext): [EventStream, vscode.OutputChannel, LoggingObserver, IEventStreamObserver[], TelemetryObserver | null] + utilityContext : IUtilityContext): [EventStream, vscode.OutputChannel, LoggingObserver, IEventStreamObserver[], TelemetryObserver | null, ModalEventRepublisher] { const outputChannel = vscode.window.createOutputChannel(context.displayChannelName); if (!fs.existsSync(context.logPath)) @@ -62,7 +63,10 @@ export function registerEventStream(context: IEventStreamContext, extensionConte eventStream.subscribe(event => telemetryObserver!.post(event)); } - return [eventStream, outputChannel, loggingObserver, eventStreamObservers, telemetryObserver]; + const modalEventObserver = new ModalEventRepublisher(eventStream); + eventStream.subscribe(event => modalEventObserver.post(event)); + + return [eventStream, outputChannel, loggingObserver, eventStreamObservers, telemetryObserver, modalEventObserver]; } export function enableExtensionTelemetry(extensionConfiguration: IExtensionConfiguration, enableTelemetryKey: string): boolean { diff --git a/vscode-dotnet-runtime-library/src/EventStream/EventType.ts b/vscode-dotnet-runtime-library/src/EventStream/EventType.ts index ac2b4d1907..900b0a870b 100644 --- a/vscode-dotnet-runtime-library/src/EventStream/EventType.ts +++ b/vscode-dotnet-runtime-library/src/EventStream/EventType.ts @@ -7,6 +7,7 @@ export enum EventType { DotnetAcquisitionStart, DotnetSDKAcquisitionStart, DotnetRuntimeAcquisitionStart, + DotnetASPNetRuntimeAcquisitionStarted, DotnetAcquisitionCompleted, DotnetAcquisitionError, DotnetAcquisitionSuccessEvent, @@ -18,5 +19,8 @@ export enum EventType { DotnetTotalSuccessEvent, DotnetUpgradedEvent, SuppressedAcquisitionError, - DotnetInstallExpectedAbort + DotnetInstallExpectedAbort, + + DotnetModalChildEvent, // For sub-events that are published as a more specific version of an existing published generic event. + // Example: DotnetAcquisitionStarted -> Children events are RuntimeStarted, SDKStarted, etc. } diff --git a/vscode-dotnet-runtime-library/src/test/ILoggingObserver.ts b/vscode-dotnet-runtime-library/src/EventStream/IModalEventPublisher.ts similarity index 62% rename from vscode-dotnet-runtime-library/src/test/ILoggingObserver.ts rename to vscode-dotnet-runtime-library/src/EventStream/IModalEventPublisher.ts index 7d171efee6..1341e86127 100644 --- a/vscode-dotnet-runtime-library/src/test/ILoggingObserver.ts +++ b/vscode-dotnet-runtime-library/src/EventStream/IModalEventPublisher.ts @@ -1,12 +1,12 @@ -/*--------------------------------------------------------------------------------------------- -* Licensed to the .NET Foundation under one or more agreements. -* The .NET Foundation licenses this file to you under the MIT license. -*--------------------------------------------------------------------------------------------*/ -import { IEvent } from '../EventStream/IEvent'; -import { IEventStreamObserver } from '../EventStream/IEventStreamObserver'; - -export interface ILoggingObserver extends IEventStreamObserver { - post(event: IEvent): void; - dispose(): void; - getFileLocation(): string; -} +/*--------------------------------------------------------------------------------------------- +* Licensed to the .NET Foundation under one or more agreements. +* The .NET Foundation licenses this file to you under the MIT license. +*--------------------------------------------------------------------------------------------*/ +import { IEvent } from './IEvent'; +import { IEventStreamObserver } from './IEventStreamObserver'; + +export interface IModalEventRepublisher extends IEventStreamObserver +{ + post(event: IEvent): void; + dispose(): void; +} diff --git a/vscode-dotnet-runtime-library/src/EventStream/LoggingObserver.ts b/vscode-dotnet-runtime-library/src/EventStream/LoggingObserver.ts index fe018c1eb2..da2c7d30b5 100644 --- a/vscode-dotnet-runtime-library/src/EventStream/LoggingObserver.ts +++ b/vscode-dotnet-runtime-library/src/EventStream/LoggingObserver.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs'; import { IEvent } from './IEvent'; -import { ILoggingObserver } from '../test/ILoggingObserver'; +import { ILoggingObserver } from './ILoggingObserver'; export class LoggingObserver implements ILoggingObserver { private log: string[] = []; diff --git a/vscode-dotnet-runtime-library/src/EventStream/ModalEventPublisher.ts b/vscode-dotnet-runtime-library/src/EventStream/ModalEventPublisher.ts new file mode 100644 index 0000000000..232525990d --- /dev/null +++ b/vscode-dotnet-runtime-library/src/EventStream/ModalEventPublisher.ts @@ -0,0 +1,117 @@ +/*--------------------------------------------------------------------------------------------- +* Licensed to the .NET Foundation under one or more agreements. +* The .NET Foundation licenses this file to you under the MIT license. +*--------------------------------------------------------------------------------------------*/ +import +{ + DotnetAcquisitionFinalError, + DotnetAcquisitionRequested, + DotnetAcquisitionStarted, + DotnetAcquisitionTotalSuccessEvent, + DotnetASPNetRuntimeAcquisitionRequested, + DotnetASPNetRuntimeAcquisitionStarted, + DotnetASPNetRuntimeAcquisitionTotalSuccessEvent, + DotnetASPNetRuntimeFinalAcquisitionError, + DotnetGlobalSDKAcquisitionError, + DotnetGlobalSDKAcquisitionRequested, + DotnetGlobalSDKAcquisitionTotalSuccessEvent, + DotnetRuntimeAcquisitionRequested, + DotnetRuntimeAcquisitionStarted, + DotnetRuntimeAcquisitionTotalSuccessEvent, + DotnetRuntimeFinalAcquisitionError, + DotnetGlobalSDKAcquisitionStarted, + GenericModalEvent +} from '../EventStream/EventStreamEvents'; +import { IEventStream } from './EventStream'; +import { IEvent } from './IEvent'; +import { IModalEventRepublisher } from './IModalEventPublisher'; +/* tslint:disable:no-empty */ + +export class ModalEventRepublisher implements IModalEventRepublisher +{ + constructor(protected readonly eventStreamReference : IEventStream) {} + + private getSpecificEvent(event : GenericModalEvent) : IEvent | null + { + const mode = event.mode; + + if(event instanceof DotnetAcquisitionStarted) + { + switch(mode) + { + case 'sdk': + return event.installType === 'global' ? new DotnetGlobalSDKAcquisitionStarted(event.requestingExtensionId) : null; + case 'runtime': + return new DotnetRuntimeAcquisitionStarted(event.requestingExtensionId); + case 'aspnetcore': + return new DotnetASPNetRuntimeAcquisitionStarted(event.requestingExtensionId); + default: + break; + } + } + else if(event instanceof DotnetAcquisitionTotalSuccessEvent) + { + switch(mode) + { + case 'sdk': + return event.installType === 'global' ? new DotnetGlobalSDKAcquisitionTotalSuccessEvent(event.install) : null; + case 'runtime': + return new DotnetRuntimeAcquisitionTotalSuccessEvent(event.install); + case 'aspnetcore': + return new DotnetASPNetRuntimeAcquisitionTotalSuccessEvent(event.install); + default: + break; + } + } + else if(event instanceof DotnetAcquisitionFinalError) + { + switch(mode) + { + case 'sdk': + return event.installType === 'global' ? new DotnetGlobalSDKAcquisitionError(event.error, event.originalEventName, event.install) : null; + case 'runtime': + return new DotnetRuntimeFinalAcquisitionError(event.error, event.originalEventName, event.install); + case 'aspnetcore': + return new DotnetASPNetRuntimeFinalAcquisitionError(event.error, event.originalEventName, event.install); + default: + break; + } + } + else if(event instanceof DotnetAcquisitionRequested) + { + switch(mode) + { + case 'sdk': + return event.installType === 'global' ? new DotnetGlobalSDKAcquisitionRequested(event.startingVersion, event.requestingId, event.mode) : null; + case 'runtime': + return new DotnetRuntimeAcquisitionRequested(event.startingVersion, event.requestingId, event.mode); + case 'aspnetcore': + return new DotnetASPNetRuntimeAcquisitionRequested(event.startingVersion, event.requestingId, event.mode); + default: + break; + } + } + + return null; + } + + private republishEvent(event : GenericModalEvent) : void + { + const modeSpecificEvent = this.getSpecificEvent(event); + if(modeSpecificEvent !== null) + { + this.eventStreamReference.post(modeSpecificEvent); + } + } + + public post(event: IEvent): void + { + if(event instanceof GenericModalEvent) + { + this.republishEvent(event); + } + } + + public dispose(): void { + } +} diff --git a/vscode-dotnet-runtime-library/src/EventStream/OutputChannelObserver.ts b/vscode-dotnet-runtime-library/src/EventStream/OutputChannelObserver.ts index 7bc57c9cfd..42f6c6c45a 100644 --- a/vscode-dotnet-runtime-library/src/EventStream/OutputChannelObserver.ts +++ b/vscode-dotnet-runtime-library/src/EventStream/OutputChannelObserver.ts @@ -30,21 +30,19 @@ export class OutputChannelObserver implements IEventStreamObserver { { switch (event.type) { - case EventType.DotnetRuntimeAcquisitionStart: - const runtimeAcquisitionStarted = event as DotnetAcquisitionStarted; - this.outputChannel.append(`${runtimeAcquisitionStarted.requestingExtensionId} requested to download the .NET Runtime.`); - this.outputChannel.appendLine(''); - break; - case EventType.DotnetSDKAcquisitionStart: - const sdkAcquisitionStarted = event as DotnetAcquisitionStarted; - this.outputChannel.append(`${sdkAcquisitionStarted.requestingExtensionId} requested to download the .NET SDK.`); - this.outputChannel.appendLine(''); - break; case EventType.DotnetAcquisitionStart: const acquisitionStarted = event as DotnetAcquisitionStarted; this.inProgressDownloads.push(acquisitionStarted.install.installKey); + this.outputChannel.append(`${acquisitionStarted.requestingExtensionId} requested to download the ${ + acquisitionStarted.install.installMode === 'sdk' ? '.NET SDK' : + acquisitionStarted.install.installMode === 'runtime' ? '.NET Runtime' : + '.NET ASP.NET Runtime' + }.`); + + this.outputChannel.appendLine(''); + if (this.inProgressDownloads.length > 1) { // Already a download in progress this.outputChannel.appendLine(` -- Concurrent download of '${acquisitionStarted.install.installKey}' started!`); diff --git a/vscode-dotnet-runtime-library/src/IDotnetAcquireContext.ts b/vscode-dotnet-runtime-library/src/IDotnetAcquireContext.ts index ef78c18066..b4651386d1 100644 --- a/vscode-dotnet-runtime-library/src/IDotnetAcquireContext.ts +++ b/vscode-dotnet-runtime-library/src/IDotnetAcquireContext.ts @@ -10,7 +10,7 @@ export interface IDotnetAcquireContext { /** * @remarks - * The data required to acquire either the sdk or the runtime. + * The data required to acquire either the sdk or the runtimes. * * @property version - The major.minor version of the SDK or Runtime desired. * @@ -31,12 +31,19 @@ export interface IDotnetAcquireContext * @property architecture - null is for deliberate legacy install behavior that is not-architecture specific. * undefined is for the default of node.arch(). * Does NOT impact global installs yet, it will be ignored. Follows node architecture terminology. + * + * @property mode - Whether the install should be of the sdk, runtime, aspnet runtime, or windowsdesktop runtime. + * For example, set to 'aspnetcore' to install the aspnet runtime. + * Defaults to 'runtime' as this was the default choice before this existed. + * Note: If you try to call an install SDK endpoint or install runtime endpoint with a conflicting mode set, the mode will be ignored. + * Only certain install types / modes combinations are supported at this time, such as local runtimes, local aspnet, and global SDKs. */ version: string; requestingExtensionId?: string; errorConfiguration?: AcquireErrorConfiguration; installType?: DotnetInstallType; architecture?: string | null | undefined; + mode? : DotnetInstallMode; } /** diff --git a/vscode-dotnet-runtime-library/src/Utils/ErrorHandler.ts b/vscode-dotnet-runtime-library/src/Utils/ErrorHandler.ts index d0f69251f2..9d79bfceae 100644 --- a/vscode-dotnet-runtime-library/src/Utils/ErrorHandler.ts +++ b/vscode-dotnet-runtime-library/src/Utils/ErrorHandler.ts @@ -5,9 +5,9 @@ import * as fs from 'fs'; import * as open from 'open'; import { + DotnetAcquisitionFinalError, DotnetCommandFailed, DotnetCommandSucceeded, - DotnetGlobalSDKAcquisitionError, DotnetInstallExpectedAbort, DotnetNotInstallRelatedCommandFailed, EventCancellationError @@ -74,13 +74,12 @@ export async function callWithErrorHandling(callback: () => T, context: IIssu ); } - if(acquireContext?.installMode === 'sdk' && acquireContext.acquisitionContext?.installType === 'global') + if(acquireContext) { - context.eventStream.post(new DotnetGlobalSDKAcquisitionError(error, (caughtError?.eventType) ?? 'Unknown', - GetDotnetInstallInfo(acquireContext.acquisitionContext.version, acquireContext.installMode, 'global', acquireContext.acquisitionContext.architecture ?? - + context.eventStream.post(new DotnetAcquisitionFinalError(error, (caughtError?.eventType) ?? 'Unknown', + GetDotnetInstallInfo(acquireContext.acquisitionContext.version, acquireContext.acquisitionContext.mode!, 'global', acquireContext.acquisitionContext.architecture ?? DotnetCoreAcquisitionWorker.defaultArchitecture() - ))); + ))); } if (context.errorConfiguration === AcquireErrorConfiguration.DisplayAllErrorPopups) diff --git a/vscode-dotnet-runtime-library/src/Utils/IIssueContext.ts b/vscode-dotnet-runtime-library/src/Utils/IIssueContext.ts index 1415d8ec30..427a7e06a4 100644 --- a/vscode-dotnet-runtime-library/src/Utils/IIssueContext.ts +++ b/vscode-dotnet-runtime-library/src/Utils/IIssueContext.ts @@ -3,7 +3,7 @@ * The .NET Foundation licenses this file to you under the MIT license. *--------------------------------------------------------------------------------------------*/ import { IEventStream } from '../EventStream/EventStream'; -import { ILoggingObserver } from '../test/ILoggingObserver'; +import { ILoggingObserver } from '../EventStream/ILoggingObserver'; import { IWindowDisplayWorker } from '../EventStream/IWindowDisplayWorker'; import { ErrorConfiguration } from './ErrorHandler'; import { IExtensionConfigurationWorker } from './IExtensionConfigurationWorker'; diff --git a/vscode-dotnet-runtime-library/src/Utils/InstallKeyUtilities.ts b/vscode-dotnet-runtime-library/src/Utils/InstallKeyUtilities.ts index d392ac9dda..82795228cc 100644 --- a/vscode-dotnet-runtime-library/src/Utils/InstallKeyUtilities.ts +++ b/vscode-dotnet-runtime-library/src/Utils/InstallKeyUtilities.ts @@ -8,7 +8,25 @@ import { looksLikeRuntimeVersion } from '../Acquisition/DotnetInstall'; import { DotnetInstall } from '../Acquisition/DotnetInstall'; import { DOTNET_INSTALL_MODE_LIST, DotnetInstallMode } from '../Acquisition/DotnetInstallMode'; import { IAcquisitionWorkerContext } from '../Acquisition/IAcquisitionWorkerContext'; -import * as os from 'os'; +import { DotnetInstallType } from '../IDotnetAcquireContext'; + + +export function getInstallKeyCustomArchitecture(version : string, architecture: string | null | undefined, mode: DotnetInstallMode, + installType : DotnetInstallType = 'local') : string +{ + if(architecture === null || architecture === 'null') + { + // Use the legacy method (no architecture) of installs + return installType === 'global' ? `${version}-global` : version; + } + else if(architecture === undefined) + { + architecture = DotnetCoreAcquisitionWorker.defaultArchitecture(); + } + + return installType === 'global' ? `${version}-global~${architecture}${mode === 'aspnetcore' ? '~aspnetcore' : ''}` : + `${version}~${architecture}${mode === 'aspnetcore' ? '~aspnetcore' : ''}`; +} export function getInstallKeyFromContext(ctx : IAcquisitionWorkerContext | undefined | null) : DotnetInstall | null { @@ -20,12 +38,12 @@ export function getInstallKeyFromContext(ctx : IAcquisitionWorkerContext | undef const acquireContext = ctx.acquisitionContext!; return { - installKey : DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(acquireContext.version, acquireContext.architecture, + installKey : getInstallKeyCustomArchitecture(acquireContext.version, acquireContext.architecture, ctx.acquisitionContext.mode!, acquireContext.installType), version: acquireContext.version, architecture: acquireContext.architecture, isGlobal: acquireContext.installType ? acquireContext.installType === 'global' : false, - installMode: ctx.installMode + installMode: ctx.acquisitionContext.mode! } as DotnetInstall; @@ -42,7 +60,7 @@ export function isGlobalLegacyInstallKey(installKey: string): boolean { export function getArchFromLegacyInstallKey(installKey: string): string | undefined { const splitKey = installKey.split('~'); - if (splitKey.length === 2) { + if (splitKey.length >= 2) { return splitKey[1]; } return undefined; diff --git a/vscode-dotnet-runtime-library/src/index.ts b/vscode-dotnet-runtime-library/src/index.ts index faee8d7082..160a863cb1 100644 --- a/vscode-dotnet-runtime-library/src/index.ts +++ b/vscode-dotnet-runtime-library/src/index.ts @@ -25,6 +25,7 @@ export * from './Utils/ExtensionConfigurationWorker'; export * from './Utils/FileUtilities'; export * from './Utils/ICommandExecutor'; export * from './Utils/IFileUtilities'; +export * from './Utils/InstallKeyUtilities'; export * from './Utils/IIssueContext'; export * from './Utils/IssueReporter'; export * from './Utils/IVSCodeEnvironment'; diff --git a/vscode-dotnet-runtime-library/src/test/unit/DotnetCoreAcquisitionWorker.test.ts b/vscode-dotnet-runtime-library/src/test/unit/DotnetCoreAcquisitionWorker.test.ts index 9ddef0d07b..bad8385158 100644 --- a/vscode-dotnet-runtime-library/src/test/unit/DotnetCoreAcquisitionWorker.test.ts +++ b/vscode-dotnet-runtime-library/src/test/unit/DotnetCoreAcquisitionWorker.test.ts @@ -17,6 +17,9 @@ import { DotnetUninstallAllCompleted, DotnetUninstallAllStarted, TestAcquireCalled, + DotnetASPNetRuntimeAcquisitionTotalSuccessEvent, + DotnetGlobalSDKAcquisitionTotalSuccessEvent, + DotnetRuntimeAcquisitionTotalSuccessEvent } from '../../EventStream/EventStreamEvents'; import { EventType } from '../../EventStream/EventType'; import { @@ -34,9 +37,9 @@ import { InstallOwner, InstallRecord } from '../../Acquisition/InstallRecord'; import { GetDotnetInstallInfo } from '../../Acquisition/DotnetInstall'; import { DotnetInstallMode } from '../../Acquisition/DotnetInstallMode'; import { IAcquisitionWorkerContext } from '../../Acquisition/IAcquisitionWorkerContext'; -import { InstallTrackerSingleton } from '../../Acquisition/InstallTrackerSingleton'; -import { get } from 'http'; import { IEventStream } from '../../EventStream/EventStream'; +import { DotnetInstallType} from '../../IDotnetAcquireContext'; +import { getInstallKeyCustomArchitecture } from '../../Utils/InstallKeyUtilities'; const assert = chai.assert; chai.use(chaiAsPromised); @@ -63,25 +66,44 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { return [acquisitionWorker, invoker]; } + async function callAcquire(workerContext : IAcquisitionWorkerContext, acquisitionWorker : DotnetCoreAcquisitionWorker, invoker : IAcquisitionInvoker) + { + const result = workerContext.acquisitionContext.mode === undefined || workerContext.acquisitionContext.mode === 'runtime' ? + await acquisitionWorker.acquireLocalRuntime(workerContext, invoker) : + workerContext.acquisitionContext.mode === 'sdk' ? await acquisitionWorker.acquireLocalSDK(workerContext, invoker) : + workerContext.acquisitionContext.mode === 'aspnetcore' ? await acquisitionWorker.acquireLocalASPNET(workerContext, invoker) : + {} as { dotnetPath: string }; + + return result; + } + function migrateContextToNewInstall(worker : IAcquisitionWorkerContext, newVersion : string, newArch : string | null | undefined) { worker.acquisitionContext.version = newVersion; worker.acquisitionContext.architecture = newArch; } - function getExpectedPath(version: string, isRuntimeInstall: boolean): string { - return isRuntimeInstall ? - path.join(dotnetFolderName, version, os.platform() === 'win32' ? 'dotnet.exe' : 'dotnet') : - path.join(dotnetFolderName, os.platform() === 'win32' ? 'dotnet.exe' : 'dotnet'); + function getExpectedPath(installKey: string, mode: DotnetInstallMode): string + { + if(mode === 'runtime' || mode === 'aspnetcore') + { + return path.join(dotnetFolderName, installKey, os.platform() === 'win32' ? 'dotnet.exe' : 'dotnet') + } + else if(mode === 'sdk') + { + return path.join(dotnetFolderName, os.platform() === 'win32' ? 'dotnet.exe' : 'dotnet'); + } + + return 'There is a mode without a designated return path'; } async function assertAcquisitionSucceeded(installKey: string, exePath: string, eventStream: MockEventStream, context: MockExtensionContext, - isRuntimeInstall = true) + mode : DotnetInstallMode = 'runtime') { - const expectedPath = getExpectedPath(installKey, isRuntimeInstall); + const expectedPath = getExpectedPath(installKey, mode); // Path to exe should be correct assert.equal(exePath, expectedPath, 'The exe path is correct'); @@ -104,10 +126,13 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { //  Acquire got called with the correct args const acquireEvent = eventStream.events.find(event => event instanceof TestAcquireCalled && - ((DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture((event as TestAcquireCalled).context.version, - (event as TestAcquireCalled).context.architecture, (event as TestAcquireCalled).context.installType))) - === installKey) as TestAcquireCalled; - assert.exists(acquireEvent, 'The acquisition acquire event appears'); + getInstallKeyCustomArchitecture((event as TestAcquireCalled).context.version, + (event as TestAcquireCalled).context.architecture, mode, (event as TestAcquireCalled).context.installType) + === installKey + ) as TestAcquireCalled; + + assert.exists(acquireEvent, `The acquisition acquire event appears. Events: ${eventStream.events.filter(event => + event instanceof TestAcquireCalled).map((e) => e.eventName).join(', ')};`); assert.equal(acquireEvent!.context.dotnetPath, expectedPath, 'The acquisition went to the expected dotnetPath'); assert.equal(acquireEvent!.context.installDir, path.dirname(expectedPath), 'The acquisition went to the expected installation directory'); } @@ -116,72 +141,99 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { process.env._VSCODE_DOTNET_INSTALL_FOLDER = dotnetFolderName; }); - async function AssertInstallRuntime(acquisitionWorker : DotnetCoreAcquisitionWorker, context : MockExtensionContext, eventStream : MockEventStream, version : string, + async function AssertInstall(acquisitionWorker : DotnetCoreAcquisitionWorker, context : MockExtensionContext, eventStream : MockEventStream, version : string, invoker : IAcquisitionInvoker, workerContext : IAcquisitionWorkerContext) { - const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(workerContext.acquisitionContext.version, workerContext.acquisitionContext.architecture, 'local'); - const result = await acquisitionWorker.acquireRuntime(workerContext, invoker); - await assertAcquisitionSucceeded(installKey, result.dotnetPath, eventStream, context); - } + const installKey = getInstallKeyCustomArchitecture(workerContext.acquisitionContext.version, workerContext.acquisitionContext.architecture, + workerContext.acquisitionContext.mode ?? 'runtime', workerContext.acquisitionContext.installType ?? 'local'); - async function AssertInstallSDK(acquisitionWorker : DotnetCoreAcquisitionWorker, context : MockExtensionContext, eventStream : MockEventStream, version : string, - invoker : IAcquisitionInvoker, workerContext : IAcquisitionWorkerContext) - { - const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(workerContext.acquisitionContext.version, workerContext.acquisitionContext.architecture, 'local'); - const result = await acquisitionWorker.acquireSDK(workerContext, invoker); - await assertAcquisitionSucceeded(installKey, result.dotnetPath, eventStream, context, false); + const result = await callAcquire(workerContext, acquisitionWorker, invoker); + + await assertAcquisitionSucceeded(installKey, result.dotnetPath, eventStream, context, workerContext.acquisitionContext.mode!); } - test('Acquire Runtime Version', async () => { - const version = '1.0'; + async function acquireWithVersion(version : string, mode : DotnetInstallMode) + { const [eventStream, extContext] = setupStates(); - const ctx = getMockAcquisitionContext('runtime', version, expectedTimeoutTime, eventStream, extContext); + const ctx = getMockAcquisitionContext(mode, version, expectedTimeoutTime, eventStream, extContext); const [acquisitionWorker, invoker] = setupWorker(ctx, eventStream); - await AssertInstallRuntime(acquisitionWorker, extContext, eventStream, version, invoker, ctx); - }).timeout(expectedTimeoutTime); - test('Acquire SDK Version', async () => { - const version = '5.0'; - const [eventStream, extContext] = setupStates(); - const ctx = getMockAcquisitionContext('sdk', version, expectedTimeoutTime, eventStream, extContext ); - const [acquisitionWorker, invoker] = setupWorker(ctx, eventStream); - await AssertInstallSDK(acquisitionWorker, extContext, eventStream, version, invoker, ctx); - }).timeout(expectedTimeoutTime); + await AssertInstall(acquisitionWorker, extContext, eventStream, version, invoker, ctx); + } - test('Acquire SDK Status', async () => { - const version = '5.0'; + async function acquireStatus(version : string, mode : DotnetInstallMode, type : DotnetInstallType) + { const [eventStream, extContext] = setupStates(); - const ctx = getMockAcquisitionContext('sdk', version, expectedTimeoutTime, eventStream, extContext); + const ctx = getMockAcquisitionContext(mode, version, expectedTimeoutTime, eventStream, extContext); const [acquisitionWorker, invoker] = setupWorker(ctx, eventStream); - const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, 'local'); - let result = await acquisitionWorker.acquireStatus(ctx, 'sdk'); + const installKey = getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, mode, type); + + let result = await acquisitionWorker.acquireStatus(ctx, mode); assert.isUndefined(result); const undefinedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusUndefined); assert.exists(undefinedEvent, 'Undefined event exists'); - await acquisitionWorker.acquireSDK(ctx, invoker,); - result = await acquisitionWorker.acquireStatus(ctx, 'sdk', undefined); - await assertAcquisitionSucceeded(installKey, result!.dotnetPath, eventStream, extContext, false); + await callAcquire(ctx, acquisitionWorker, invoker); + + result = await acquisitionWorker.acquireStatus(ctx, mode, undefined); + await assertAcquisitionSucceeded(installKey, result!.dotnetPath, eventStream, extContext, mode); const resolvedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusResolved); assert.exists(resolvedEvent, 'The sdk is resolved'); - }).timeout(expectedTimeoutTime); + } - test('Acquire Runtime Status', async () => { - const version = '5.0'; + async function repeatAcquisition(version : string, mode : DotnetInstallMode) + { const [eventStream, extContext] = setupStates(); - const ctx = getMockAcquisitionContext('runtime', version, expectedTimeoutTime, eventStream, extContext); + const ctx = getMockAcquisitionContext(mode, version, expectedTimeoutTime, eventStream, extContext); const [acquisitionWorker, invoker] = setupWorker(ctx, eventStream); - const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, 'local'); - let result = await acquisitionWorker.acquireStatus(ctx, 'sdk'); - assert.isUndefined(result); - const undefinedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusUndefined); - assert.exists(undefinedEvent); - await acquisitionWorker.acquireRuntime(ctx, invoker,); - result = await acquisitionWorker.acquireStatus(ctx, 'runtime', undefined); - await assertAcquisitionSucceeded(installKey, result!.dotnetPath, eventStream, extContext, true); - const resolvedEvent = eventStream.events.find(event => event instanceof DotnetAcquisitionStatusResolved); - assert.exists(resolvedEvent); + for (let i = 0; i < 3; i++) + { + await callAcquire(ctx, acquisitionWorker, invoker); + } + + // We should only actually Acquire once + const events = eventStream.events.filter(event => event instanceof DotnetAcquisitionStarted); + assert.equal(events.length, 1); + } + + async function acquireAndUninstall(version : string, mode : DotnetInstallMode, type : DotnetInstallType) + { + const [eventStream, extContext] = setupStates(); + const ctx = getMockAcquisitionContext(mode, version, expectedTimeoutTime, eventStream, extContext); + const [acquisitionWorker, invoker] = setupWorker(ctx, eventStream); + + const installKey = getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, mode, type); + const res = await callAcquire(ctx, acquisitionWorker, invoker); + await assertAcquisitionSucceeded(installKey, res.dotnetPath, eventStream, extContext, mode); + + await acquisitionWorker.uninstallAll(ctx.eventStream, ctx.installDirectoryProvider.getStoragePath(), ctx.extensionState); + assert.exists(eventStream.events.find(event => event instanceof DotnetUninstallAllStarted)); + assert.exists(eventStream.events.find(event => event instanceof DotnetUninstallAllCompleted)); + assert.isEmpty(extContext.get(installingVersionsKey, [])); + assert.isEmpty(extContext.get(installedVersionsKey, [])); + } + + test('Acquire Runtime Version', async () => + { + await acquireWithVersion('1.0', 'runtime'); + }).timeout(expectedTimeoutTime); + + test('Acquire SDK Version', async () => { + await acquireWithVersion('5.0', 'sdk'); + }).timeout(expectedTimeoutTime); + + test('Acquire ASP.NET Runtime Version', async () => + { + await acquireWithVersion('1.0', 'aspnetcore'); + }).timeout(expectedTimeoutTime); + + test('Acquire SDK Status', async () => { + await acquireStatus('5.0', 'sdk', 'local'); + }).timeout(expectedTimeoutTime); + + test('Acquire Runtime Status', async () => { + await acquireStatus('5.0', 'runtime', 'local'); }).timeout(expectedTimeoutTime); test('Acquire Runtime Version Multiple Times', async () => { @@ -192,8 +244,8 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { const [acquisitionWorker, invoker] = setupWorker(ctx, eventStream); for (let i = 0; i < numAcquisitions; i++) { - const pathResult = await acquisitionWorker.acquireRuntime(ctx, invoker); - const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, 'local'); + const pathResult = await acquisitionWorker.acquireLocalRuntime(ctx, invoker); + const installKey = getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, 'runtime', 'local'); await assertAcquisitionSucceeded(installKey, pathResult.dotnetPath, eventStream, extContext); } @@ -211,8 +263,8 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { for (const version of versions) { migrateContextToNewInstall(ctx, version, os.arch()); - const res = await acquisitionWorker.acquireRuntime(ctx, invoker); - const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, 'local'); + const res = await acquisitionWorker.acquireLocalRuntime(ctx, invoker); + const installKey = getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, 'runtime', 'local'); await assertAcquisitionSucceeded(installKey, res.dotnetPath, eventStream, extContext); } @@ -225,20 +277,17 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { test('Acquire Runtime and UninstallAll', async () => { - const version = '1.0'; - const [eventStream, extContext] = setupStates(); - const ctx = getMockAcquisitionContext('runtime', version, expectedTimeoutTime, eventStream, extContext); - const [acquisitionWorker, invoker] = setupWorker(ctx, eventStream); + await acquireAndUninstall('1.0', 'runtime', 'local'); + }).timeout(expectedTimeoutTime); - const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, 'local'); - const res = await acquisitionWorker.acquireRuntime(ctx, invoker); - await assertAcquisitionSucceeded(installKey, res.dotnetPath, eventStream, extContext); + test('Acquire ASP.NET and UninstallAll', async () => + { + await acquireAndUninstall('1.0', 'aspnetcore', 'local'); + }).timeout(expectedTimeoutTime); - await acquisitionWorker.uninstallAll(ctx.eventStream, ctx.installDirectoryProvider.getStoragePath(), ctx.extensionState); - assert.exists(eventStream.events.find(event => event instanceof DotnetUninstallAllStarted)); - assert.exists(eventStream.events.find(event => event instanceof DotnetUninstallAllCompleted)); - assert.isEmpty(extContext.get(installingVersionsKey, [])); - assert.isEmpty(extContext.get(installedVersionsKey, [])); + test('Acquire SDK and UninstallAll', async () => + { + await acquireAndUninstall('6.0', 'sdk', 'local'); }).timeout(expectedTimeoutTime); test('Graveyard Removes Failed Uninstalls', async () => { @@ -246,16 +295,16 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { const [eventStream, extContext] = setupStates(); const ctx = getMockAcquisitionContext('runtime', version, expectedTimeoutTime, eventStream, extContext); const [acquisitionWorker, invoker] = setupWorker(ctx, eventStream); - const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, 'local'); + const installKey = getInstallKeyCustomArchitecture(ctx.acquisitionContext.version, ctx.acquisitionContext.architecture, 'runtime', 'local'); const install = GetDotnetInstallInfo(version, 'runtime', 'local', os.arch()); - const res = await acquisitionWorker.acquireRuntime(ctx, invoker); + const res = await acquisitionWorker.acquireLocalRuntime(ctx, invoker); await assertAcquisitionSucceeded(installKey, res.dotnetPath, eventStream, extContext); acquisitionWorker.AddToGraveyard(ctx, install, 'Not applicable'); const versionToKeep = '5.0'; migrateContextToNewInstall(ctx, versionToKeep, os.arch()); - await acquisitionWorker.acquireRuntime(ctx, invoker); + await acquisitionWorker.acquireLocalRuntime(ctx, invoker); assert.exists(eventStream.events.find(event => event instanceof DotnetInstallGraveyardEvent), 'The graveyard tried to uninstall .NET'); assert.isEmpty(extContext.get(installingVersionsKey, []), 'We did not hang/ get interrupted during the install.'); @@ -289,19 +338,19 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { const [worker, invoker] = setupWorker(ctx, eventStream); // Install 5.0, 6.0 runtime without an architecture - await AssertInstallRuntime(worker, extensionContext, eventStream, runtimeV5, invoker, ctx); + await AssertInstall(worker, extensionContext, eventStream, runtimeV5, invoker, ctx); migrateContextToNewInstall(ctx, runtimeV6, null); - await AssertInstallRuntime(worker, extensionContext, eventStream, runtimeV6, invoker, ctx); + await AssertInstall(worker, extensionContext, eventStream, runtimeV6, invoker, ctx); // Install similar SDKs without an architecture. const sdkCtx = getMockAcquisitionContext('sdk', sdkV5, expectedTimeoutTime, eventStream, extensionContext, null); - await AssertInstallSDK(worker, extensionContext, eventStream, sdkV5, invoker, sdkCtx); + await AssertInstall(worker, extensionContext, eventStream, sdkV5, invoker, sdkCtx); migrateContextToNewInstall(sdkCtx, sdkV6, null); - await AssertInstallSDK(worker, extensionContext, eventStream, sdkV6, invoker, sdkCtx); + await AssertInstall(worker, extensionContext, eventStream, sdkV6, invoker, sdkCtx); // Install 5.0 runtime with an architecture. Share the same event stream and context. migrateContextToNewInstall(ctx, runtimeV5, os.arch()); - await AssertInstallRuntime(worker, extensionContext, eventStream, runtimeV5, invoker, ctx); + await AssertInstall(worker, extensionContext, eventStream, runtimeV5, invoker, ctx); // 5.0 legacy runtime should be replaced, but 6.0 runtime should remain, and all SDK items should remain. let detailedRemainingInstalls : InstallRecord[] = extensionContext.get(installedVersionsKey, []); @@ -311,11 +360,11 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { // Install a legacy runtime again to make sure its not removed when installing a new SDK with the same version migrateContextToNewInstall(ctx, runtimeV5, null); - await AssertInstallRuntime(worker, extensionContext, eventStream, runtimeV5, invoker, ctx); + await AssertInstall(worker, extensionContext, eventStream, runtimeV5, invoker, ctx); // Install non-legacy SDK migrateContextToNewInstall(sdkCtx, sdkV5, os.arch()); - await AssertInstallSDK(worker, extensionContext, eventStream, sdkV5, invoker, sdkCtx); + await AssertInstall(worker, extensionContext, eventStream, sdkV5, invoker, sdkCtx); // 6.0 sdk legacy should remain, as well as 5.0 and 6.0 runtime. 5.0 SDK should be removed. detailedRemainingInstalls = extensionContext.get(installedVersionsKey, []); @@ -324,19 +373,16 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { 'Only The Requested Legacy SDK is replaced when new SDK is installed'); }).timeout(expectedTimeoutTime * 6); - test('Repeated Acquisition', async () => { - const version = '1.0'; - const [eventStream, extContext] = setupStates(); - const ctx = getMockAcquisitionContext('runtime', version, expectedTimeoutTime, eventStream, extContext); - const [acquisitionWorker, invoker] = setupWorker(ctx, eventStream); + test('Repeated Runtime Acquisition', async () => { + await repeatAcquisition('1.0', 'runtime'); + }).timeout(expectedTimeoutTime); - for (let i = 0; i < 3; i++) - { - await acquisitionWorker.acquireRuntime(ctx, invoker); - } - // We should only actually Acquire once - const events = eventStream.events.filter(event => event instanceof DotnetAcquisitionStarted); - assert.equal(events.length, 1); + test('Repeated ASP.NET Acquisition', async () => { + await repeatAcquisition('1.0', 'aspnetcore'); + }).timeout(expectedTimeoutTime); + + test('Repeated SDK Acquisition', async () => { + await repeatAcquisition('5.0', 'sdk'); }).timeout(expectedTimeoutTime); test('Error is Redirected on Acquisition Failure', async () => { @@ -346,22 +392,7 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { const [acquisitionWorker, _] = setupWorker(ctx, eventStream); const acquisitionInvoker = new RejectingAcquisitionInvoker(eventStream); - return assert.isRejected(acquisitionWorker.acquireRuntime(ctx, acquisitionInvoker), '.NET Acquisition Failed: Rejecting message'); - }).timeout(expectedTimeoutTime); - - test('Repeated SDK Acquisition', async () => { - const version = '5.0'; - const [eventStream, extContext] = setupStates(); - const ctx = getMockAcquisitionContext('sdk', version, expectedTimeoutTime, eventStream, extContext); - const [acquisitionWorker, invoker] = setupWorker(ctx, eventStream); - - for (let i = 0; i < 3; i++) - { - await acquisitionWorker.acquireSDK(ctx, invoker); - } - // We should only actually Acquire once - const events = eventStream.events.filter(event => event instanceof DotnetAcquisitionStarted); - assert.equal(events.length, 1); + return assert.isRejected(acquisitionWorker.acquireLocalRuntime(ctx, acquisitionInvoker), '.NET Acquisition Failed: "Rejecting message"'); }).timeout(expectedTimeoutTime); test('Get Expected Path With Apostrophe In Install path', async () => { @@ -373,9 +404,9 @@ suite('DotnetCoreAcquisitionWorker Unit Tests', function () { const [acquisitionWorker, invoker] = setupWorker(acquisitionContext, eventStream); const acquisitionInvoker = new MockAcquisitionInvoker(acquisitionContext, installApostropheFolder); - const installKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(version, os.arch(), 'local'); - const result = await acquisitionWorker.acquireRuntime(acquisitionContext, acquisitionInvoker); - const expectedPath = getExpectedPath(installKey, true); + const installKey = getInstallKeyCustomArchitecture(version, os.arch(), 'runtime', 'local'); + const result = await acquisitionWorker.acquireLocalRuntime(acquisitionContext, acquisitionInvoker); + const expectedPath = getExpectedPath(installKey, acquisitionContext.acquisitionContext.mode!); assert.equal(result.dotnetPath, expectedPath); deleteFolderRecursive(path.join(process.cwd(), installApostropheFolder)); } diff --git a/vscode-dotnet-runtime-library/src/test/unit/InstallTracker.test.ts b/vscode-dotnet-runtime-library/src/test/unit/InstallTracker.test.ts index 6e39f397dc..6b5752c860 100644 --- a/vscode-dotnet-runtime-library/src/test/unit/InstallTracker.test.ts +++ b/vscode-dotnet-runtime-library/src/test/unit/InstallTracker.test.ts @@ -87,7 +87,6 @@ suite('InstallTracker Unit Tests', () => { const validator = new MockInstallTracker(mockContext.eventStream, mockContext.extensionState); await validator.trackInstalledVersion(mockContext, defaultInstall); - // TODO: verify these tests still work const otherRequesterValidator = new MockInstallTracker(mockContextFromOtherExtension.eventStream, mockContext.extensionState); // Inject the extension state from the old class into the new one, because in vscode its a shared global state but here its mocked otherRequesterValidator.setExtensionState(validator.getExtensionState()); diff --git a/vscode-dotnet-runtime-library/src/test/unit/TestUtility.ts b/vscode-dotnet-runtime-library/src/test/unit/TestUtility.ts index f0edc250e1..75bab0387b 100644 --- a/vscode-dotnet-runtime-library/src/test/unit/TestUtility.ts +++ b/vscode-dotnet-runtime-library/src/test/unit/TestUtility.ts @@ -27,10 +27,9 @@ export function getMockAcquisitionContext(mode: DotnetInstallMode, version : str storagePath: '', extensionState: extensionContext, eventStream: myEventStream, - acquisitionContext: getMockAcquireContext(version, arch), + acquisitionContext: getMockAcquireContext(version, arch, mode), installationValidator: new MockInstallationValidator(myEventStream), timeoutSeconds: timeoutTime, - installMode: mode, proxyUrl: undefined, installDirectoryProvider: directory ? directory : directoryProviderFactory(mode, ''), isExtensionTelemetryInitiallyEnabled: true @@ -53,13 +52,14 @@ export function getMockUtilityContext() : IUtilityContext return utilityContext; } -export function getMockAcquireContext(nextAcquiringVersion : string, arch : string | null | undefined) : IDotnetAcquireContext +export function getMockAcquireContext(nextAcquiringVersion : string, arch : string | null | undefined, installMode : DotnetInstallMode) : IDotnetAcquireContext { const acquireContext : IDotnetAcquireContext = { version: nextAcquiringVersion, architecture: arch, - requestingExtensionId: 'test' + requestingExtensionId: 'test', + mode: installMode }; return acquireContext; } \ No newline at end of file diff --git a/vscode-dotnet-runtime-library/src/test/unit/VersionResolver.test.ts b/vscode-dotnet-runtime-library/src/test/unit/VersionResolver.test.ts index 6a038564c9..45134e5cc6 100644 --- a/vscode-dotnet-runtime-library/src/test/unit/VersionResolver.test.ts +++ b/vscode-dotnet-runtime-library/src/test/unit/VersionResolver.test.ts @@ -36,25 +36,25 @@ suite('VersionResolver Unit Tests', () => { }); test('Error With Invalid Version', async () => { - assert.isRejected(resolver.getFullRuntimeVersion('foo'), Error, 'Invalid version'); + assert.isRejected(resolver.getFullVersion('foo', 'runtime'), Error, 'Invalid version'); }); test('Error With Three Part Version', async () => { - assert.isRejected(resolver.getFullRuntimeVersion('1.0.16'), Error, 'Invalid version'); + assert.isRejected(resolver.getFullVersion('1.0.16', 'runtime'), Error, 'Invalid version'); }); test('Error With Invalid Major.Minor', async () => { - assert.isRejected(resolver.getFullRuntimeVersion('0.0'), Error, 'Unable to resolve version'); + assert.isRejected(resolver.getFullVersion('0.0', 'runtime'), Error, 'Unable to resolve version'); }); test('Resolve Valid Runtime Versions', async () => { for (const version of versionPairs) { - assert.equal(await resolver.getFullRuntimeVersion(version[0]), version[1]); + assert.equal(await resolver.getFullVersion(version[0], 'runtime'), version[1]); } }); test('Resolve Latest SDK Version', async () => { - assert.equal(await resolver.getFullSDKVersion('2.2'), '2.2.207'); + assert.equal(await resolver.getFullVersion('2.2', 'sdk'), '2.2.207'); }); test('Get Major from SDK Version', async () => { 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 ed7eaf32e4..b97d09e4e4 100644 --- a/vscode-dotnet-runtime-library/src/test/unit/WebRequestWorker.test.ts +++ b/vscode-dotnet-runtime-library/src/test/unit/WebRequestWorker.test.ts @@ -38,7 +38,7 @@ suite('WebRequestWorker Unit Tests', () => { const mockContext = getMockAcquisitionContext('runtime', '1.0', undefined, eventStream); const acquisitionWorker = new DotnetCoreAcquisitionWorker(getMockUtilityContext(), new MockVSCodeExtensionContext()); const invoker = new ErrorAcquisitionInvoker(eventStream); - return assert.isRejected(acquisitionWorker.acquireRuntime(mockContext, invoker), Error, '.NET Acquisition Failed'); + return assert.isRejected(acquisitionWorker.acquireLocalRuntime(mockContext, invoker), Error, '.NET Acquisition Failed'); }).timeout(maxTimeoutTime); test('Install Script Request Failure', async () => { diff --git a/vscode-dotnet-runtime.code-workspace b/vscode-dotnet-runtime.code-workspace index 56542c586a..2942fb4c8a 100644 --- a/vscode-dotnet-runtime.code-workspace +++ b/vscode-dotnet-runtime.code-workspace @@ -15,7 +15,8 @@ ], "settings": { "cSpell.words": [ - "DOTNETINSTALLMODELIST" + "DOTNETINSTALLMODELIST", + "Republisher" ] } } \ No newline at end of file diff --git a/vscode-dotnet-sdk-extension/package-lock.json b/vscode-dotnet-sdk-extension/package-lock.json index 70cb058cb0..74f66f5c93 100644 --- a/vscode-dotnet-sdk-extension/package-lock.json +++ b/vscode-dotnet-sdk-extension/package-lock.json @@ -34,7 +34,7 @@ "vscode-dotnet-runtime-library": "file:../vscode-dotnet-runtime-library" }, "devDependencies": { - "@types/source-map-support": "^0.5.6", + "@types/source-map-support": "^0.5.10", "copy-webpack-plugin": "^9.0.1", "webpack": "5.76.0", "webpack-cli": "4.9.1" @@ -371,9 +371,9 @@ } }, "node_modules/@types/source-map-support": { - "version": "0.5.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.6.tgz", - "integrity": "sha1-qkqMmOxzofHzCoE1c6myFUpus5o=", + "version": "0.5.10", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha1-gk3O+YlJa66Y6dBMjcGsHXDhvTk=", "dev": true, "license": "MIT", "dependencies": { @@ -4208,9 +4208,9 @@ } }, "@types/source-map-support": { - "version": "0.5.6", - "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.6.tgz", - "integrity": "sha1-qkqMmOxzofHzCoE1c6myFUpus5o=", + "version": "0.5.10", + "resolved": "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.10.tgz", + "integrity": "sha1-gk3O+YlJa66Y6dBMjcGsHXDhvTk=", "dev": true, "requires": { "source-map": "^0.6.0" diff --git a/vscode-dotnet-sdk-extension/package.json b/vscode-dotnet-sdk-extension/package.json index 0cdb882172..2e48e80915 100644 --- a/vscode-dotnet-sdk-extension/package.json +++ b/vscode-dotnet-sdk-extension/package.json @@ -111,7 +111,7 @@ "vscode-dotnet-runtime-library": "file:../vscode-dotnet-runtime-library" }, "devDependencies": { - "@types/source-map-support": "^0.5.6", + "@types/source-map-support": "^0.5.10", "copy-webpack-plugin": "^9.0.1", "webpack": "5.76.0", "webpack-cli": "4.9.1" diff --git a/vscode-dotnet-sdk-extension/src/extension.ts b/vscode-dotnet-sdk-extension/src/extension.ts index 2cdeb07bb8..d2109d38f0 100644 --- a/vscode-dotnet-sdk-extension/src/extension.ts +++ b/vscode-dotnet-sdk-extension/src/extension.ts @@ -16,16 +16,13 @@ import { DotnetAcquisitionStatusRequested, DotnetCoreAcquisitionWorker, DotnetSDKAcquisitionStarted, - DotnetVersionResolutionError, enableExtensionTelemetry, ErrorConfiguration, ExtensionConfigurationWorker, formatIssueUrl, IDotnetAcquireContext, - IDotnetListVersionsContext, IDotnetUninstallContext, EventBasedError, - IDotnetVersion, IEventStreamContext, IExtensionContext, IIssueContext, @@ -35,8 +32,6 @@ import { VersionResolver, VSCodeExtensionContext, VSCodeEnvironment, - WebRequestWorker, - IWindowDisplayWorker, WindowDisplayWorker, Debugging, CommandExecutor, @@ -94,7 +89,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE showLogCommand: `${commandPrefix}.${commandKeys.showAcquisitionLog}`, packageJson, } as IEventStreamContext; - const [eventStream, outputChannel, loggingObserver, eventStreamObservers, telemetryObserver] = registerEventStream(eventStreamContext, vsCodeExtensionContext, utilContext); + 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) => { @@ -148,7 +143,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE const pathResult = callWithErrorHandling(async () => { eventStream.post(new DotnetSDKAcquisitionStarted(commandContext.requestingExtensionId)); - eventStream.post(new DotnetAcquisitionRequested(commandContext.version, commandContext.requestingExtensionId)); + eventStream.post(new DotnetAcquisitionRequested(commandContext.version, commandContext.requestingExtensionId ?? 'notProvided', 'sdk', 'local')); telemetryObserver?.setAcquisitionContext(acquisitionContext, commandContext); if(commandContext.installType === 'global') @@ -172,10 +167,10 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE { Debugging.log(`Acquisition Request was remarked as local.`, eventStream); - const resolvedVersion = await versionResolver.getFullSDKVersion(commandContext.version); + const resolvedVersion = await versionResolver.getFullVersion(commandContext.version, 'sdk'); acquisitionContext.acquisitionContext.version = resolvedVersion; const acquisitionInvoker = new LocalAcquisitionInvoker(acquisitionContext, utilContext); - const dotnetPath = await acquisitionWorker.acquireSDK(acquisitionContext, acquisitionInvoker); + const dotnetPath = await acquisitionWorker.acquireLocalSDK(acquisitionContext, acquisitionInvoker); const pathEnvVar = path.dirname(dotnetPath.dotnetPath); new CommandExecutor(acquisitionContext, utilContext).setPathEnvVar(pathEnvVar, troubleshootingUrl, displayWorker, vsCodeExtensionContext, false); @@ -194,7 +189,7 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE const fakeContext = getContext(null); const versionResolver = new VersionResolver(fakeContext); - commandContext.version = await versionResolver.getFullSDKVersion(commandContext.version); + commandContext.version = await versionResolver.getFullVersion(commandContext.version, 'sdk'); const dotnetPath = await acquisitionWorker.acquireStatus(fakeContext, 'sdk'); return dotnetPath; }, issueContext(commandContext.errorConfiguration, 'acquireSDKStatus')); @@ -222,13 +217,13 @@ export function activate(context: vscode.ExtensionContext, extensionContext?: IE extensionState: context.globalState, eventStream, installationValidator: new InstallationValidator(eventStream), - installMode: 'sdk', timeoutSeconds: resolvedTimeoutSeconds, installDirectoryProvider: new SdkInstallationDirectoryProvider(storagePath), acquisitionContext : commandContext ?? { // See runtime extension for more details on this fake context. version: 'unspecified', architecture: os.arch(), requestingExtensionId: 'notAnAcquisitionCall', + mode: 'sdk' }, isExtensionTelemetryInitiallyEnabled : isExtensionTelemetryEnabled, }; diff --git a/vscode-dotnet-sdk-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts b/vscode-dotnet-sdk-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts index 70351f8fd7..ac2d110d86 100644 --- a/vscode-dotnet-sdk-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts +++ b/vscode-dotnet-sdk-extension/src/test/functional/DotnetCoreAcquisitionExtension.test.ts @@ -28,8 +28,7 @@ import { NoInstallAcquisitionInvoker, SdkInstallationDirectoryProvider, MockIndexWebRequestWorker, - MockVSCodeExtensionContext, - getMockUtilityContext, + getInstallKeyCustomArchitecture, IExistingPaths, getMockAcquisitionContext, getMockAcquisitionWorker, @@ -99,10 +98,10 @@ suite('DotnetCoreAcquisitionExtension End to End', function() const dotnetDir = installDirectoryProvider.getInstallDir(version); const dotnetExePath = path.join(dotnetDir, `dotnet${os.platform() === 'win32' ? '.exe' : ''}`); - const sdkCurrentInstallKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(version, os.arch()); + const sdkCurrentInstallKey = getInstallKeyCustomArchitecture(version, os.arch(), 'sdk', 'local'); const sdkDirCurrent = path.join(dotnetDir, 'sdk', sdkCurrentInstallKey); - const sdkEarlierInstallKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(earlierVersion, os.arch()); + const sdkEarlierInstallKey = getInstallKeyCustomArchitecture(earlierVersion, os.arch(), 'sdk', 'local'); const sdkDirEarlier = path.join(dotnetDir, 'sdk', sdkEarlierInstallKey); fs.mkdirSync(sdkDirCurrent, { recursive: true }); fs.mkdirSync(sdkDirEarlier, { recursive: true }); @@ -110,7 +109,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function() // Assert preinstalled SDKs are detected const acquisitionInvoker = new NoInstallAcquisitionInvoker(eventStream, acquisitionWorker); - const result = await acquisitionWorker.acquireSDK(mockContext, acquisitionInvoker); + const result = await acquisitionWorker.acquireLocalSDK(mockContext, acquisitionInvoker); assert.equal(path.dirname(result.dotnetPath), dotnetDir, 'preinstalled sdk path is the same as installed sdk path on api call'); const preinstallEvents = eventStream.events .filter(event => event instanceof DotnetPreinstallDetected) @@ -135,7 +134,7 @@ suite('DotnetCoreAcquisitionExtension End to End', function() const mockContext = getMockAcquisitionContext('sdk', version, 10000, undefined, undefined, undefined, installDirectoryProvider); const acquisitionWorker = getMockAcquisitionWorker(mockContext); - const currentVersionInstallKey = DotnetCoreAcquisitionWorker.getInstallKeyCustomArchitecture(version, os.arch()); + const currentVersionInstallKey = getInstallKeyCustomArchitecture(version, os.arch(), 'sdk', 'local'); // Ensure nothing is returned when there is no preinstalled SDK const noPreinstallResult = await acquisitionWorker.acquireStatus(mockContext, 'sdk'); assert.isUndefined(noPreinstallResult); diff --git a/vscode-dotnet-sdk-extension/yarn.lock b/vscode-dotnet-sdk-extension/yarn.lock index 78b78561e3..bd4b29ded3 100644 --- a/vscode-dotnet-sdk-extension/yarn.lock +++ b/vscode-dotnet-sdk-extension/yarn.lock @@ -212,10 +212,10 @@ "@types/glob" "*" "@types/node" "*" -"@types/source-map-support@^0.5.6": - "integrity" "sha1-qkqMmOxzofHzCoE1c6myFUpus5o=" - "resolved" "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.6.tgz" - "version" "0.5.6" +"@types/source-map-support@^0.5.10": + "integrity" "sha1-gk3O+YlJa66Y6dBMjcGsHXDhvTk=" + "resolved" "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public-npm/npm/registry/@types/source-map-support/-/source-map-support-0.5.10.tgz" + "version" "0.5.10" dependencies: "source-map" "^0.6.0" @@ -2193,6 +2193,7 @@ "proper-lockfile" "^4.1.2" "rimraf" "3.0.2" "run-script-os" "^1.1.6" + "semver" "^7.6.2" "shelljs" "0.8.5" "typescript" "4.4.4" "vscode-extension-telemetry" "^0.4.3"