diff --git a/vscode-dotnet-runtime-library/distro-data/distro-support.json b/vscode-dotnet-runtime-library/distro-data/distro-support.json index 1eac973468..d2b02c9d9f 100644 --- a/vscode-dotnet-runtime-library/distro-data/distro-support.json +++ b/vscode-dotnet-runtime-library/distro-data/distro-support.json @@ -457,5 +457,281 @@ "version": "9.0" } ] + }, + "Debian": { + "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": "10", + "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/10/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": "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" + ] + } + ] + } + ] } } \ No newline at end of file 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..2a5450dca9 --- /dev/null +++ b/vscode-dotnet-runtime-library/src/Acquisition/DebianDistroSDKProvider.ts @@ -0,0 +1,28 @@ +/* -------------------------------------------------------------------------------------------- + * 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 { 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(1); + // 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; + } +} \ No newline at end of file diff --git a/vscode-dotnet-runtime-library/src/Acquisition/LinuxVersionResolver.ts b/vscode-dotnet-runtime-library/src/Acquisition/LinuxVersionResolver.ts index 3da9ff5d78..af4cdb4b13 100644 --- a/vscode-dotnet-runtime-library/src/Acquisition/LinuxVersionResolver.ts +++ b/vscode-dotnet-runtime-library/src/Acquisition/LinuxVersionResolver.ts @@ -30,6 +30,7 @@ import { IDotnetAcquireContext } from '../IDotnetAcquireContext' import { getInstallFromContext } from '../Utils/InstallIdUtilities'; import { DotnetInstallMode } from './DotnetInstallMode'; import { version } from 'os'; +import { DebianDistroSDKProvider } from './DebianDistroSDKProvider'; /** * An enumeration type representing all distros with their versions that we recognize. @@ -214,6 +215,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': + 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/test/unit/DebianDistroTests.test.ts b/vscode-dotnet-runtime-library/src/test/unit/DebianDistroTests.test.ts new file mode 100644 index 0000000000..d02430183e --- /dev/null +++ b/vscode-dotnet-runtime-library/src/test/unit/DebianDistroTests.test.ts @@ -0,0 +1,176 @@ +/* -------------------------------------------------------------------------------------------- + * 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 { DebianDistroSDKProvider } from '../../Acquisition/DebianDistroSDKProvider'; +import { MockCommandExecutor, MockEventStream } from '../mocks/MockObjects'; +import { DistroVersionPair, DotnetDistroSupportStatus, LinuxVersionResolver } from '../../Acquisition/LinuxVersionResolver'; +import { getMockAcquisitionContext, getMockUtilityContext } from './TestUtility'; +import { DotnetInstallMode } from '../../Acquisition/DotnetInstallMode'; +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', version : '11' }; +const provider : DebianDistroSDKProvider = new DebianDistroSDKProvider(pair, acquisitionContext, getMockUtilityContext(), mockExecutor); +const versionResolver = new LinuxVersionResolver(acquisitionContext, getMockUtilityContext(), mockExecutor); +let shouldRun = os.platform() === 'linux'; +const installType : DotnetInstallMode = 'sdk'; +const noDotnetString = ` +Command 'dotnet' not found, but can be installed with: + + snap install dotnet-sdk # version 7.0.304, or + + apt install dotnet-host # version 6.0.118-06ubuntu1~22.04.1 + apt install dotnet-host-7.0 # version 7.0.107-6ubuntu1~22.04.1 + See 'snap info dotnet-sdk' for additional versions. +` + +suite('Linux 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. + assert.equal(recVersion, '8.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 () => { + shouldRun = os.platform() === 'linux' && (await versionResolver.getRunningDistro()).distro === 'Debian'; + 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); +});