diff --git a/vscode-dotnet-runtime-extension/CHANGELOG.md b/vscode-dotnet-runtime-extension/CHANGELOG.md index 0f5102cd46..a82389095d 100644 --- a/vscode-dotnet-runtime-extension/CHANGELOG.md +++ b/vscode-dotnet-runtime-extension/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning]. ## [2.2.9] - 2025-2-26 -Add community support for Debian. +Add community support for Debian SDK installation -- thank you @curllog! Fix issue with install scripts on validating the dotnet install. Performance improvements. diff --git a/vscode-dotnet-runtime-library/distro-data/distro-support.json b/vscode-dotnet-runtime-library/distro-data/distro-support.json index 1eac973468..b0fc2a1729 100644 --- a/vscode-dotnet-runtime-library/distro-data/distro-support.json +++ b/vscode-dotnet-runtime-library/distro-data/distro-support.json @@ -1,5 +1,401 @@ { "$schema": "../../Documentation/json-schema/distro-support-schema.json", + "Debian GNU/Linux": { + "installCommand": [ + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "update" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "install", + "-y", + "{packageName}" + ] + } + ], + "uninstallCommand": [ + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "remove", + "-y", + "{packageName}" + ] + } + ], + "updateCommand": [ + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "update", + "-y" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "upgrade", + "-y", + "{packageName}" + ] + } + ], + "searchCommand": [ + { + "runUnderSudo": false, + "commandRoot": "apt-cache", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "search", + "--names-only", + "^{packageName}$" + ] + } + ], + "isInstalledCommand": [ + { + "runUnderSudo": true, + "commandRoot": "apt", + "commandParts": [ + "list", + "--installed", + "{packageName}" + ] + } + ], + "packageLookupCommand": [ + { + "runUnderSudo": false, + "commandRoot": "dpkg", + "commandParts": [ + "-l", + "{packageName}" + ] + } + ], + "readSymLinkCommand": [ + { + "runUnderSudo": false, + "commandRoot": "readlink", + "commandParts": [ + "-f", + "{path}" + ] + } + ], + "expectedDistroFeedInstallDirectory": "/usr/lib/dotnet", + "expectedMicrosoftFeedInstallDirectory": "/usr/share/dotnet", + "installedSDKVersionsCommand": [ + { + "runUnderSudo": false, + "commandRoot": "dotnet", + "commandParts": [ + "--list-sdks" + ] + } + ], + "installedRuntimeVersionsCommand": [ + { + "runUnderSudo": false, + "commandRoot": "dotnet", + "commandParts": [ + "--list-runtimes" + ] + } + ], + "currentInstallationVersionCommand": [ + { + "runUnderSudo": false, + "commandRoot": "dotnet", + "commandParts": [ + "--version" + ] + } + ], + "currentInstallPathCommand": [ + { + "runUnderSudo": false, + "commandRoot": "which", + "commandParts": [ + "dotnet" + ] + } + ], + "packages": [ + { + "version": "6.0", + "sdk": [ + "dotnet-sdk-6.0" + ], + "runtime": [ + "dotnet-runtime-6.0" + ], + "aspnetcore": [ + "aspnetcore-runtime-6.0" + ] + }, + { + "version": "7.0", + "sdk": [ + "dotnet-sdk-7.0" + ], + "runtime": [ + "dotnet-runtime-7.0" + ], + "aspnetcore": [ + "aspnetcore-runtime-7.0" + ] + }, + { + "version": "8.0", + "sdk": [ + "dotnet-sdk-8.0" + ], + "runtime": [ + "dotnet-runtime-8.0" + ], + "aspnetcore": [ + "aspnetcore-runtime-8.0" + ] + }, + { + "version": "9.0", + "sdk": [ + "dotnet-sdk-9.0" + ], + "runtime": [ + "dotnet-runtime-9.0" + ], + "aspnetcore": [ + "aspnetcore-runtime-9.0" + ] + } + ], + "versions": [ + { + "version": "8", + "preInstallCommands": [ + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "install", + "-y", + "wget" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "wget", + "commandParts": [ + "https://packages.microsoft.com/config/debian/8/packages-microsoft-prod.deb", + "-O", + "packages-microsoft-prod.deb" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "dpkg", + "commandParts": [ + "-i", + "packages-microsoft-prod.deb" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "update" + ] + } + ] + }, + { + "version": "9", + "preInstallCommands": [ + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "update" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "install", + "-y", + "wget" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "wget", + "commandParts": [ + "https://packages.microsoft.com/config/debian/9/packages-microsoft-prod.deb", + "-O", + "packages-microsoft-prod.deb" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "dpkg", + "commandParts": [ + "-i", + "packages-microsoft-prod.deb" + ] + } + ] + }, + { + "version": "10", + "preInstallCommands": [ + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "update" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "install", + "-y", + "wget" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "wget", + "commandParts": [ + "https://packages.microsoft.com/config/debian/10/packages-microsoft-prod.deb", + "-O", + "packages-microsoft-prod.deb" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "dpkg", + "commandParts": [ + "-i", + "packages-microsoft-prod.deb" + ] + } + ] + }, + { + "version": "11", + "preInstallCommands": [ + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "update" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "install", + "-y", + "wget" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "wget", + "commandParts": [ + "https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb", + "-O", + "packages-microsoft-prod.deb" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "dpkg", + "commandParts": [ + "-i", + "packages-microsoft-prod.deb" + ] + } + ] + }, + { + "version": "12", + "preInstallCommands": [ + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "-o", + "DPkg::Lock::Timeout=180", + "update" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "apt-get", + "commandParts": [ + "install", + "-y", + "wget" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "wget", + "commandParts": [ + "https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb", + "-O", + "packages-microsoft-prod.deb" + ] + }, + { + "runUnderSudo": true, + "commandRoot": "dpkg", + "commandParts": [ + "-i", + "packages-microsoft-prod.deb" + ] + } + ] + } + ] + }, "Ubuntu": { "installCommand": [ { diff --git a/vscode-dotnet-runtime-library/src/Acquisition/DebianDistroSDKProvider.ts b/vscode-dotnet-runtime-library/src/Acquisition/DebianDistroSDKProvider.ts new file mode 100644 index 0000000000..5b2313aa91 --- /dev/null +++ b/vscode-dotnet-runtime-library/src/Acquisition/DebianDistroSDKProvider.ts @@ -0,0 +1,34 @@ +/* -------------------------------------------------------------------------------------------- + * Licensed to the .NET Foundation under one or more agreements. +* The .NET Foundation licenses this file to you under the MIT license. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +import { ICommandExecutor } from '../Utils/ICommandExecutor'; +import { IUtilityContext } from '../Utils/IUtilityContext'; +import { DotnetInstallMode } from './DotnetInstallMode'; +import { GenericDistroSDKProvider } from './GenericDistroSDKProvider'; +import { IAcquisitionWorkerContext } from './IAcquisitionWorkerContext'; +import { DistroVersionPair } from './LinuxVersionResolver'; + +export class DebianDistroSDKProvider extends GenericDistroSDKProvider +{ + constructor(distroVersion: DistroVersionPair, context: IAcquisitionWorkerContext, utilContext: IUtilityContext, executor: ICommandExecutor | null = null) + { + super(distroVersion, context, utilContext, executor); + } + + protected myVersionDetails(): any + { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const distroVersions = this.distroJson[this.distroVersion.distro][this.distroVersionsKey]; + const targetVersion = Math.floor(parseFloat(this.distroVersion.version[0])).toFixed(0); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const versionData = distroVersions.filter((x: { [x: string]: string; }) => x[this.versionKey] === String(targetVersion)); + return versionData; + } + public override async dotnetPackageExistsOnSystem(fullySpecifiedDotnetVersion: string, installType: DotnetInstallMode): Promise + { + await this.injectPMCFeed(fullySpecifiedDotnetVersion, installType); + return super.dotnetPackageExistsOnSystem(fullySpecifiedDotnetVersion, installType); + } +} \ No newline at end of file diff --git a/vscode-dotnet-runtime-library/src/Acquisition/GenericDistroSDKProvider.ts b/vscode-dotnet-runtime-library/src/Acquisition/GenericDistroSDKProvider.ts index 50648a2b54..ba21820d16 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/GenericDistroSDKProvider.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/GenericDistroSDKProvider.ts @@ -5,7 +5,7 @@ * ------------------------------------------------------------------------------------------ */ import * as path from 'path'; -import { DotnetVersionResolutionError, EventBasedError } from '../EventStream/EventStreamEvents'; +import { DistroPackagesSearch, DistroSupport, DotnetVersionResolutionError, EventBasedError } from '../EventStream/EventStreamEvents'; import { CommandExecutor } from '../Utils/CommandExecutor'; import { READ_SYMLINK_CACHE_DURATION_MS } from './CacheTimeConstants'; import { DotnetInstallMode } from './DotnetInstallMode'; @@ -35,7 +35,7 @@ export class GenericDistroSDKProvider extends IDistroDotnetSDKProvider { const commandResult = await this.commandRunner.executeMultipleCommands(this.myDistroCommands(this.currentInstallPathCommandKey), null, false); - if (commandResult[0].status !== '0') // no dotnet error can be returned, dont want to try to parse this as a path + if (commandResult[0].status !== '0') // no dotnet error can be returned, do not want to try to parse this as a path { return null; } @@ -162,6 +162,7 @@ export class GenericDistroSDKProvider extends IDistroDotnetSDKProvider if (versionUtils.getFeatureBandFromVersion(fullySpecifiedVersion, this.context.eventStream, this.context) !== '1' || Number(versionUtils.getMajor(fullySpecifiedVersion, this.context.eventStream, this.context)) < 6) { + this.context.eventStream.post(new DistroSupport(`Distro: Dotnet Version ${fullySpecifiedVersion} is not supported by this extension. It has a non 1 level band or < 6.0.`)); return Promise.resolve(DotnetDistroSupportStatus.Unsupported); } @@ -169,10 +170,12 @@ export class GenericDistroSDKProvider extends IDistroDotnetSDKProvider if (this.myVersionDetails().hasOwnProperty(this.preinstallCommandKey)) { // If preinstall commands exist ( to add the msft feed ) then it's a microsoft feed. + this.context.eventStream.post(new DistroSupport(`Distro: Dotnet Version ${fullySpecifiedVersion} is Microsoft support, because it has preinstallCmdKey.`)); return Promise.resolve(DotnetDistroSupportStatus.Microsoft); } else { + this.context.eventStream.post(new DistroSupport(`Couldn't find preinstallCmdKey for ${this.distroVersion.distro} ${this.distroVersion.version} with dotnet Version ${fullySpecifiedVersion}.`)); const availableVersions = await this.myVersionPackages(installType, this.isMidFeedInjection); const simplifiedVersion = this.JsonDotnetVersion(fullySpecifiedVersion); @@ -180,11 +183,13 @@ export class GenericDistroSDKProvider extends IDistroDotnetSDKProvider { if (Number(dotnetPackages.version) === Number(simplifiedVersion)) { + this.context.eventStream.post(new DistroSupport(`Version ${fullySpecifiedVersion} is Distro supported, because it has packages already.`)); return Promise.resolve(DotnetDistroSupportStatus.Distro); } } } + this.context.eventStream.post(new DistroSupport(`Version ${fullySpecifiedVersion} is unknown.`)); return Promise.resolve(DotnetDistroSupportStatus.Unknown); } @@ -196,8 +201,10 @@ export class GenericDistroSDKProvider extends IDistroDotnetSDKProvider { if (Number(dotnetPackages.version) > Number(maxVersion)) { + this.context.eventStream.post(new DistroPackagesSearch(`Found version ${dotnetPackages.version} for .NET and and picking it, as it is higher than ${maxVersion}.`)); maxVersion = dotnetPackages.version; } + this.context.eventStream.post(new DistroPackagesSearch(`Skipping version ${dotnetPackages.version} for .NET and and picking it, as it is lower than ${maxVersion}.`)); } if (maxVersion === '0') diff --git a/vscode-dotnet-runtime-library/src/Acquisition/IDistroDotnetSDKProvider.ts b/vscode-dotnet-runtime-library/src/Acquisition/IDistroDotnetSDKProvider.ts index f552decddf..1b64b82ebd 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/IDistroDotnetSDKProvider.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/IDistroDotnetSDKProvider.ts @@ -7,7 +7,7 @@ import * as fs from 'fs'; import path = require('path'); -import { DotnetAcquisitionDistroUnknownError, DotnetVersionResolutionError, EventBasedError, EventCancellationError, SuppressedAcquisitionError } from '../EventStream/EventStreamEvents'; +import { DistroPackagesSearch, DotnetAcquisitionDistroUnknownError, DotnetVersionResolutionError, EventBasedError, EventCancellationError, FeedInjection, FoundDistroVersionDetails, SuppressedAcquisitionError } from '../EventStream/EventStreamEvents'; import { CommandExecutor } from '../Utils/CommandExecutor'; import { CommandExecutorCommand } from '../Utils/CommandExecutorCommand'; import { ICommandExecutor } from '../Utils/ICommandExecutor'; @@ -62,7 +62,7 @@ export abstract class IDistroDotnetSDKProvider protected aspNetKey = 'aspnetcore'; protected isMidFeedInjection = false; - protected cachedMyVersionPacakges: any = null; + protected cachedMyVersionPackages: any = null; constructor(distroVersion: DistroVersionPair, context: IAcquisitionWorkerContext, utilContext: IUtilityContext, executor: ICommandExecutor | null = null) { @@ -191,9 +191,12 @@ If you would like to contribute to the list of supported distros, please visit: protected async myVersionPackages(installType: DotnetInstallMode, haveTriedFeedInjectionAlready = false): Promise { - if (this.cachedMyVersionPacakges) + this.context.eventStream.post(new DistroPackagesSearch(`Searching for .NET packages for distro ${this.distroVersion.distro} version ${this.distroVersion.version}: has tried injection? ${haveTriedFeedInjectionAlready}`)); + + if (this.cachedMyVersionPackages) { - return this.cachedMyVersionPacakges; + this.context.eventStream.post(new FoundDistroVersionDetails(`Found cached distro version details: ${JSON.stringify(this.cachedMyVersionPackages)}`)); + return this.cachedMyVersionPackages; } const availableVersions: LinuxPackageCollection[] = []; @@ -201,6 +204,7 @@ If you would like to contribute to the list of supported distros, please visit: const potentialDotnetPackageNames = this.distroJson[this.distroVersion.distro][this.distroPackagesKey]; for (const packageSet of potentialDotnetPackageNames) { + this.context.eventStream.post(new DistroPackagesSearch(`Searching for .NET packages for distro ${this.distroVersion.distro} version ${this.distroVersion.version} in package set ${JSON.stringify(packageSet)}`)); const thisVersionPackage: LinuxPackageCollection = { version: packageSet[this.versionKey], @@ -238,21 +242,26 @@ If you would like to contribute to the list of supported distros, please visit: // Our check runs by checking the feature band first, so that needs to be supported for it to fallback to the preinstall command check. const fakeVersionToCheckMicrosoftSupportStatus = '6.0.1xx'; + this.context.eventStream.post(new FeedInjection(`Starting feed injection after package version searching as no packages could be found.`)); await this.injectPMCFeed(fakeVersionToCheckMicrosoftSupportStatus, installType); - this.cachedMyVersionPacakges = this.myVersionPackages(installType, true); + const packagesAfterFeedInjection = this.myVersionPackages(installType, true); + this.context.eventStream.post(new FoundDistroVersionDetails(`Caching distro version details after injection: ${JSON.stringify(packagesAfterFeedInjection)}`)); + this.cachedMyVersionPackages = packagesAfterFeedInjection; } else { - this.cachedMyVersionPacakges = availableVersions; + this.context.eventStream.post(new FoundDistroVersionDetails(`Caching distro version details: ${JSON.stringify(this.cachedMyVersionPackages)}`)); + this.cachedMyVersionPackages = availableVersions; } - return this.cachedMyVersionPacakges; + return this.cachedMyVersionPackages; } protected async injectPMCFeed(fullySpecifiedVersion: string, installType: DotnetInstallMode) { if (this.isMidFeedInjection) { + this.context.eventStream.post(new FeedInjection(`Skipping injection : already started.`)); return; } @@ -260,9 +269,15 @@ If you would like to contribute to the list of supported distros, please visit: const supportStatus = await this.getDotnetVersionSupportStatus(fullySpecifiedVersion, installType); if (supportStatus === DotnetDistroSupportStatus.Microsoft) { + this.context.eventStream.post(new FeedInjection(`Starting feed injection.`)); const myVersionDetails = this.myVersionDetails(); const preInstallCommands = myVersionDetails[this.preinstallCommandKey] as CommandExecutorCommand[]; await this.commandRunner.executeMultipleCommands(preInstallCommands, {}, false); + this.context.eventStream.post(new FeedInjection(`Finished injection.`)); + } + else + { + this.context.eventStream.post(new FeedInjection(`Skipping injection : not Microsoft supported.`)); } this.isMidFeedInjection = false; @@ -273,11 +288,14 @@ If you would like to contribute to the list of supported distros, please visit: const distroVersions = this.distroJson[this.distroVersion.distro][this.distroVersionsKey]; const versionData = distroVersions.filter((x: { [x: string]: string; }) => x[this.versionKey] === this.distroVersion.version)[0]; + if (!versionData) { const closestVersion = this.findMostSimilarVersion(this.distroVersion.version, distroVersions.map((x: { [x: string]: string; }) => parseFloat(x[this.versionKey]))); return distroVersions.filter((x: { [x: string]: string; }) => parseFloat(x[this.versionKey]) === closestVersion)[0]; } + + this.context.eventStream.post(new FoundDistroVersionDetails(`Found distro version details: ${JSON.stringify(versionData)}`)); return versionData; } @@ -286,6 +304,7 @@ If you would like to contribute to the list of supported distros, please visit: const sameMajorVersions = knownVersions.filter(x => Math.floor(x) === Math.floor(parseFloat(myVersion))); if (sameMajorVersions && sameMajorVersions.length) { + this.context.eventStream.post(new FoundDistroVersionDetails(`Found similar version details for same major: ${JSON.stringify(sameMajorVersions)}`)); return Math.max(...sameMajorVersions); } @@ -371,6 +390,7 @@ If you would like to contribute to the list of supported distros, please visit: if (dotnetPackage.version === this.JsonDotnetVersion(fullySpecifiedDotnetVersion)) { // Arbitrarily pick the first existing package. + this.context.eventStream.post(new DistroPackagesSearch(`Found .NET package for version ${fullySpecifiedDotnetVersion} and taking the first: ${JSON.stringify(dotnetPackage.packages)}`)); return dotnetPackage.packages[0]; } } diff --git a/vscode-dotnet-runtime-library/src/Acquisition/LinuxVersionResolver.ts b/vscode-dotnet-runtime-library/src/Acquisition/LinuxVersionResolver.ts index 5a2aad5acd..dfe6fea637 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/LinuxVersionResolver.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/LinuxVersionResolver.ts @@ -23,14 +23,19 @@ import * as versionUtils from './VersionUtilities'; import { IDotnetAcquireContext } from '../IDotnetAcquireContext'; import { FileUtilities } from '../Utils/FileUtilities'; + import { ICommandExecutor } from '../Utils/ICommandExecutor'; import { getInstallFromContext } from '../Utils/InstallIdUtilities'; import { IUtilityContext } from '../Utils/IUtilityContext'; -import { SYSTEM_INFORMATION_CACHE_DURATION_MS } from './CacheTimeConstants'; import { DotnetInstallMode } from './DotnetInstallMode'; import { IAcquisitionWorkerContext } from './IAcquisitionWorkerContext'; import { IDistroDotnetSDKProvider } from './IDistroDotnetSDKProvider'; +import { DebianDistroSDKProvider } from './DebianDistroSDKProvider'; + +import { SYSTEM_INFORMATION_CACHE_DURATION_MS } from './CacheTimeConstants'; + + /** * An enumeration type representing all distros with their versions that we recognize. * @remarks @@ -95,6 +100,9 @@ Follow the instructions here to download the .NET SDK: https://learn.microsoft.c Or, install Red Hat Enterprise Linux 8.0 or Red Hat Enterprise Linux 9.0 from https://access.redhat.com/downloads/;` protected acquireCtx: IDotnetAcquireContext | null | undefined; + // This includes all distros that we officially support for this tool as a company. If a distro is not in this list, it can still have community member support. + public microsoftSupportedDistroIds = ['Red Hat Enterprise Linux', 'Ubuntu']; + constructor(private readonly workerContext: IAcquisitionWorkerContext, private readonly utilityContext: IUtilityContext, executor: ICommandExecutor | null = null, distroProvider: IDistroDotnetSDKProvider | null = null) { @@ -184,6 +192,17 @@ Or, install Red Hat Enterprise Linux 8.0 or Red Hat Enterprise Linux 9.0 from ht this.workerContext.eventStream.post(error); throw error.error; } + else + { + if (!this.microsoftSupportedDistroIds.includes(this.distro.distro)) + { + // UX: Could eventually add a 'Go away' button via the callback: + this.utilityContext.ui.showInformationMessage(`Automated SDK installation for the distro ${this.distro.distro} is not officially supported, except for community implemented and Microsoft approved support. +If you experience issues, please reach out on https://github.com/dotnet/vscode-dotnet-runtime/issues.`, + () => {/* No Callback */ }, + ); + } + } } private isRedHatVersion7(rhelVersion: string) @@ -217,6 +236,8 @@ Or, install Red Hat Enterprise Linux 8.0 or Red Hat Enterprise Linux 9.0 from ht throw unsupportedRhelErr.error; } return new RedHatDistroSDKProvider(distroAndVersion, this.workerContext, this.utilityContext); + case 'Debian GNU/Linux': + return new DebianDistroSDKProvider(distroAndVersion, this.workerContext, this.utilityContext); default: return new GenericDistroSDKProvider(distroAndVersion, this.workerContext, this.utilityContext); } diff --git a/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts b/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts index 09cfbcdda8..4e9ba39197 100644 --- a/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts +++ b/vscode-dotnet-runtime-library/src/EventStream/EventStreamEvents.ts @@ -1213,6 +1213,27 @@ export class DotnetTelemetrySettingEvent extends DotnetCustomMessageEvent public readonly eventName = 'DotnetTelemetrySettingEvent'; } + +export class DistroSupport extends DotnetCustomMessageEvent +{ + public readonly eventName = 'DistroSupport'; +} + +export class FeedInjection extends DotnetCustomMessageEvent +{ + public readonly eventName = 'FeedInjection'; +} + +export class DistroPackagesSearch extends DotnetCustomMessageEvent +{ + public readonly eventName = 'DistroPackagesSearch'; +} + +export class FoundDistroVersionDetails extends DotnetCustomMessageEvent +{ + public readonly eventName = 'FoundDistroVersionDetails'; +} + export class DotnetVSCodeExtensionFound extends DotnetCustomMessageEvent { public readonly eventName = 'DotnetVSCodeExtensionFound'; diff --git a/vscode-dotnet-runtime-library/src/Utils/WebRequestWorker.ts b/vscode-dotnet-runtime-library/src/Utils/WebRequestWorker.ts index 891e1765b8..0e80369416 100644 --- a/vscode-dotnet-runtime-library/src/Utils/WebRequestWorker.ts +++ b/vscode-dotnet-runtime-library/src/Utils/WebRequestWorker.ts @@ -270,7 +270,7 @@ export class WebRequestWorker */ public async downloadFile(url: string, dest: string) { - if (await new FileUtilities().exists(dest)) + if (fs.existsSync(dest)) { return; } @@ -344,10 +344,10 @@ export class WebRequestWorker this.context.eventStream.post(new WebRequestSent(this.url)); const response = await this.axiosGet( this.url, - { transformResponse: (x: any) => x as any, ...options } + { transformResponse: (x: any) => x, ...options } ); - if (response?.headers?.['content-type'] === 'application/json') + if (response !== null && response?.headers['content-type'] === 'application/json') { try { diff --git a/vscode-dotnet-runtime-library/src/test/unit/DebianDistroTests.test.ts b/vscode-dotnet-runtime-library/src/test/unit/DebianDistroTests.test.ts new file mode 100644 index 0000000000..005744a807 --- /dev/null +++ b/vscode-dotnet-runtime-library/src/test/unit/DebianDistroTests.test.ts @@ -0,0 +1,185 @@ +/* -------------------------------------------------------------------------------------------- + * Licensed to the .NET Foundation under one or more agreements. +* The .NET Foundation licenses this file to you under the MIT license. + * Licensed under the MIT License. See License.txt in the project root for license information. + * ------------------------------------------------------------------------------------------ */ +import * as chai from 'chai'; +import * as os from 'os'; +import { MockCommandExecutor, MockEventStream } from '../mocks/MockObjects'; +import { DistroVersionPair, DotnetDistroSupportStatus, LinuxVersionResolver } from '../../Acquisition/LinuxVersionResolver'; +import { getMockAcquisitionContext, getMockUtilityContext } from './TestUtility'; +import { DotnetInstallMode } from '../../Acquisition/DotnetInstallMode'; +import { DebianDistroSDKProvider } from '../../Acquisition/DebianDistroSDKProvider'; +const assert = chai.assert; +const standardTimeoutTime = 100000; + +const mockVersion = '7.0.103'; +const acquisitionContext = getMockAcquisitionContext('sdk', mockVersion); +const mockExecutor = new MockCommandExecutor(acquisitionContext, getMockUtilityContext()); +const pair: DistroVersionPair = { distro: 'Debian GNU/Linux', version: '12' }; +const provider: DebianDistroSDKProvider = new DebianDistroSDKProvider(pair, acquisitionContext, getMockUtilityContext(), mockExecutor); +const shouldRun = os.platform() === 'linux'; +const installType: DotnetInstallMode = 'sdk'; +const noDotnetString = ` +dotnet: command not found +` + +suite('Debian Distro Logic Unit Tests', () => +{ + test('Recommends Correct Version', async () => + { + if (shouldRun) + { + const recVersion = await provider.getRecommendedDotnetVersion(installType); + assert.equal(mockExecutor.attemptedCommand, + 'apt-cache -o DPkg::Lock::Timeout=180 search --names-only ^dotnet-sdk-9.0$', 'Searched for the newest package last with regex'); // this may fail if test not exec'd first + // the data is cached so --version may not be executed. + const distroVersion = await new LinuxVersionResolver(acquisitionContext, getMockUtilityContext()).getRunningDistro(); + assert.equal(recVersion,'9.0.1xx', 'Resolved the most recent available version : will eventually break if the mock data is not updated'); + } + }).timeout(standardTimeoutTime); + + test('Package Check Succeeds', async () => + { + if (shouldRun) + { + // assert this passes : we don't want the test to be reliant on machine state for whether the package exists or not, so don't check output + await provider.dotnetPackageExistsOnSystem(mockVersion, installType); + assert.equal(mockExecutor.attemptedCommand, 'dpkg -l dotnet-sdk-7.0'); + } + }).timeout(standardTimeoutTime); + + test('Support Status Check', async () => + { + if (shouldRun) + { + const status = await provider.getDotnetVersionSupportStatus(mockVersion, installType); + assert.equal(status, DotnetDistroSupportStatus.Distro); + } + }).timeout(standardTimeoutTime); + + test('Gets Distro Feed Install Dir', async () => + { + if (shouldRun) + { + const distroFeedDir = await provider.getExpectedDotnetDistroFeedInstallationDirectory(); + assert.equal(distroFeedDir, '/usr/lib/dotnet'); + } + }).timeout(standardTimeoutTime); + + test('Gets Microsoft Feed Install Dir', async () => + { + if (shouldRun) + { + const microsoftFeedDir = await provider.getExpectedDotnetMicrosoftFeedInstallationDirectory(); + assert.equal(microsoftFeedDir, '/usr/share/dotnet'); + } + }).timeout(standardTimeoutTime); + + test('Gets Installed SDKs', async () => + { + if (shouldRun) + { + mockExecutor.fakeReturnValue = { + stdout: ` +7.0.105 [/usr/lib/dotnet/sdk] +7.0.104 [/usr/custom/dotnet/sdk]`, stderr: '', status: '0' + }; + let versions = await provider.getInstalledDotnetSDKVersions(); + mockExecutor.resetReturnValues(); + assert.deepStrictEqual(versions, ['7.0.105', '7.0.104']); + + mockExecutor.fakeReturnValue = { stdout: noDotnetString, stderr: '', status: '0' }; + versions = await provider.getInstalledDotnetSDKVersions(); + mockExecutor.resetReturnValues(); + assert.deepStrictEqual(versions, []); + } + }).timeout(standardTimeoutTime); + + test('Gets Installed Runtimes', async () => + { + if (shouldRun) + { + mockExecutor.fakeReturnValue = { + stdout: ` +Microsoft.NETCore.App 6.0.16 [/usr/lib/dotnet/shared/Microsoft.NETCore.App] +Microsoft.NETCore.App 7.0.5 [/usr/lib/dotnet/shared/Microsoft.NETCore.App]`, stderr: '', status: '0' + }; + let versions = await provider.getInstalledDotnetRuntimeVersions(); + mockExecutor.resetReturnValues(); + assert.deepStrictEqual(versions, ['6.0.16', '7.0.5']); + + mockExecutor.fakeReturnValue = { stdout: noDotnetString, stderr: '', status: '0' }; + versions = await provider.getInstalledDotnetRuntimeVersions(); + mockExecutor.resetReturnValues(); + assert.deepStrictEqual(versions, []); + } + }).timeout(standardTimeoutTime); + + test('Looks for Global Dotnet Path Correctly', async () => + { + if (shouldRun) + { + await provider.getInstalledGlobalDotnetPathIfExists(installType); + assert.equal(mockExecutor.attemptedCommand, 'readlink -f /usr/bin/dotnet'); + } + }).timeout(standardTimeoutTime); + + test('Finds Existing Global Dotnet Version', async () => + { + if (shouldRun) + { + mockExecutor.fakeReturnValue = { stdout: `7.0.105`, stderr: '', status: '0' }; + let currentInfo = await provider.getInstalledGlobalDotnetVersionIfExists(); + mockExecutor.resetReturnValues(); + assert.equal(currentInfo, '7.0.105'); + + mockExecutor.fakeReturnValue = { stdout: noDotnetString, stderr: noDotnetString, status: '0' }; + currentInfo = await provider.getInstalledGlobalDotnetVersionIfExists(); + mockExecutor.resetReturnValues(); + assert.equal(currentInfo, null); + } + }).timeout(standardTimeoutTime); + + test('Gives Correct Version Support Info', async () => + { + if (shouldRun) + { + let supported = await provider.isDotnetVersionSupported('11.0.101', installType); + // In the mock data, 8.0 is not supported, so it should be false. + assert.equal(supported, false); + supported = await provider.isDotnetVersionSupported('7.0.101', installType); + assert.equal(supported, true); + // this feature band isn't supported by most distros yet. + supported = await provider.isDotnetVersionSupported('7.0.201', installType); + assert.equal(supported, false); + } + }).timeout(standardTimeoutTime); + + test('Runs Correct Install Command', async () => + { + if (shouldRun) + { + await provider.installDotnet(mockVersion, installType); + assert.equal(mockExecutor.attemptedCommand, 'sudo apt-get -o DPkg::Lock::Timeout=180 install -y dotnet-sdk-7.0'); + } + }).timeout(standardTimeoutTime); + + test('Runs Correct Uninstall Command', async () => + { + if (shouldRun) + { + await provider.uninstallDotnet(mockVersion, installType); + assert.equal(mockExecutor.attemptedCommand, 'sudo apt-get -o DPkg::Lock::Timeout=180 remove -y dotnet-sdk-7.0'); + } + }).timeout(standardTimeoutTime); + + test('Runs Correct Update Command', async () => + { + if (shouldRun) + { + await provider.upgradeDotnet(mockVersion, installType); + assert.equal(mockExecutor.attemptedCommand, 'sudo apt-get -o DPkg::Lock::Timeout=180 upgrade -y dotnet-sdk-7.0'); + } + }).timeout(standardTimeoutTime * 1000); +});